Merge pull request #351 from boreq/list-notices-as-json
Add a way to list notices as JSON
This commit is contained in:
commit
641069a8f1
|
@ -329,12 +329,13 @@ func New(
|
||||||
|
|
||||||
// notices (the mini-CMS)
|
// notices (the mini-CMS)
|
||||||
var nh = noticeHandler{
|
var nh = noticeHandler{
|
||||||
|
render: r,
|
||||||
flashes: flashHelper,
|
flashes: flashHelper,
|
||||||
|
|
||||||
notices: dbs.Notices,
|
notices: dbs.Notices,
|
||||||
pinned: dbs.PinnedNotices,
|
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))
|
m.Get(router.CompleteNoticeShow).Handler(r.HTML("notice/show.tmpl", nh.show))
|
||||||
|
|
||||||
// public aliases
|
// public aliases
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"go.mindeco.de/http/render"
|
||||||
"html/template"
|
"html/template"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -16,6 +18,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type noticeHandler struct {
|
type noticeHandler struct {
|
||||||
|
render *render.Renderer
|
||||||
flashes *errors.FlashHelper
|
flashes *errors.FlashHelper
|
||||||
|
|
||||||
pinned roomdb.PinnedNoticesService
|
pinned roomdb.PinnedNoticesService
|
||||||
|
@ -27,22 +30,33 @@ type noticesListData struct {
|
||||||
Flashes []errors.FlashMessage
|
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())
|
lst, err := h.pinned.List(req.Context())
|
||||||
if err != nil {
|
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{
|
pageData := noticesListData{
|
||||||
AllNotices: lst.Sorted(),
|
AllNotices: lst.Sorted(),
|
||||||
}
|
Flashes: flashes,
|
||||||
pageData.Flashes, err = h.flashes.GetAll(rw, req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return pageData, nil
|
responder.Render(pageData)
|
||||||
}
|
}
|
||||||
|
|
||||||
type noticeShowData struct {
|
type noticeShowData struct {
|
||||||
|
@ -80,3 +94,86 @@ func (h noticeHandler) show(rw http.ResponseWriter, req *http.Request) (interfac
|
||||||
|
|
||||||
return pageData, nil
|
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"`
|
||||||
|
}
|
||||||
|
|
|
@ -223,3 +223,74 @@ func TestNoticesCreateOnlyModsAndHigherInRestricted(t *testing.T) {
|
||||||
webassert.HasFlashMessages(t, ts.Client, noticeListURL, "ErrorNotAuthorized")
|
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)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue