Most API features now work
- Switched from Vestigo to ozzo-routing in a misguided attempt to get JSON to parse as a form; not switching back at this point - Fixed error with request SQL for individual requests - Parsing JSON body works now Still need to handle "no rows found" for journal/answered lists; this isn't an error for new users
This commit is contained in:
parent
bad430fc37
commit
79ced40470
@ -156,6 +156,9 @@ func Answered(userID string) []JournalRequest {
|
||||
AND "lastStatus" = 'Answered'
|
||||
ORDER BY "asOf" DESC`,
|
||||
userID)
|
||||
if err == sql.ErrNoRows {
|
||||
return make([]JournalRequest, 0)
|
||||
}
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return nil
|
||||
@ -169,8 +172,8 @@ func ByID(userID, reqID string) (*JournalRequest, bool) {
|
||||
req := JournalRequest{}
|
||||
err := db.QueryRow(currentRequestSQL+
|
||||
` WHERE "requestId" = $1
|
||||
AND "userId = $2`,
|
||||
userID, reqID).Scan(
|
||||
AND "userId" = $2`,
|
||||
reqID, userID).Scan(
|
||||
&req.RequestID, &req.Text, &req.AsOf, &req.LastStatus,
|
||||
)
|
||||
if err != nil {
|
||||
@ -235,7 +238,7 @@ func FullByID(userID, reqID string) (*JournalRequest, bool) {
|
||||
|
||||
// Journal retrieves the current user's active prayer journal.
|
||||
func Journal(userID string) []JournalRequest {
|
||||
rows, err := db.Query(journalSQL, userID)
|
||||
rows, err := db.Query(journalSQL+` ORDER BY "asOf"`, userID)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return nil
|
||||
|
@ -9,132 +9,155 @@ import (
|
||||
|
||||
"github.com/danieljsummers/myPrayerJournal/src/api/data"
|
||||
jwt "github.com/dgrijalva/jwt-go"
|
||||
"github.com/husobee/vestigo"
|
||||
routing "github.com/go-ozzo/ozzo-routing"
|
||||
)
|
||||
|
||||
/* Support */
|
||||
|
||||
// Set the content type, the HTTP error code, and return the error message.
|
||||
func sendError(w http.ResponseWriter, r *http.Request, err error) {
|
||||
func sendError(c *routing.Context, err error) error {
|
||||
w := c.Response
|
||||
w.Header().Set("Content-Type", "application/json; encoding=UTF-8")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
if err := json.NewEncoder(w).Encode(map[string]string{"error": err.Error()}); err != nil {
|
||||
log.Print("Error creating error JSON: " + err.Error())
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Set the content type and return the JSON to the user.
|
||||
func sendJSON(w http.ResponseWriter, r *http.Request, result interface{}) {
|
||||
func sendJSON(c *routing.Context, result interface{}) error {
|
||||
w := c.Response
|
||||
w.Header().Set("Content-Type", "application/json; encoding=UTF-8")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
if err := json.NewEncoder(w).Encode(result); err != nil {
|
||||
sendError(w, r, err)
|
||||
return sendError(c, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Parse the request body as JSON.
|
||||
func parseJSON(c *routing.Context) (map[string]interface{}, error) {
|
||||
payload := make(map[string]interface{})
|
||||
if err := json.NewDecoder(c.Request.Body).Decode(&payload); err != nil {
|
||||
log.Println("Error decoding JSON:", err)
|
||||
return payload, err
|
||||
}
|
||||
return payload, nil
|
||||
}
|
||||
|
||||
// userID is a convenience function to extract the subscriber ID from the user's JWT.
|
||||
// NOTE: Do not call this from public routes; there are a lot of type assertions that won't be true if the request
|
||||
// hasn't gone through the authorization process.
|
||||
func userID(r *http.Request) string {
|
||||
return r.Context().Value("user").(*jwt.Token).Claims.(jwt.MapClaims)["sub"].(string)
|
||||
func userID(c *routing.Context) string {
|
||||
return c.Request.Context().Value("user").(*jwt.Token).Claims.(jwt.MapClaims)["sub"].(string)
|
||||
}
|
||||
|
||||
/* Handlers */
|
||||
|
||||
// GET: /api/journal/
|
||||
func journal(w http.ResponseWriter, r *http.Request) {
|
||||
reqs := data.Journal(userID(r))
|
||||
func journal(c *routing.Context) error {
|
||||
reqs := data.Journal(userID(c))
|
||||
if reqs == nil {
|
||||
reqs = []data.JournalRequest{}
|
||||
}
|
||||
sendJSON(w, r, reqs)
|
||||
return sendJSON(c, reqs)
|
||||
}
|
||||
|
||||
// POST: /api/request/
|
||||
func requestAdd(w http.ResponseWriter, r *http.Request) {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
sendError(w, r, err)
|
||||
func requestAdd(c *routing.Context) error {
|
||||
payload, err := parseJSON(c)
|
||||
if err != nil {
|
||||
return sendError(c, err)
|
||||
}
|
||||
result, ok := data.AddNew(userID(r), r.FormValue("requestText"))
|
||||
result, ok := data.AddNew(userID(c), payload["requestText"].(string))
|
||||
if !ok {
|
||||
sendError(w, r, errors.New("error adding request"))
|
||||
return sendError(c, errors.New("error adding request"))
|
||||
}
|
||||
sendJSON(w, r, result)
|
||||
return sendJSON(c, result)
|
||||
}
|
||||
|
||||
// GET: /api/request/:id
|
||||
func requestGet(w http.ResponseWriter, r *http.Request) {
|
||||
request, ok := data.ByID(userID(r), vestigo.Param(r, "id"))
|
||||
// GET: /api/request/<id>
|
||||
func requestGet(c *routing.Context) error {
|
||||
request, ok := data.ByID(userID(c), c.Param("id"))
|
||||
if !ok {
|
||||
sendError(w, r, errors.New("error retrieving request"))
|
||||
return sendError(c, errors.New("error retrieving request"))
|
||||
}
|
||||
sendJSON(w, r, request)
|
||||
return sendJSON(c, request)
|
||||
}
|
||||
|
||||
// GET: /api/request/:id/complete
|
||||
func requestGetComplete(w http.ResponseWriter, r *http.Request) {
|
||||
request, ok := data.FullByID(userID(r), vestigo.Param(r, "id"))
|
||||
// GET: /api/request/<id>/complete
|
||||
func requestGetComplete(c *routing.Context) error {
|
||||
request, ok := data.FullByID(userID(c), c.Param("id"))
|
||||
if !ok {
|
||||
sendError(w, r, errors.New("error retrieving request"))
|
||||
return sendError(c, errors.New("error retrieving request"))
|
||||
}
|
||||
request.Notes = data.NotesByID(userID(r), vestigo.Param(r, "id"))
|
||||
sendJSON(w, r, request)
|
||||
request.Notes = data.NotesByID(userID(c), c.Param("id"))
|
||||
return sendJSON(c, request)
|
||||
}
|
||||
|
||||
// GET: /api/request/:id/full
|
||||
func requestGetFull(w http.ResponseWriter, r *http.Request) {
|
||||
request, ok := data.FullByID(userID(r), vestigo.Param(r, "id"))
|
||||
// GET: /api/request/<id>/full
|
||||
func requestGetFull(c *routing.Context) error {
|
||||
request, ok := data.FullByID(userID(c), c.Param("id"))
|
||||
if !ok {
|
||||
sendError(w, r, errors.New("error retrieving request"))
|
||||
return sendError(c, errors.New("error retrieving request"))
|
||||
}
|
||||
sendJSON(w, r, request)
|
||||
return sendJSON(c, request)
|
||||
}
|
||||
|
||||
// POST: /api/request/:id/history
|
||||
func requestAddHistory(w http.ResponseWriter, r *http.Request) {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
sendError(w, r, err)
|
||||
// POST: /api/request/<id>/history
|
||||
func requestAddHistory(c *routing.Context) error {
|
||||
payload, err := parseJSON(c)
|
||||
if err != nil {
|
||||
return sendError(c, err)
|
||||
}
|
||||
w.WriteHeader(data.AddHistory(userID(r), vestigo.Param(r, "id"), r.FormValue("status"), r.FormValue("updateText")))
|
||||
c.Response.WriteHeader(
|
||||
data.AddHistory(userID(c), c.Param("id"), payload["status"].(string), payload["updateText"].(string)))
|
||||
return nil
|
||||
}
|
||||
|
||||
// POST: /api/request/:id/note
|
||||
func requestAddNote(w http.ResponseWriter, r *http.Request) {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
sendError(w, r, err)
|
||||
// POST: /api/request/<id>/note
|
||||
func requestAddNote(c *routing.Context) error {
|
||||
payload, err := parseJSON(c)
|
||||
if err != nil {
|
||||
return sendError(c, err)
|
||||
}
|
||||
w.WriteHeader(data.AddNote(userID(r), vestigo.Param(r, "id"), r.FormValue("notes")))
|
||||
c.Response.WriteHeader(data.AddNote(userID(c), c.Param("id"), payload["notes"].(string)))
|
||||
return nil
|
||||
}
|
||||
|
||||
// GET: /api/request/:id/notes
|
||||
func requestGetNotes(w http.ResponseWriter, r *http.Request) {
|
||||
notes := data.NotesByID(userID(r), vestigo.Param(r, "id"))
|
||||
// GET: /api/request/<id>/notes
|
||||
func requestGetNotes(c *routing.Context) error {
|
||||
notes := data.NotesByID(userID(c), c.Param("id"))
|
||||
if notes == nil {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
c.Response.WriteHeader(http.StatusNotFound)
|
||||
return errors.New("Not Found")
|
||||
}
|
||||
sendJSON(w, r, notes)
|
||||
return sendJSON(c, notes)
|
||||
}
|
||||
|
||||
// GET: /api/request/answered
|
||||
func requestsAnswered(w http.ResponseWriter, r *http.Request) {
|
||||
reqs := data.Answered(userID(r))
|
||||
func requestsAnswered(c *routing.Context) error {
|
||||
reqs := data.Answered(userID(c))
|
||||
if reqs == nil {
|
||||
reqs = []data.JournalRequest{}
|
||||
}
|
||||
sendJSON(w, r, reqs)
|
||||
return sendJSON(c, reqs)
|
||||
}
|
||||
|
||||
// GET: /*
|
||||
func staticFiles(w http.ResponseWriter, r *http.Request) {
|
||||
func staticFiles(c *routing.Context) error {
|
||||
// serve index for known routes handled client-side by the app
|
||||
r := c.Request
|
||||
w := c.Response
|
||||
for _, prefix := range ClientPrefixes {
|
||||
if strings.HasPrefix(r.URL.Path, prefix) {
|
||||
w.Header().Add("Content-Type", "text/html")
|
||||
http.ServeFile(w, r, "./public/index.html")
|
||||
return
|
||||
return nil
|
||||
}
|
||||
}
|
||||
// 404 here is fine; quit hacking, y'all...
|
||||
http.ServeFile(w, r, "./public"+r.URL.Path)
|
||||
return nil
|
||||
}
|
||||
|
@ -5,11 +5,14 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/auth0/go-jwt-middleware"
|
||||
jwt "github.com/dgrijalva/jwt-go"
|
||||
"github.com/husobee/vestigo"
|
||||
"github.com/go-ozzo/ozzo-routing"
|
||||
"github.com/go-ozzo/ozzo-routing/access"
|
||||
"github.com/go-ozzo/ozzo-routing/fault"
|
||||
)
|
||||
|
||||
// AuthConfig contains the Auth0 configuration passed from the "auth" JSON object.
|
||||
@ -96,25 +99,24 @@ var authZero = jwtmiddleware.New(jwtmiddleware.Options{
|
||||
SigningMethod: jwt.SigningMethodRS256,
|
||||
})
|
||||
|
||||
// authMiddleware is a wrapper for the Auth0 middleware above with a signature Vestigo recognizes.
|
||||
func authMiddleware(f http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
err := authZero.CheckJWT(w, r)
|
||||
if err == nil {
|
||||
f(w, r)
|
||||
}
|
||||
}
|
||||
// authMiddleware is a wrapper for the Auth0 middleware above with a signature ozzo-routing recognizes.
|
||||
func authMiddleware(c *routing.Context) error {
|
||||
return authZero.CheckJWT(c.Response, c.Request)
|
||||
}
|
||||
|
||||
// NewRouter returns a configured router to handle all incoming requests.
|
||||
func NewRouter(cfg *AuthConfig) *vestigo.Router {
|
||||
func NewRouter(cfg *AuthConfig) *routing.Router {
|
||||
authCfg = cfg
|
||||
router := vestigo.NewRouter()
|
||||
router := routing.New()
|
||||
router.Use(
|
||||
access.Logger(log.Printf), // TODO: remove before go-live
|
||||
fault.Recovery(log.Printf),
|
||||
)
|
||||
for _, route := range routes {
|
||||
if route.IsPublic {
|
||||
router.Add(route.Method, route.Pattern, route.Func)
|
||||
router.To(route.Method, route.Pattern, route.Func)
|
||||
} else {
|
||||
router.Add(route.Method, route.Pattern, route.Func, authMiddleware)
|
||||
router.To(route.Method, route.Pattern, authMiddleware, route.Func)
|
||||
}
|
||||
}
|
||||
return router
|
||||
|
@ -3,6 +3,8 @@ package routes
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
routing "github.com/go-ozzo/ozzo-routing"
|
||||
)
|
||||
|
||||
// Route is a route served in the application.
|
||||
@ -10,7 +12,7 @@ type Route struct {
|
||||
Name string
|
||||
Method string
|
||||
Pattern string
|
||||
Func http.HandlerFunc
|
||||
Func routing.Handler
|
||||
IsPublic bool
|
||||
}
|
||||
|
||||
@ -36,42 +38,42 @@ var routes = Routes{
|
||||
Route{
|
||||
"GetRequestByID",
|
||||
http.MethodGet,
|
||||
"/api/request/:id",
|
||||
"/api/request/<id>",
|
||||
requestGet,
|
||||
false,
|
||||
},
|
||||
Route{
|
||||
"GetCompleteRequestByID",
|
||||
http.MethodGet,
|
||||
"/api/request/:id/complete",
|
||||
"/api/request/<id>/complete",
|
||||
requestGetComplete,
|
||||
false,
|
||||
},
|
||||
Route{
|
||||
"GetFullRequestByID",
|
||||
http.MethodGet,
|
||||
"/api/request/:id/full",
|
||||
"/api/request/<id>/full",
|
||||
requestGetFull,
|
||||
false,
|
||||
},
|
||||
Route{
|
||||
"AddNewHistoryEntry",
|
||||
http.MethodPost,
|
||||
"/api/request/:id/history",
|
||||
"/api/request/<id>/history",
|
||||
requestAddHistory,
|
||||
false,
|
||||
},
|
||||
Route{
|
||||
"AddNewNote",
|
||||
http.MethodPost,
|
||||
"/api/request/:id/note",
|
||||
"/api/request/<id>/note",
|
||||
requestAddNote,
|
||||
false,
|
||||
},
|
||||
Route{
|
||||
"GetNotesForRequest",
|
||||
http.MethodGet,
|
||||
"/api/request/:id/notes",
|
||||
"/api/request/<id>/notes",
|
||||
requestGetNotes,
|
||||
false,
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user