Skip to content

Commit

Permalink
added 3 months cap for since and some error handling string fromattin…
Browse files Browse the repository at this point in the history
…g tidy
  • Loading branch information
galiri committed Nov 4, 2016
1 parent a607b1a commit 6f7a842
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 19 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,12 @@ The default port is `8080`, but can be configured in the [config.yml](/config.ym

Write a new list notification:


```
curl http://localhost:8080/lists/notifications/{uuid} -XPUT --data '$json'
```


Where `$json` is a valid internal list in json format. To get example list data, see [sample-list.json](/sample-list.json) or get an example from the MongoDB `lists` collection.

Read notifications:
Expand All @@ -50,7 +52,8 @@ Read notifications:
curl http://localhost:8080/lists/notifications?since=$date
```

Where `$date` is a date in RFC3339 format which is within the last 3 months. For an example date, simply hit the `/lists/notifications` endpoint with no since parameter.
Where `$date` is a date in RFC3339 format which is within the last 3 months. simply hit the `/lists/notifications` endpoint with no since parameter.
e.g. since=2016-11-02T12:41:47.4692365ZFor an example date.

To see healthcheck results:

Expand Down
13 changes: 9 additions & 4 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,15 @@ func main() {
Usage: "The max age for content records in varnish in seconds.",
Value: 10,
}),
altsrc.NewIntFlag(cli.IntFlag{
Name: "max-since-interval",
Usage: "The maximum time interval clients are allowed to query for notifications in months.",
Value: 3,
}),
altsrc.NewStringFlag(cli.StringFlag{
Name: "api-host",
Usage: "Api host to use for read responses.",
Value: "test.api.ft.com",
Value: "api.ft.com",
}),
altsrc.NewIntFlag(cli.IntFlag{
Name: "db-connect-timeout",
Expand Down Expand Up @@ -77,16 +82,16 @@ func main() {
MaxLimit: ctx.Int("limit"),
}

server(ctx.Int("port"), mapper, nextLink, mongo)
server(ctx.Int("port"), ctx.Int("max-since-interval"), mapper, nextLink, mongo)
}

app.Run(os.Args)
}

func server(port int, mapper mapping.NotificationsMapper, nextLink mapping.NextLinkGenerator, db db.DB) {
func server(port int, maxSinceInterval int, mapper mapping.NotificationsMapper, nextLink mapping.NextLinkGenerator, db db.DB) {
r := mux.NewRouter()

r.HandleFunc("/lists/notifications", resources.ReadNotifications(mapper, nextLink, db))
r.HandleFunc("/lists/notifications", resources.ReadNotifications(mapper, nextLink, db, maxSinceInterval))
r.HandleFunc("/lists/notifications/{uuid}", resources.FilterSyntheticTransactions(resources.WriteNotification(mapper, db))).Methods("PUT")

r.HandleFunc("/__health", resources.Health(db))
Expand Down
19 changes: 12 additions & 7 deletions resources/read.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"net/http"
"strconv"
"time"

"fmt"
"github.com/Financial-Times/list-notifications-rw/db"
"github.com/Financial-Times/list-notifications-rw/mapping"
"github.com/Financial-Times/list-notifications-rw/model"
Expand All @@ -17,26 +17,31 @@ type msg struct {
}

// ReadNotifications reads notifications from the backing db
func ReadNotifications(mapper mapping.NotificationsMapper, nextLink mapping.NextLinkGenerator, db db.DB) func(w http.ResponseWriter, r *http.Request) {
func ReadNotifications(mapper mapping.NotificationsMapper, nextLink mapping.NextLinkGenerator, db db.DB, maxSinceInterval int) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
param := r.URL.Query().Get("since")
if param == "" {
logrus.Warn("User didn't provide since date.")
logrus.Info("User didn't provide since date.")
writeMessage(400, sinceMessage(), w)
return
}

since, err := time.Parse(time.RFC3339Nano, param)
since, err := time.Parse(time.RFC3339Nano, param)
if(since.Before(time.Now().UTC().AddDate(0, -maxSinceInterval, 0))){
logrus.Infof("User provided since date before query cap date, since= [%v].", since.Format(time.RFC3339Nano))
writeMessage(400, fmt.Sprintf("since date must be within the last %d months.", maxSinceInterval), w)
return
}
if err != nil {
logrus.WithError(err).WithField("since", param).Warn("Failed to parse user provided since date.")
logrus.WithError(err).WithField("since", param).Info("Failed to parse user provided since date.")
writeMessage(400, sinceMessage(), w)
return
}

offset, err := getOffset(r)

if err != nil {
logrus.WithError(err).Error("User provided offset is not an integer!")
logrus.WithError(err).Info("User provided offset is not an integer!")
writeMessage(400, "Please specify an integer offset.", w)
return
}
Expand Down Expand Up @@ -92,7 +97,7 @@ func getOffset(r *http.Request) (offset int, err error) {
}

func sinceMessage() string {
return "A mandatory 'since' query parameter has not been specified. Please supply a since date. For eg., since=" + time.Now().UTC().AddDate(0, 0, -1).Format(time.RFC3339Nano) + "."
return fmt.Sprintf("A mandatory 'since' query parameter has not been specified. Please supply a since date. For eg., since= %s ." , time.Now().UTC().AddDate(0, 0, -1).Format(time.RFC3339Nano))
}

func writeMessage(status int, message string, w http.ResponseWriter) {
Expand Down
27 changes: 20 additions & 7 deletions resources/read_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func TestReadNotifications(t *testing.T) {
mockTx.On("Close").Return()
mockTx.On("ReadNotifications", 0, mockSince).Return(&mockNotifications, nil)

ReadNotifications(testMapper, testLinkGenerator, mockDb)(w, req)
ReadNotifications(testMapper, testLinkGenerator, mockDb, 1000)(w, req)

assert.Equal(t, 200, w.Code, "Everything should be OK but we didn't return 200!")
assert.Equal(t, "application/json", w.Header().Get("Content-Type"), "Everything should be OK but we didn't return json!")
Expand Down Expand Up @@ -87,7 +87,7 @@ func TestReadNoNotifications(t *testing.T) {
mockTx.On("Close").Return()
mockTx.On("ReadNotifications", 0, mockSince).Return(&mockNotifications, nil)

ReadNotifications(testMapper, testLinkGenerator, mockDb)(w, req)
ReadNotifications(testMapper, testLinkGenerator, mockDb, 1000)(w, req)

assert.Equal(t, 200, w.Code, "Everything should be OK but we didn't return 200!")
assert.Equal(t, "application/json", w.Header().Get("Content-Type"), "Everything should be OK but we didn't return json!")
Expand All @@ -108,7 +108,7 @@ func Test400NoSinceDate(t *testing.T) {
w := httptest.NewRecorder()

mockDb := new(MockDB)
ReadNotifications(testMapper, testLinkGenerator, mockDb)(w, req)
ReadNotifications(testMapper, testLinkGenerator, mockDb, 1000)(w, req)

assert.Equal(t, 400, w.Code, "No since date, should be 400!")

Expand All @@ -121,22 +121,35 @@ func Test400JunkSinceDate(t *testing.T) {
w := httptest.NewRecorder()

mockDb := new(MockDB)
ReadNotifications(testMapper, testLinkGenerator, mockDb)(w, req)
ReadNotifications(testMapper, testLinkGenerator, mockDb, 1000)(w, req)

assert.Equal(t, 400, w.Code, "The since date was garbage! Should be 400!")

mockDb.AssertNotCalled(t, "Open")
t.Log("Recorded 400 response as expected.")
}

func Test400SinceDateTooEarly(t *testing.T) {
req, _ := http.NewRequest("GET", "http://nothing/at/all", nil)
w := httptest.NewRecorder()

mockDb := new(MockDB)
ReadNotifications(testMapper, testLinkGenerator, mockDb, 3)(w, req)

assert.Equal(t, 400, w.Code, "Sice date too early, should be 400!")

t.Log("Recorded 400 response as expected.")
mockDb.AssertNotCalled(t, "Open")
}

func TestFailedDatabaseOnRead(t *testing.T) {
req, _ := http.NewRequest("GET", "http://nothing/at/all?since=2006-01-02T15:04:05.999Z", nil)
w := httptest.NewRecorder()

mockDb := new(MockDB)
mockDb.On("Open").Return(nil, errors.New("I broke soz"))

ReadNotifications(testMapper, testLinkGenerator, mockDb)(w, req)
ReadNotifications(testMapper, testLinkGenerator, mockDb, 1000)(w, req)

assert.Equal(t, 500, w.Code, "Mongo was broken but we didn't return 500!")

Expand All @@ -150,7 +163,7 @@ func TestInvalidOffset(t *testing.T) {

mockDb := new(MockDB)

ReadNotifications(testMapper, testLinkGenerator, mockDb)(w, req)
ReadNotifications(testMapper, testLinkGenerator, mockDb, 1000)(w, req)

assert.Equal(t, 400, w.Code, "Offset was invalid but we didn't 400!")

Expand All @@ -171,7 +184,7 @@ func TestFailedToQueryAndOffset(t *testing.T) {
mockTx.On("ReadNotifications", 100, mockSince).Return(nil, errors.New("I broke again soz"))
mockDb.On("Open").Return(mockTx, nil)

ReadNotifications(testMapper, testLinkGenerator, mockDb)(w, req)
ReadNotifications(testMapper, testLinkGenerator, mockDb, 1000)(w, req)

assert.Equal(t, 500, w.Code, "Mongo failed to query but we didn't return 500!")

Expand Down

0 comments on commit 6f7a842

Please sign in to comment.