From 6dda2606d411b9c8f02d670da2744e20fece7ef1 Mon Sep 17 00:00:00 2001 From: Jannis Mattheis Date: Sat, 10 Mar 2018 21:22:13 +0100 Subject: [PATCH] Adjust message api to use new app/client id --- api/internalutil.go | 15 +++++++++ api/message.go | 41 ++++++++++++------------ api/message_test.go | 55 +++++++++++++++++--------------- api/mock/mock_messagedatabase.go | 38 +++++++++++++++------- 4 files changed, 91 insertions(+), 58 deletions(-) create mode 100644 api/internalutil.go diff --git a/api/internalutil.go b/api/internalutil.go new file mode 100644 index 0000000..a4f3b2a --- /dev/null +++ b/api/internalutil.go @@ -0,0 +1,15 @@ +package api + +import ( + "github.com/gin-gonic/gin" + "errors" + "strconv" +) + +func withID(ctx *gin.Context, name string, f func(id uint)) { + if id, err := strconv.ParseUint(ctx.Param(name), 10, 32); err == nil { + f(uint(id)); + } else { + ctx.AbortWithError(400, errors.New("invalid id")) + } +} diff --git a/api/message.go b/api/message.go index 55f2c83..4b161a0 100644 --- a/api/message.go +++ b/api/message.go @@ -2,7 +2,6 @@ package api import ( "errors" - "strconv" "time" "github.com/gin-gonic/gin" @@ -12,14 +11,15 @@ import ( // The MessageDatabase interface for encapsulating database access. type MessageDatabase interface { - GetMessagesByApplication(tokenID string) []*model.Message - GetApplicationByID(id string) *model.Application + GetMessagesByApplication(id uint) []*model.Message + GetApplicationByID(id uint) *model.Application GetMessagesByUser(userID uint) []*model.Message DeleteMessageByID(id uint) error GetMessageByID(id uint) *model.Message DeleteMessagesByUser(userID uint) error - DeleteMessagesByApplication(applicationID string) error + DeleteMessagesByApplication(applicationID uint) error CreateMessage(message *model.Message) error + GetApplicationByToken(token string) *model.Application } // Notifier notifies when a new message was created. @@ -42,9 +42,10 @@ func (a *MessageAPI) GetMessages(ctx *gin.Context) { // GetMessagesWithApplication returns all messages from a specific application. func (a *MessageAPI) GetMessagesWithApplication(ctx *gin.Context) { - appID := ctx.Param("appid") - messages := a.DB.GetMessagesByApplication(appID) - ctx.JSON(200, messages) + withID(ctx, "appid", func(id uint) { + messages := a.DB.GetMessagesByApplication(id) + ctx.JSON(200, messages) + }) } // DeleteMessages delete all messages from a user. @@ -55,33 +56,31 @@ func (a *MessageAPI) DeleteMessages(ctx *gin.Context) { // DeleteMessageWithApplication deletes all messages from a specific application. func (a *MessageAPI) DeleteMessageWithApplication(ctx *gin.Context) { - appID := ctx.Param("appid") - if application := a.DB.GetApplicationByID(appID); application != nil && application.UserID == auth.GetUserID(ctx) { - a.DB.DeleteMessagesByApplication(appID) - } else { - ctx.AbortWithError(404, errors.New("application does not exists")) - } + withID(ctx, "appid", func(id uint) { + if application := a.DB.GetApplicationByID(id); application != nil && application.UserID == auth.GetUserID(ctx) { + a.DB.DeleteMessagesByApplication(id) + } else { + ctx.AbortWithError(404, errors.New("application does not exists")) + } + }); } // DeleteMessage deletes a message with an id. func (a *MessageAPI) DeleteMessage(ctx *gin.Context) { - id := ctx.Param("id") - if parsedUInt, err := strconv.ParseUint(id, 10, 32); err == nil { - if msg := a.DB.GetMessageByID(uint(parsedUInt)); msg != nil && a.DB.GetApplicationByID(msg.ApplicationID).UserID == auth.GetUserID(ctx) { - a.DB.DeleteMessageByID(uint(parsedUInt)) + withID(ctx, "id", func(id uint) { + if msg := a.DB.GetMessageByID(id); msg != nil && a.DB.GetApplicationByID(msg.ApplicationID).UserID == auth.GetUserID(ctx) { + a.DB.DeleteMessageByID(id) } else { ctx.AbortWithError(404, errors.New("message does not exists")) } - } else { - ctx.AbortWithError(400, errors.New("message does not exist")) - } + }) } // CreateMessage creates a message, authentication via application-token is required. func (a *MessageAPI) CreateMessage(ctx *gin.Context) { message := model.Message{} if err := ctx.Bind(&message); err == nil { - message.ApplicationID = auth.GetTokenID(ctx) + message.ApplicationID = a.DB.GetApplicationByToken(auth.GetTokenID(ctx)).ID message.Date = time.Now() a.DB.CreateMessage(&message) a.Notifier.Notify(auth.GetUserID(ctx), &message) diff --git a/api/message_test.go b/api/message_test.go index b9ee0e3..5b346ab 100644 --- a/api/message_test.go +++ b/api/message_test.go @@ -46,27 +46,27 @@ func (s *MessageSuite) Notify(userID uint, msg *model.Message) { func (s *MessageSuite) Test_GetMessages() { auth.RegisterAuthentication(s.ctx, nil, 5, "") t, _ := time.Parse("2006/01/02", "2017/01/02") - s.db.On("GetMessagesByUser", uint(5)).Return([]*model.Message{{ID: 1, ApplicationID: "asd", Message: "OH HELLO THERE", Date: t, Title: "wup", Priority: 2}, {ID: 2, ApplicationID: "cloud", Message: "hi", Title: "hi", Date: t, Priority: 4}}) + s.db.On("GetMessagesByUser", uint(5)).Return([]*model.Message{{ID: 1, ApplicationID: 1, Message: "OH HELLO THERE", Date: t, Title: "wup", Priority: 2}, {ID: 2, ApplicationID: 2, Message: "hi", Title: "hi", Date: t, Priority: 4}}) s.a.GetMessages(s.ctx) assert.Equal(s.T(), 200, s.recorder.Code) bytes, _ := ioutil.ReadAll(s.recorder.Body) - assert.JSONEq(s.T(), `[{"id":1,"appid":"asd","message":"OH HELLO THERE","title":"wup","priority":2,"date":"2017-01-02T00:00:00Z"},{"id":2,"appid":"cloud","message":"hi","title":"hi","priority":4,"date":"2017-01-02T00:00:00Z"}]`, string(bytes)) + assert.JSONEq(s.T(), `[{"id":1,"appid":1,"message":"OH HELLO THERE","title":"wup","priority":2,"date":"2017-01-02T00:00:00Z"},{"id":2,"appid":2,"message":"hi","title":"hi","priority":4,"date":"2017-01-02T00:00:00Z"}]`, string(bytes)) } func (s *MessageSuite) Test_GetMessagesWithToken() { auth.RegisterAuthentication(s.ctx, nil, 4, "") t, _ := time.Parse("2006/01/02", "2021/01/02") - s.db.On("GetMessagesByApplication", "mytoken").Return([]*model.Message{{ID: 2, ApplicationID: "mytoken", Message: "hi", Title: "hi", Date: t, Priority: 4}}) - s.ctx.Params = gin.Params{{Key: "appid", Value: "mytoken"}} + s.db.On("GetMessagesByApplication", uint(1)).Return([]*model.Message{{ID: 2, ApplicationID: 1, Message: "hi", Title: "hi", Date: t, Priority: 4}}) + s.ctx.Params = gin.Params{{Key: "appid", Value: "1"}} s.a.GetMessagesWithApplication(s.ctx) assert.Equal(s.T(), 200, s.recorder.Code) bytes, _ := ioutil.ReadAll(s.recorder.Body) - assert.JSONEq(s.T(), `[{"id":2,"appid":"mytoken","message":"hi","title":"hi","priority":4,"date":"2021-01-02T00:00:00Z"}]`, string(bytes)) + assert.JSONEq(s.T(), `[{"id":2,"appid":1,"message":"hi","title":"hi","priority":4,"date":"2021-01-02T00:00:00Z"}]`, string(bytes)) } func (s *MessageSuite) Test_DeleteMessage_invalidID() { @@ -89,8 +89,8 @@ func (s *MessageSuite) Test_DeleteMessage_notExistingID() { func (s *MessageSuite) Test_DeleteMessage_existingIDButNotOwner() { auth.RegisterAuthentication(s.ctx, nil, 6, "") s.ctx.Params = gin.Params{{Key: "id", Value: "1"}} - s.db.On("GetMessageByID", uint(1)).Return(&model.Message{ID: 1, ApplicationID: "token"}) - s.db.On("GetApplicationByID", "token").Return(&model.Application{ID: "token", UserID: 2}) + s.db.On("GetMessageByID", uint(1)).Return(&model.Message{ID: 1, ApplicationID: 1}) + s.db.On("GetApplicationByID", uint(1)).Return(&model.Application{ID: 1, Token: "token", UserID: 2}) s.a.DeleteMessage(s.ctx) @@ -100,8 +100,8 @@ func (s *MessageSuite) Test_DeleteMessage_existingIDButNotOwner() { func (s *MessageSuite) Test_DeleteMessage() { auth.RegisterAuthentication(s.ctx, nil, 2, "") s.ctx.Params = gin.Params{{Key: "id", Value: "1"}} - s.db.On("GetMessageByID", uint(1)).Return(&model.Message{ID: 1, ApplicationID: "token"}) - s.db.On("GetApplicationByID", "token").Return(&model.Application{ID: "token", UserID: 2}) + s.db.On("GetMessageByID", uint(1)).Return(&model.Message{ID: 1, ApplicationID: 5}) + s.db.On("GetApplicationByID", uint(5)).Return(&model.Application{ID: 5, Token: "token", UserID: 2}) s.db.On("DeleteMessageByID", uint(1)).Return(nil) s.a.DeleteMessage(s.ctx) @@ -112,37 +112,37 @@ func (s *MessageSuite) Test_DeleteMessage() { func (s *MessageSuite) Test_DeleteMessageWithToken() { auth.RegisterAuthentication(s.ctx, nil, 2, "") - s.ctx.Params = gin.Params{{Key: "appid", Value: "mytoken"}} - s.db.On("GetApplicationByID", "mytoken").Return(&model.Application{ID: "mytoken", UserID: 2}) - s.db.On("DeleteMessagesByApplication", "mytoken").Return(nil) + s.ctx.Params = gin.Params{{Key: "appid", Value: "5"}} + s.db.On("GetApplicationByID", uint(5)).Return(&model.Application{ID: 5, Token: "mytoken", UserID: 2}) + s.db.On("DeleteMessagesByApplication", uint(5)).Return(nil) s.a.DeleteMessageWithApplication(s.ctx) - s.db.AssertCalled(s.T(), "DeleteMessagesByApplication", "mytoken") + s.db.AssertCalled(s.T(), "DeleteMessagesByApplication", uint(5)) assert.Equal(s.T(), 200, s.recorder.Code) } func (s *MessageSuite) Test_DeleteMessageWithToken_notExistingToken() { auth.RegisterAuthentication(s.ctx, nil, 2, "") - s.ctx.Params = gin.Params{{Key: "appid", Value: "asdasdasd"}} - s.db.On("GetApplicationByID", "asdasdasd").Return(nil) - s.db.On("DeleteMessagesByApplication", "asdasdasd").Return(nil) + s.ctx.Params = gin.Params{{Key: "appid", Value: "55"}} + s.db.On("GetApplicationByID", uint(55)).Return(nil) + s.db.On("DeleteMessagesByApplication", mock.Anything).Return(nil) s.a.DeleteMessageWithApplication(s.ctx) - s.db.AssertNotCalled(s.T(), "DeleteMessagesByApplication", "mytoken") + s.db.AssertNotCalled(s.T(), "DeleteMessagesByApplication", mock.Anything) assert.Equal(s.T(), 404, s.recorder.Code) } func (s *MessageSuite) Test_DeleteMessageWithToken_notOwner() { auth.RegisterAuthentication(s.ctx, nil, 4, "") - s.ctx.Params = gin.Params{{Key: "appid", Value: "mytoken"}} - s.db.On("GetApplicationByID", "mytoken").Return(&model.Application{ID: "mytoken", UserID: 2}) - s.db.On("DeleteMessagesByApplication", "mytoken").Return(nil) + s.ctx.Params = gin.Params{{Key: "appid", Value: "55"}} + s.db.On("GetApplicationByID", uint(55)).Return(&model.Application{ID: 55, Token: "mytoken", UserID: 2}) + s.db.On("DeleteMessagesByApplication", uint(55)).Return(nil) s.a.DeleteMessageWithApplication(s.ctx) - s.db.AssertNotCalled(s.T(), "DeleteMessagesByApplication", "mytoken") + s.db.AssertNotCalled(s.T(), "DeleteMessagesByApplication", mock.Anything) assert.Equal(s.T(), 404, s.recorder.Code) } @@ -158,11 +158,12 @@ func (s *MessageSuite) Test_DeleteMessages() { func (s *MessageSuite) Test_CreateMessage_onJson_allParams() { auth.RegisterAuthentication(s.ctx, nil, 4, "app-token") + s.db.On("GetApplicationByToken", "app-token").Return(&model.Application{ID: 7}) t, _ := time.Parse("2006/01/02", "2017/01/02") patch := monkey.Patch(time.Now, func() time.Time { return t }) defer patch.Unpatch() - expected := &model.Message{ID: 0, ApplicationID: "app-token", Title: "mytitle", Message: "mymessage", Priority: 1, Date: t} + expected := &model.Message{ID: 0, ApplicationID: 7, Title: "mytitle", Message: "mymessage", Priority: 1, Date: t} s.ctx.Request = httptest.NewRequest("POST", "/token", strings.NewReader(`{"title": "mytitle", "message": "mymessage", "priority": 1}`)) s.ctx.Request.Header.Set("Content-Type", "application/json") @@ -177,11 +178,11 @@ func (s *MessageSuite) Test_CreateMessage_onJson_allParams() { func (s *MessageSuite) Test_CreateMessage_onlyRequired() { auth.RegisterAuthentication(s.ctx, nil, 4, "app-token") - + s.db.On("GetApplicationByToken", "app-token").Return(&model.Application{ID: 5}) t, _ := time.Parse("2006/01/02", "2017/01/02") patch := monkey.Patch(time.Now, func() time.Time { return t }) defer patch.Unpatch() - expected := &model.Message{ID: 0, ApplicationID: "app-token", Title: "mytitle", Message: "mymessage", Date: t} + expected := &model.Message{ID: 0, ApplicationID: 5, Title: "mytitle", Message: "mymessage", Date: t} s.ctx.Request = httptest.NewRequest("POST", "/token", strings.NewReader(`{"title": "mytitle", "message": "mymessage"}`)) s.ctx.Request.Header.Set("Content-Type", "application/json") @@ -235,11 +236,12 @@ func (s *MessageSuite) Test_CreateMessage_failWhenPriorityNotNumber() { func (s *MessageSuite) Test_CreateMessage_onQueryData() { auth.RegisterAuthentication(s.ctx, nil, 4, "app-token") + s.db.On("GetApplicationByToken", "app-token").Return(&model.Application{ID: 2}) t, _ := time.Parse("2006/01/02", "2017/01/02") patch := monkey.Patch(time.Now, func() time.Time { return t }) defer patch.Unpatch() - expected := &model.Message{ID: 0, ApplicationID: "app-token", Title: "mytitle", Message: "mymessage", Priority: 1, Date: t} + expected := &model.Message{ID: 0, ApplicationID: 2, Title: "mytitle", Message: "mymessage", Priority: 1, Date: t} s.ctx.Request = httptest.NewRequest("POST", "/token?title=mytitle&message=mymessage&priority=1", nil) s.ctx.Request.Header.Set("Content-Type", "application/x-www-form-urlencoded") @@ -254,11 +256,12 @@ func (s *MessageSuite) Test_CreateMessage_onQueryData() { func (s *MessageSuite) Test_CreateMessage_onFormData() { auth.RegisterAuthentication(s.ctx, nil, 4, "app-token") + s.db.On("GetApplicationByToken", "app-token").Return(&model.Application{ID: 99}) t, _ := time.Parse("2006/01/02", "2017/01/02") patch := monkey.Patch(time.Now, func() time.Time { return t }) defer patch.Unpatch() - expected := &model.Message{ID: 0, ApplicationID: "app-token", Title: "mytitle", Message: "mymessage", Priority: 1, Date: t} + expected := &model.Message{ID: 0, ApplicationID: 99, Title: "mytitle", Message: "mymessage", Priority: 1, Date: t} s.ctx.Request = httptest.NewRequest("POST", "/token", strings.NewReader("title=mytitle&message=mymessage&priority=1")) s.ctx.Request.Header.Set("Content-Type", "application/x-www-form-urlencoded") diff --git a/api/mock/mock_messagedatabase.go b/api/mock/mock_messagedatabase.go index b09f88e..b3e9a16 100644 --- a/api/mock/mock_messagedatabase.go +++ b/api/mock/mock_messagedatabase.go @@ -1,8 +1,8 @@ // Code generated by mockery v1.0.0 package mock -import mock "github.com/stretchr/testify/mock" -import model "github.com/gotify/server/model" +import "github.com/stretchr/testify/mock" +import "github.com/gotify/server/model" // MockMessageDatabase is an autogenerated mock type for the MessageDatabase type type MockMessageDatabase struct { @@ -38,11 +38,11 @@ func (_m *MockMessageDatabase) DeleteMessageByID(id uint) error { } // DeleteMessagesByApplication provides a mock function with given fields: applicationID -func (_m *MockMessageDatabase) DeleteMessagesByApplication(applicationID string) error { +func (_m *MockMessageDatabase) DeleteMessagesByApplication(applicationID uint) error { ret := _m.Called(applicationID) var r0 error - if rf, ok := ret.Get(0).(func(string) error); ok { + if rf, ok := ret.Get(0).(func(uint) error); ok { r0 = rf(applicationID) } else { r0 = ret.Error(0) @@ -66,11 +66,11 @@ func (_m *MockMessageDatabase) DeleteMessagesByUser(userID uint) error { } // GetApplicationByID provides a mock function with given fields: id -func (_m *MockMessageDatabase) GetApplicationByID(id string) *model.Application { +func (_m *MockMessageDatabase) GetApplicationByID(id uint) *model.Application { ret := _m.Called(id) var r0 *model.Application - if rf, ok := ret.Get(0).(func(string) *model.Application); ok { + if rf, ok := ret.Get(0).(func(uint) *model.Application); ok { r0 = rf(id) } else { if ret.Get(0) != nil { @@ -81,6 +81,22 @@ func (_m *MockMessageDatabase) GetApplicationByID(id string) *model.Application return r0 } +// GetApplicationByToken provides a mock function with given fields: token +func (_m *MockMessageDatabase) GetApplicationByToken(token string) *model.Application { + ret := _m.Called(token) + + var r0 *model.Application + if rf, ok := ret.Get(0).(func(string) *model.Application); ok { + r0 = rf(token) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.Application) + } + } + + return r0 +} + // GetMessageByID provides a mock function with given fields: id func (_m *MockMessageDatabase) GetMessageByID(id uint) *model.Message { ret := _m.Called(id) @@ -97,13 +113,13 @@ func (_m *MockMessageDatabase) GetMessageByID(id uint) *model.Message { return r0 } -// GetMessagesByApplication provides a mock function with given fields: tokenID -func (_m *MockMessageDatabase) GetMessagesByApplication(tokenID string) []*model.Message { - ret := _m.Called(tokenID) +// GetMessagesByApplication provides a mock function with given fields: id +func (_m *MockMessageDatabase) GetMessagesByApplication(id uint) []*model.Message { + ret := _m.Called(id) var r0 []*model.Message - if rf, ok := ret.Get(0).(func(string) []*model.Message); ok { - r0 = rf(tokenID) + if rf, ok := ret.Get(0).(func(uint) []*model.Message); ok { + r0 = rf(id) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]*model.Message)