Merge pull request #351 from boreq/list-notices-as-json

Add a way to list notices as JSON
This commit is contained in:
decentral1se 2022-11-09 10:28:36 +01:00 committed by GitHub
commit 641069a8f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 177 additions and 8 deletions

View File

@ -329,12 +329,13 @@ func New(
// notices (the mini-CMS)
var nh = noticeHandler{
render: r,
flashes: flashHelper,
notices: dbs.Notices,
pinned: dbs.PinnedNotices,
}
m.Get(router.CompleteNoticeList).Handler(r.HTML("notice/list.tmpl", nh.list))
m.Get(router.CompleteNoticeList).HandlerFunc(nh.list)
m.Get(router.CompleteNoticeShow).Handler(r.HTML("notice/show.tmpl", nh.show))
// public aliases

View File

@ -5,6 +5,8 @@
package handlers
import (
"encoding/json"
"go.mindeco.de/http/render"
"html/template"
"net/http"
"strconv"
@ -16,6 +18,7 @@ import (
)
type noticeHandler struct {
render *render.Renderer
flashes *errors.FlashHelper
pinned roomdb.PinnedNoticesService
@ -27,22 +30,33 @@ type noticesListData struct {
Flashes []errors.FlashMessage
}
func (h noticeHandler) list(rw http.ResponseWriter, req *http.Request) (interface{}, error) {
func (h noticeHandler) list(rw http.ResponseWriter, req *http.Request) {
var responder listNoticesResponder
switch req.URL.Query().Get("encoding") {
case "json":
responder = newListNoticesJSONResponder(rw)
default:
responder = newListNoticesHTMLResponder(h.render, rw, req)
}
lst, err := h.pinned.List(req.Context())
if err != nil {
return nil, err
responder.RenderError(err)
return
}
flashes, err := h.flashes.GetAll(rw, req)
if err != nil {
responder.RenderError(err)
return
}
pageData := noticesListData{
AllNotices: lst.Sorted(),
}
pageData.Flashes, err = h.flashes.GetAll(rw, req)
if err != nil {
return nil, err
Flashes: flashes,
}
return pageData, nil
responder.Render(pageData)
}
type noticeShowData struct {
@ -80,3 +94,86 @@ func (h noticeHandler) show(rw http.ResponseWriter, req *http.Request) (interfac
return pageData, nil
}
type listNoticesResponder interface {
Render(noticesListData)
RenderError(error)
}
type listNoticesJSONResponder struct {
rw http.ResponseWriter
}
func newListNoticesJSONResponder(rw http.ResponseWriter) *listNoticesJSONResponder {
return &listNoticesJSONResponder{rw: rw}
}
func (l listNoticesJSONResponder) Render(data noticesListData) {
l.rw.Header().Set("Content-Type", "application/json")
var pinnedNotices []listNoticesJSONResponsePinnedNotice
for _, pinnedNotice := range data.AllNotices {
v := listNoticesJSONResponsePinnedNotice{
Name: string(pinnedNotice.Name),
Notices: nil,
}
for _, notice := range pinnedNotice.Notices {
v.Notices = append(v.Notices, listNoticesJSONResponseNotice{
ID: notice.ID,
Title: notice.Title,
Content: notice.Content,
Language: notice.Language,
})
}
pinnedNotices = append(pinnedNotices, v)
}
var resp = listNoticesJSONResponse{
PinnedNotices: pinnedNotices,
}
json.NewEncoder(l.rw).Encode(resp)
}
func (l listNoticesJSONResponder) RenderError(err error) {
l.rw.Header().Set("Content-Type", "application/json")
l.rw.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(l.rw).Encode(struct {
Status string `json:"status"`
Error string `json:"error"`
}{"error", err.Error()})
}
type listNoticesHTMLResponder struct {
renderer *render.Renderer
rw http.ResponseWriter
req *http.Request
}
func newListNoticesHTMLResponder(renderer *render.Renderer, rw http.ResponseWriter, req *http.Request) *listNoticesHTMLResponder {
return &listNoticesHTMLResponder{renderer: renderer, rw: rw, req: req}
}
func (l listNoticesHTMLResponder) Render(data noticesListData) {
l.renderer.HTML("notice/list.tmpl", func(w http.ResponseWriter, req *http.Request) (interface{}, error) {
return data, nil
})(l.rw, l.req)
}
func (l listNoticesHTMLResponder) RenderError(err error) {
l.renderer.Error(l.rw, l.req, http.StatusInternalServerError, err)
}
type listNoticesJSONResponse struct {
PinnedNotices []listNoticesJSONResponsePinnedNotice `json:"pinned_notices"`
}
type listNoticesJSONResponsePinnedNotice struct {
Name string `json:"name"`
Notices []listNoticesJSONResponseNotice `json:"notices"`
}
type listNoticesJSONResponseNotice struct {
ID int64 `json:"id"`
Title string `json:"title"`
Content string `json:"content"`
Language string `json:"language"`
}

View File

@ -223,3 +223,74 @@ func TestNoticesCreateOnlyModsAndHigherInRestricted(t *testing.T) {
webassert.HasFlashMessages(t, ts.Client, noticeListURL, "ErrorNotAuthorized")
}
func TestNoticesListReturnsJSONWhenCorrectParameterIsSet(t *testing.T) {
ts := setup(t)
a := assert.New(t)
ts.PinnedDB.ListReturns(roomdb.PinnedNotices{
"name1": {
{
ID: 1,
Title: "title1",
Content: "content1",
Language: "language1",
},
{
ID: 2,
Title: "title2",
Content: "content2",
Language: "language2",
},
},
"name2": {
{
ID: 3,
Title: "title3",
Content: "content3",
Language: "language3",
},
},
}, nil)
noticeURL := ts.URLTo(router.CompleteNoticeList)
values := noticeURL.Query()
values.Set("encoding", "json")
noticeURL.RawQuery = values.Encode()
var response listNoticesJSONResponse
res := ts.Client.GetJSON(noticeURL, &response)
a.Equal(http.StatusOK, res.Code, "wrong HTTP status code")
a.Equal(listNoticesJSONResponse{
PinnedNotices: []listNoticesJSONResponsePinnedNotice{
{
Name: "name1",
Notices: []listNoticesJSONResponseNotice{
{
ID: 1,
Title: "title1",
Content: "content1",
Language: "language1",
},
{
ID: 2,
Title: "title2",
Content: "content2",
Language: "language2",
},
},
},
{
Name: "name2",
Notices: []listNoticesJSONResponseNotice{
{
ID: 3,
Title: "title3",
Content: "content3",
Language: "language3",
},
},
},
},
}, response)
}