Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CORE-1120: completed implementation of new notifications service. #2

Merged
merged 27 commits into from
Oct 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
380d1a1
CORE-1120: partial implementation of the notification request endpoint
slr71 Aug 12, 2020
1ad901d
CORE-1120: finished implementing the notification request endpoint
slr71 Aug 13, 2020
f42bed6
CORE-1120: added the code to establish a database connection and pass…
slr71 Aug 13, 2020
a610575
CORE-1120: began implementing the /v1/messages endpoint
slr71 Aug 20, 2020
b0978e0
CORE-1120: added support for the `seen` query parameter to the /v1/me…
slr71 Aug 25, 2020
f3bfb02
CORE-1120: added support for the sort_field and sort_dir query parame…
slr71 Aug 27, 2020
6289282
CORE-1120: implemented the `filter` query parameter in the `/v1/messa…
slr71 Aug 27, 2020
cc74c3c
CORE-1120: implemented the `/v1/unseen-messages` endpoint
slr71 Aug 27, 2020
82ac1dd
CORE-1120: implemented the `/v1/count-messages` endpoint
slr71 Aug 28, 2020
61c9aee
CORE-1120: implemented the `/v1/seen` endpoint
slr71 Sep 1, 2020
0978063
CORE-1120: implemented the `/v1/mark-all-seen` endpoint
slr71 Sep 1, 2020
863ceb2
CORE-1120: implemented the `/v1/delete` endpoint
slr71 Sep 2, 2020
e6b7304
CORE-1120: implemented the `/v1/delete-all` endpoint
slr71 Sep 3, 2020
ecd605a
CORE-1120: began implementng the /v2/messages endpoint
slr71 Sep 5, 2020
f39fcd4
CORE-1120: initial implementation of the /v2/messages endpoint; more …
slr71 Sep 15, 2020
23a1d7e
CORE-1120: finished implementing the paging parameters in the `/v2/me…
slr71 Sep 15, 2020
7b48861
CORE-1120: implemented the `count-only` query parameter in the `/v2/m…
slr71 Sep 15, 2020
1dd4c70
CORE-1120: added support for the `subject-search` query parameter in …
slr71 Sep 16, 2020
28a17a2
CORE-1120: added support for the `type` query parameter to the `/v2/m…
slr71 Sep 16, 2020
4803588
CORE-1120: implemented the `/v2/messages/{id}` endpoint
slr71 Sep 16, 2020
d7d2a88
CORE-1120: implemented the `/v2/messages/{id}/seen` endpoint and adde…
slr71 Sep 17, 2020
94b5315
CORE-1120: implemented the `DELETE /v2/messages/{id}` endpoint
slr71 Sep 17, 2020
7c92e0d
CORE-1120: implemented the `POST /v2/messages/seen` endpoint
slr71 Sep 17, 2020
4c41fad
CORE-1120: implemented the `POST /v2/messages/delete` endpoint and di…
slr71 Sep 18, 2020
ba43776
CORE-1120: fixed some problems found during proofreading
slr71 Sep 18, 2020
5907e4a
CORE-1120: centralize the definition of a statement builder with the …
slr71 Oct 2, 2020
662f3ad
CORE-1120: refactor the code to obtain the `before_id` and `after_id`…
slr71 Oct 3, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 10 additions & 0 deletions api/main.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
package api

import (
"database/sql"
"net/http"

v1 "github.com/cyverse-de/notifications/api/v1"
v2 "github.com/cyverse-de/notifications/api/v2"
"github.com/cyverse-de/notifications/common"
"github.com/cyverse-de/notifications/model"
"github.com/labstack/echo"
"gopkg.in/cyverse-de/messaging.v7"
)

// API defines the REST API of the notifications service
type API struct {
Echo *echo.Echo
AMQPSettings *common.AMQPSettings
AMQPClient *messaging.Client
DB *sql.DB
Service string
Title string
Version string
Expand All @@ -36,8 +40,11 @@ func (a API) RegisterHandlers() {
// Register the group for API version 1.
v1Group := a.Echo.Group("/v1")
v1API := v1.API{
Echo: a.Echo,
Group: v1Group,
AMQPSettings: a.AMQPSettings,
AMQPClient: a.AMQPClient,
DB: a.DB,
Service: a.Service,
Title: a.Title,
Version: a.Version,
Expand All @@ -47,8 +54,11 @@ func (a API) RegisterHandlers() {
// Register the group for API version 2.
v2Group := a.Echo.Group("/v2")
v2API := v2.API{
Echo: a.Echo,
Group: v2Group,
AMQPSettings: a.AMQPSettings,
AMQPClient: a.AMQPClient,
DB: a.DB,
Service: a.Service,
Title: a.Title,
Version: a.Version,
Expand Down
4 changes: 4 additions & 0 deletions api/main_docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,7 @@ type errorResponseWrapper struct {
// in:body
Body model.ErrorResponse
}

// An empty response body.
// swagger:response emptyResponse
type emptyResponseWrapper struct{}
193 changes: 193 additions & 0 deletions api/v1/listings.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
package v1

import (
"net/http"
"strings"

"github.com/cyverse-de/notifications/db"
"github.com/cyverse-de/notifications/model"
"github.com/cyverse-de/notifications/query"
"github.com/labstack/echo"
)

// GetMessagesHandler handles requests for listing notification messages for a user.
func (a API) GetMessagesHandler(ctx echo.Context) error {
var err error

// Extract and validate the user query parameter.
user, err := query.ValidatedQueryParam(ctx, "user", "required")
if err != nil {
return ctx.JSON(http.StatusBadRequest, model.ErrorResponse{
Message: "missing required query parameter: user",
})
}

// Extract and validate the limit query parameter.
defaultLimit := uint64(0)
limit, err := query.ValidateUIntQueryParam(ctx, "limit", &defaultLimit)
if err != nil {
return ctx.JSON(http.StatusBadRequest, model.ErrorResponse{
Message: err.Error(),
})
}

// Extract and validate the offset query parameter.
defaultOffset := uint64(0)
offset, err := query.ValidateUIntQueryParam(ctx, "offset", &defaultOffset)
if err != nil {
return ctx.JSON(http.StatusBadRequest, model.ErrorResponse{
Message: err.Error(),
})
}

// Extract and validate the seen query parameter.
seen, err := query.ValidateBoolPQueryParam(ctx, "seen")
if err != nil {
return ctx.JSON(http.StatusBadRequest, model.ErrorResponse{
Message: err.Error(),
})
}

// Extract and validate the sort_dir query parameter.
defaultSortOrder := query.SortOrderDescending
sortOrderParam := query.NewSortOrderParam(&defaultSortOrder)
err = query.ValidateParseableParam(ctx, "sort_dir", sortOrderParam)
if err != nil {
return ctx.JSON(http.StatusBadRequest, model.ErrorResponse{
Message: err.Error(),
})
}

// Extract and validate the sort_field query parameter.
defaultSortField := query.V1ListingSortFieldTimestamp
sortFieldParam := query.NewV1ListingSortFieldParam(&defaultSortField)
err = query.ValidateParseableParam(ctx, "sort_field", sortFieldParam)
if err != nil {
return ctx.JSON(http.StatusBadRequest, model.ErrorResponse{
Message: err.Error(),
})
}

// Extract and reformat the filter.
var notificationType string
filter := strings.ReplaceAll(strings.ToLower(ctx.QueryParam("filter")), " ", "_")
if filter == "new" {
seen = new(bool)
*seen = false
} else if filter != "" {
notificationType = filter
}

// Start a transaction.
tx, err := a.DB.Begin()
if err != nil {
a.Echo.Logger.Error(err)
return err
}
defer tx.Rollback()

// Obtain the listing.
params := &db.V1NotificationListingParameters{
User: user,
Limit: limit,
Offset: offset,
Seen: seen,
SortOrder: *(sortOrderParam.GetValue().(*query.SortOrder)),
SortField: *(sortFieldParam.GetValue().(*query.V1ListingSortField)),
NotificationType: notificationType,
}
listing, err := db.V1ListNotifications(tx, params)
if err != nil {
a.Echo.Logger.Error(err)
return err
}

return ctx.JSON(http.StatusOK, listing)
}

// GetUnseenMessagesHandler handles requests for listing messages that haven't been marked as seen yet for a
// user.
func (a *API) GetUnseenMessagesHandler(ctx echo.Context) error {
var err error

// Extract and validate the user query parameter.
user, err := query.ValidatedQueryParam(ctx, "user", "required")
if err != nil {
return ctx.JSON(http.StatusBadRequest, model.ErrorResponse{
Message: "missing required query parameter: user",
})
}

// Start a transaction.
tx, err := a.DB.Begin()
if err != nil {
a.Echo.Logger.Error(err)
return err
}
defer tx.Rollback()

// Obtain the listing.
seen := false
params := &db.V1NotificationListingParameters{
User: user,
Limit: 0,
Offset: 0,
Seen: &seen,
SortOrder: query.SortOrderAscending,
SortField: query.V1ListingSortFieldTimestamp,
NotificationType: "",
}
listing, err := db.V1ListNotifications(tx, params)
if err != nil {
a.Echo.Logger.Error(err)
return err
}

return ctx.JSON(http.StatusOK, listing)
}

// CountMessagesHandler handles requests for counting notification messages.
func (a *API) CountMessagesHandler(ctx echo.Context) error {
var err error

// Extract and validate the user query parameter.
user, err := query.ValidatedQueryParam(ctx, "user", "required")
if err != nil {
return ctx.JSON(http.StatusBadRequest, model.ErrorResponse{
Message: "missing required query parameter: user",
})
}

// Extract and validate the seen query parameter.
seen, err := query.ValidateBoolPQueryParam(ctx, "seen")
if err != nil {
return ctx.JSON(http.StatusBadRequest, model.ErrorResponse{
Message: err.Error(),
})
}

// Extract and reformat the notification type filter.
notificationType := strings.ReplaceAll(strings.ToLower(ctx.QueryParam("filter")), " ", "_")

// Start a transaction.
tx, err := a.DB.Begin()
if err != nil {
a.Echo.Logger.Error(err)
return err
}
defer tx.Rollback()

// Obtain the counts.
params := &db.V1NotificationCountingParameters{
User: user,
Seen: seen,
NotificationType: notificationType,
}
result, err := db.V1CountNotifications(tx, params)
if err != nil {
a.Echo.Logger.Error(err)
return err
}

return ctx.JSON(http.StatusOK, result)
}
15 changes: 14 additions & 1 deletion api/v1/main.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
package v1

import (
"database/sql"
"net/http"

"github.com/cyverse-de/notifications/common"
"github.com/cyverse-de/notifications/model"
"github.com/labstack/echo"
"gopkg.in/cyverse-de/messaging.v7"
)

// API defines version 1 of the REST API for the notifications service.
type API struct {
Echo *echo.Echo
Group *echo.Group
AMQPSettings *common.AMQPSettings
AMQPClient *messaging.Client
DB *sql.DB
Service string
Title string
Version string
}

// RootHandler handles GET requests to the /v1/ endpoint.
// RootHandler handles GET requests to the /v1 endpoint.
func (a API) RootHandler(ctx echo.Context) error {
resp := model.VersionRootResponse{
Service: a.Service,
Expand All @@ -32,4 +37,12 @@ func (a API) RootHandler(ctx echo.Context) error {
func (a API) RegisterHandlers() {
a.Group.GET("", a.RootHandler)
a.Group.GET("/", a.RootHandler)
a.Group.POST("/notification", a.NotificationRequestHandler)
a.Group.GET("/messages", a.GetMessagesHandler)
a.Group.GET("/unseen-messages", a.GetUnseenMessagesHandler)
a.Group.GET("/count-messages", a.CountMessagesHandler)
a.Group.POST("/seen", a.MarkMessagesAsSeen)
a.Group.POST("/mark-all-seen", a.MarkAllMessagesAsSeen)
a.Group.POST("/delete", a.DeleteMessages)
a.Group.DELETE("/delete-all", a.DeleteMatchingMessages)
}