Added "snoozedUntil" column to API

Also added a "snooze" route (working on #17 )
This commit is contained in:
Daniel J. Summers 2018-06-18 20:25:00 -05:00
parent 05990d537a
commit d0ea7cf3c6
4 changed files with 63 additions and 18 deletions

View File

@ -14,10 +14,10 @@ import (
const ( const (
currentRequestSQL = ` currentRequestSQL = `
SELECT "requestId", "text", "asOf", "lastStatus" SELECT "requestId", "text", "asOf", "lastStatus", "snoozedUntil"
FROM mpj.journal` FROM mpj.journal`
journalSQL = ` journalSQL = `
SELECT "requestId", "text", "asOf", "lastStatus" SELECT "requestId", "text", "asOf", "lastStatus", "snoozedUntil"
FROM mpj.journal FROM mpj.journal
WHERE "userId" = $1 WHERE "userId" = $1
AND "lastStatus" <> 'Answered'` AND "lastStatus" <> 'Answered'`
@ -41,11 +41,11 @@ type Settings struct {
func retrieveRequest(reqID, userID string) (*Request, bool) { func retrieveRequest(reqID, userID string) (*Request, bool) {
req := Request{} req := Request{}
err := db.QueryRow(` err := db.QueryRow(`
SELECT "requestId", "enteredOn" SELECT "requestId", "enteredOn", "snoozedUntil"
FROM mpj.request FROM mpj.request
WHERE "requestId" = $1 WHERE "requestId" = $1
AND "userId" = $2`, reqID, userID).Scan( AND "userId" = $2`, reqID, userID).Scan(
&req.ID, &req.EnteredOn, &req.ID, &req.EnteredOn, &req.SnoozedUntil,
) )
if err != nil { if err != nil {
if err != sql.ErrNoRows { if err != sql.ErrNoRows {
@ -67,7 +67,7 @@ func makeJournal(rows *sql.Rows, userID string) []JournalRequest {
var out []JournalRequest var out []JournalRequest
for rows.Next() { for rows.Next() {
req := JournalRequest{} req := JournalRequest{}
err := rows.Scan(&req.RequestID, &req.Text, &req.AsOf, &req.LastStatus) err := rows.Scan(&req.RequestID, &req.Text, &req.AsOf, &req.LastStatus, &req.SnoozedUntil)
if err != nil { if err != nil {
log.Print(err) log.Print(err)
continue continue
@ -117,7 +117,7 @@ func AddNew(userID, text string) (*JournalRequest, bool) {
} }
}() }()
_, err = tx.Exec( _, err = tx.Exec(
`INSERT INTO mpj.request ("requestId", "enteredOn", "userId") VALUES ($1, $2, $3)`, `INSERT INTO mpj.request ("requestId", "enteredOn", "userId", "snoozedUntil") VALUES ($1, $2, $3, 0)`,
id, now, userID) id, now, userID)
if err != nil { if err != nil {
return nil, false return nil, false
@ -128,7 +128,7 @@ func AddNew(userID, text string) (*JournalRequest, bool) {
if err != nil { if err != nil {
return nil, false return nil, false
} }
return &JournalRequest{RequestID: id, Text: text, AsOf: now, LastStatus: `Created`}, true return &JournalRequest{RequestID: id, Text: text, AsOf: now, LastStatus: `Created`, SnoozedUntil: 0}, true
} }
// AddNote adds a note to a prayer request. // AddNote adds a note to a prayer request.
@ -171,7 +171,7 @@ func ByID(userID, reqID string) (*JournalRequest, bool) {
` WHERE "requestId" = $1 ` WHERE "requestId" = $1
AND "userId" = $2`, AND "userId" = $2`,
reqID, userID).Scan( reqID, userID).Scan(
&req.RequestID, &req.Text, &req.AsOf, &req.LastStatus, &req.RequestID, &req.Text, &req.AsOf, &req.LastStatus, &req.SnoozedUntil,
) )
if err != nil { if err != nil {
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
@ -284,6 +284,23 @@ func NotesByID(userID, reqID string) ([]Note, error) {
return notes, nil return notes, nil
} }
// SnoozeByID sets a request to not show until a specified time
func SnoozeByID(userID, reqID string, until int64) int {
if _, ok := retrieveRequest(reqID, userID); !ok {
return 404
}
_, err := db.Exec(`
UPDATE mpj.request
SET "snoozedUntil" = $2
WHERE "requestId" = $1`,
reqID, until)
if err != nil {
log.Print(err)
return 500
}
return 204
}
/* DDL */ /* DDL */
// EnsureDB makes sure we have a known state of data structures. // EnsureDB makes sure we have a known state of data structures.
@ -291,6 +308,11 @@ func EnsureDB() {
tableSQL := func(table string) string { tableSQL := func(table string) string {
return fmt.Sprintf(`SELECT 1 FROM pg_tables WHERE schemaname='mpj' AND tablename='%s'`, table) return fmt.Sprintf(`SELECT 1 FROM pg_tables WHERE schemaname='mpj' AND tablename='%s'`, table)
} }
columnSQL := func(table, column string) string {
return fmt.Sprintf(
`SELECT 1 FROM information_schema.columns WHERE table_schema='mpj' AND table_name='%s' AND column_name='%s'`,
table, column)
}
indexSQL := func(table, index string) string { indexSQL := func(table, index string) string {
return fmt.Sprintf(`SELECT 1 FROM pg_indexes WHERE schemaname='mpj' AND tablename='%s' AND indexname='%s'`, return fmt.Sprintf(`SELECT 1 FROM pg_indexes WHERE schemaname='mpj' AND tablename='%s' AND indexname='%s'`,
table, index) table, index)
@ -322,6 +344,9 @@ func EnsureDB() {
"enteredOn" bigint NOT NULL, "enteredOn" bigint NOT NULL,
"userId" varchar(100) NOT NULL); "userId" varchar(100) NOT NULL);
COMMENT ON TABLE mpj.request IS 'Requests'`) COMMENT ON TABLE mpj.request IS 'Requests'`)
check(`request.snoozedUntil Column`, columnSQL(`request`, `snoozedUntil`),
`ALTER TABLE mpj.request
ADD COLUMN "snoozedUntil" bigint NOT NULL DEFAULT 0`)
check(`history Table`, tableSQL(`history`), check(`history Table`, tableSQL(`history`),
`CREATE TABLE mpj.history ( `CREATE TABLE mpj.history (
"requestId" varchar(25) NOT NULL REFERENCES mpj.request, "requestId" varchar(25) NOT NULL REFERENCES mpj.request,
@ -360,7 +385,8 @@ func EnsureDB() {
FROM mpj.history FROM mpj.history
WHERE history."requestId" = request."requestId" WHERE history."requestId" = request."requestId"
ORDER BY "asOf" DESC ORDER BY "asOf" DESC
LIMIT 1) AS "lastStatus" LIMIT 1) AS "lastStatus",
request."snoozedUntil"
FROM mpj.request; FROM mpj.request;
COMMENT ON VIEW mpj.journal IS 'Requests with latest text'`) COMMENT ON VIEW mpj.journal IS 'Requests with latest text'`)
} }

View File

@ -17,18 +17,20 @@ type Note struct {
// Request is the identifying record for a prayer request. // Request is the identifying record for a prayer request.
type Request struct { type Request struct {
ID string `json:"requestId"` ID string `json:"requestId"`
EnteredOn int64 `json:"enteredOn"` EnteredOn int64 `json:"enteredOn"`
UserID string `json:"userId"` UserID string `json:"userId"`
SnoozedUntil int64 `json:"snoozedUntil"`
} }
// JournalRequest is the form of a prayer request returned for the request journal display. It also contains // JournalRequest is the form of a prayer request returned for the request journal display. It also contains
// properties that may be filled for history and notes. // properties that may be filled for history and notes.
type JournalRequest struct { type JournalRequest struct {
RequestID string `json:"requestId"` RequestID string `json:"requestId"`
Text string `json:"text"` Text string `json:"text"`
AsOf int64 `json:"asOf"` AsOf int64 `json:"asOf"`
LastStatus string `json:"lastStatus"` LastStatus string `json:"lastStatus"`
History []History `json:"history,omitempty"` SnoozedUntil int64 `json:"snoozedUntil"`
Notes []Note `json:"notes,omitempty"` History []History `json:"history,omitempty"`
Notes []Note `json:"notes,omitempty"`
} }

View File

@ -155,6 +155,16 @@ func requestGetNotes(c *routing.Context) error {
return sendJSON(c, notes) return sendJSON(c, notes)
} }
// POST: /api/request/<id>/snooze
func requestSnooze(c *routing.Context) error {
payload, err := parseJSON(c)
if err != nil {
return sendError(c, err)
}
c.Response.WriteHeader(data.SnoozeByID(userID(c), c.Param("id"), payload["until"].(int64)))
return nil
}
// GET: /api/request/answered // GET: /api/request/answered
func requestsAnswered(c *routing.Context) error { func requestsAnswered(c *routing.Context) error {
reqs := data.Answered(userID(c)) reqs := data.Answered(userID(c))

View File

@ -85,6 +85,13 @@ var routes = Routes{
requestGetNotes, requestGetNotes,
false, false,
}, },
Route{
"SnoozeRequest",
http.MethodPost,
"/api/request/<id>/snooze",
requestSnooze,
false,
},
// keep this route last // keep this route last
Route{ Route{
"StaticFiles", "StaticFiles",