add extras to message model
This commit is contained in:
parent
98710db507
commit
de09aae987
|
|
@ -1,6 +1,7 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -30,7 +31,7 @@ var timeNow = time.Now
|
||||||
|
|
||||||
// Notifier notifies when a new message was created.
|
// Notifier notifies when a new message was created.
|
||||||
type Notifier interface {
|
type Notifier interface {
|
||||||
Notify(userID uint, message *model.Message)
|
Notify(userID uint, message *model.MessageExternal)
|
||||||
}
|
}
|
||||||
|
|
||||||
// The MessageAPI provides handlers for managing messages.
|
// The MessageAPI provides handlers for managing messages.
|
||||||
|
|
@ -110,7 +111,7 @@ func buildWithPaging(ctx *gin.Context, paging *pagingParams, messages []*model.M
|
||||||
}
|
}
|
||||||
return &model.PagedMessages{
|
return &model.PagedMessages{
|
||||||
Paging: model.Paging{Size: len(useMessages), Limit: paging.Limit, Next: next, Since: since},
|
Paging: model.Paging{Size: len(useMessages), Limit: paging.Limit, Next: next, Since: since},
|
||||||
Messages: useMessages,
|
Messages: toExternalMessages(useMessages),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -329,7 +330,7 @@ func (a *MessageAPI) DeleteMessage(ctx *gin.Context) {
|
||||||
// schema:
|
// schema:
|
||||||
// $ref: "#/definitions/Error"
|
// $ref: "#/definitions/Error"
|
||||||
func (a *MessageAPI) CreateMessage(ctx *gin.Context) {
|
func (a *MessageAPI) CreateMessage(ctx *gin.Context) {
|
||||||
message := model.Message{}
|
message := model.MessageExternal{}
|
||||||
if err := ctx.Bind(&message); err == nil {
|
if err := ctx.Bind(&message); err == nil {
|
||||||
application := a.DB.GetApplicationByToken(auth.GetTokenID(ctx))
|
application := a.DB.GetApplicationByToken(auth.GetTokenID(ctx))
|
||||||
message.ApplicationID = application.ID
|
message.ApplicationID = application.ID
|
||||||
|
|
@ -337,8 +338,48 @@ func (a *MessageAPI) CreateMessage(ctx *gin.Context) {
|
||||||
message.Title = application.Name
|
message.Title = application.Name
|
||||||
}
|
}
|
||||||
message.Date = timeNow()
|
message.Date = timeNow()
|
||||||
a.DB.CreateMessage(&message)
|
msgInternal := toInternalMessage(&message)
|
||||||
a.Notifier.Notify(auth.GetUserID(ctx), &message)
|
a.DB.CreateMessage(msgInternal)
|
||||||
ctx.JSON(200, message)
|
a.Notifier.Notify(auth.GetUserID(ctx), toExternalMessage(msgInternal))
|
||||||
|
ctx.JSON(200, toExternalMessage(msgInternal))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func toInternalMessage(msg *model.MessageExternal) *model.Message {
|
||||||
|
res := &model.Message{
|
||||||
|
ID: msg.ID,
|
||||||
|
ApplicationID: msg.ApplicationID,
|
||||||
|
Message: msg.Message,
|
||||||
|
Title: msg.Title,
|
||||||
|
Priority: msg.Priority,
|
||||||
|
Date: msg.Date,
|
||||||
|
}
|
||||||
|
if msg.Extras != nil {
|
||||||
|
res.Extras, _ = json.Marshal(msg.Extras)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func toExternalMessage(msg *model.Message) *model.MessageExternal {
|
||||||
|
res := &model.MessageExternal{
|
||||||
|
ID: msg.ID,
|
||||||
|
ApplicationID: msg.ApplicationID,
|
||||||
|
Message: msg.Message,
|
||||||
|
Title: msg.Title,
|
||||||
|
Priority: msg.Priority,
|
||||||
|
Date: msg.Date,
|
||||||
|
}
|
||||||
|
if len(msg.Extras) != 0 {
|
||||||
|
res.Extras = make(map[string]interface{})
|
||||||
|
json.Unmarshal(msg.Extras, &res.Extras)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func toExternalMessages(msg []*model.Message) []*model.MessageExternal {
|
||||||
|
res := make([]*model.MessageExternal, len(msg))
|
||||||
|
for i := range msg {
|
||||||
|
res[i] = toExternalMessage(msg[i])
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ type MessageSuite struct {
|
||||||
a *MessageAPI
|
a *MessageAPI
|
||||||
ctx *gin.Context
|
ctx *gin.Context
|
||||||
recorder *httptest.ResponseRecorder
|
recorder *httptest.ResponseRecorder
|
||||||
notified bool
|
notifiedMessage *model.MessageExternal
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MessageSuite) BeforeTest(suiteName, testName string) {
|
func (s *MessageSuite) BeforeTest(suiteName, testName string) {
|
||||||
|
|
@ -35,7 +35,7 @@ func (s *MessageSuite) BeforeTest(suiteName, testName string) {
|
||||||
s.ctx, _ = gin.CreateTestContext(s.recorder)
|
s.ctx, _ = gin.CreateTestContext(s.recorder)
|
||||||
s.ctx.Request = httptest.NewRequest("GET", "/irrelevant", nil)
|
s.ctx.Request = httptest.NewRequest("GET", "/irrelevant", nil)
|
||||||
s.db = test.NewDB(s.T())
|
s.db = test.NewDB(s.T())
|
||||||
s.notified = false
|
s.notifiedMessage = nil
|
||||||
s.a = &MessageAPI{DB: s.db, Notifier: s}
|
s.a = &MessageAPI{DB: s.db, Notifier: s}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -43,8 +43,8 @@ func (s *MessageSuite) AfterTest(string, string) {
|
||||||
s.db.Close()
|
s.db.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MessageSuite) Notify(userID uint, msg *model.Message) {
|
func (s *MessageSuite) Notify(userID uint, msg *model.MessageExternal) {
|
||||||
s.notified = true
|
s.notifiedMessage = msg
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MessageSuite) Test_ensureCorrectJsonRepresentation() {
|
func (s *MessageSuite) Test_ensureCorrectJsonRepresentation() {
|
||||||
|
|
@ -52,23 +52,30 @@ func (s *MessageSuite) Test_ensureCorrectJsonRepresentation() {
|
||||||
|
|
||||||
actual := &model.PagedMessages{
|
actual := &model.PagedMessages{
|
||||||
Paging: model.Paging{Limit: 5, Since: 122, Size: 5, Next: "http://example.com/message?limit=5&since=122"},
|
Paging: model.Paging{Limit: 5, Since: 122, Size: 5, Next: "http://example.com/message?limit=5&since=122"},
|
||||||
Messages: []*model.Message{{ID: 55, ApplicationID: 2, Message: "hi", Title: "hi", Date: t, Priority: 4}},
|
Messages: []*model.MessageExternal{{ID: 55, ApplicationID: 2, Message: "hi", Title: "hi", Date: t, Priority: 4, Extras: map[string]interface{}{
|
||||||
|
"test::string": "string",
|
||||||
|
"test::array": []interface{}{1, 2, 3},
|
||||||
|
"test::int": 1,
|
||||||
|
"test::float": 0.5,
|
||||||
|
}}},
|
||||||
}
|
}
|
||||||
test.JSONEquals(s.T(), actual, `{"paging": {"limit":5, "since": 122, "size": 5, "next": "http://example.com/message?limit=5&since=122"},
|
test.JSONEquals(s.T(), actual, `{"paging": {"limit":5, "since": 122, "size": 5, "next": "http://example.com/message?limit=5&since=122"},
|
||||||
"messages": [{"id":55,"appid":2,"message":"hi","title":"hi","priority":4,"date":"2017-01-02T00:00:00Z"}]}`)
|
"messages": [{"id":55,"appid":2,"message":"hi","title":"hi","priority":4,"date":"2017-01-02T00:00:00Z","extras":{"test::string":"string","test::array":[1,2,3],"test::int":1,"test::float":0.5}}]}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MessageSuite) Test_GetMessages() {
|
func (s *MessageSuite) Test_GetMessages() {
|
||||||
user := s.db.User(5)
|
user := s.db.User(5)
|
||||||
first := user.App(1).NewMessage(1)
|
first := user.App(1).NewMessage(1)
|
||||||
second := user.App(2).NewMessage(2)
|
second := user.App(2).NewMessage(2)
|
||||||
|
firstExternal := toExternalMessage(&first)
|
||||||
|
secondExternal := toExternalMessage(&second)
|
||||||
|
|
||||||
test.WithUser(s.ctx, 5)
|
test.WithUser(s.ctx, 5)
|
||||||
s.a.GetMessages(s.ctx)
|
s.a.GetMessages(s.ctx)
|
||||||
|
|
||||||
expected := &model.PagedMessages{
|
expected := &model.PagedMessages{
|
||||||
Paging: model.Paging{Limit: 100, Size: 2, Next: ""},
|
Paging: model.Paging{Limit: 100, Size: 2, Next: ""},
|
||||||
Messages: []*model.Message{&second, &first},
|
Messages: []*model.MessageExternal{secondExternal, firstExternal},
|
||||||
}
|
}
|
||||||
|
|
||||||
test.BodyEquals(s.T(), expected, s.recorder)
|
test.BodyEquals(s.T(), expected, s.recorder)
|
||||||
|
|
@ -92,7 +99,7 @@ func (s *MessageSuite) Test_GetMessages_WithLimit_ReturnsNext() {
|
||||||
// Since: entries with ids from 100 - 96 will be returned (5 entries)
|
// Since: entries with ids from 100 - 96 will be returned (5 entries)
|
||||||
expected := &model.PagedMessages{
|
expected := &model.PagedMessages{
|
||||||
Paging: model.Paging{Limit: 5, Size: 5, Since: 96, Next: "http://example.com/messages?limit=5&since=96"},
|
Paging: model.Paging{Limit: 5, Size: 5, Since: 96, Next: "http://example.com/messages?limit=5&since=96"},
|
||||||
Messages: messages[:5],
|
Messages: toExternalMessages(messages[:5]),
|
||||||
}
|
}
|
||||||
|
|
||||||
test.BodyEquals(s.T(), expected, s.recorder)
|
test.BodyEquals(s.T(), expected, s.recorder)
|
||||||
|
|
@ -116,7 +123,7 @@ func (s *MessageSuite) Test_GetMessages_WithLimit_WithSince_ReturnsNext() {
|
||||||
// Since: entries with ids from 54 - 42 will be returned (13 entries)
|
// Since: entries with ids from 54 - 42 will be returned (13 entries)
|
||||||
expected := &model.PagedMessages{
|
expected := &model.PagedMessages{
|
||||||
Paging: model.Paging{Limit: 13, Size: 13, Since: 42, Next: "http://example.com/messages?limit=13&since=42"},
|
Paging: model.Paging{Limit: 13, Size: 13, Since: 42, Next: "http://example.com/messages?limit=13&since=42"},
|
||||||
Messages: messages[46 : 46+13],
|
Messages: toExternalMessages(messages[46 : 46+13]),
|
||||||
}
|
}
|
||||||
test.BodyEquals(s.T(), expected, s.recorder)
|
test.BodyEquals(s.T(), expected, s.recorder)
|
||||||
}
|
}
|
||||||
|
|
@ -159,7 +166,7 @@ func (s *MessageSuite) Test_GetMessagesWithToken() {
|
||||||
|
|
||||||
expected := &model.PagedMessages{
|
expected := &model.PagedMessages{
|
||||||
Paging: model.Paging{Limit: 100, Size: 1, Next: ""},
|
Paging: model.Paging{Limit: 100, Size: 1, Next: ""},
|
||||||
Messages: []*model.Message{&msg},
|
Messages: toExternalMessages([]*model.Message{&msg}),
|
||||||
}
|
}
|
||||||
|
|
||||||
test.BodyEquals(s.T(), expected, s.recorder)
|
test.BodyEquals(s.T(), expected, s.recorder)
|
||||||
|
|
@ -182,7 +189,7 @@ func (s *MessageSuite) Test_GetMessagesWithToken_WithLimit_ReturnsNext() {
|
||||||
// Since: entries with ids from 100 - 92 will be returned (9 entries)
|
// Since: entries with ids from 100 - 92 will be returned (9 entries)
|
||||||
expected := &model.PagedMessages{
|
expected := &model.PagedMessages{
|
||||||
Paging: model.Paging{Limit: 9, Size: 9, Since: 92, Next: "http://example.com/app/2/message?limit=9&since=92"},
|
Paging: model.Paging{Limit: 9, Size: 9, Since: 92, Next: "http://example.com/app/2/message?limit=9&since=92"},
|
||||||
Messages: messages[:9],
|
Messages: toExternalMessages(messages[:9]),
|
||||||
}
|
}
|
||||||
|
|
||||||
test.BodyEquals(s.T(), expected, s.recorder)
|
test.BodyEquals(s.T(), expected, s.recorder)
|
||||||
|
|
@ -205,7 +212,7 @@ func (s *MessageSuite) Test_GetMessagesWithToken_WithLimit_WithSince_ReturnsNext
|
||||||
// Since: entries with ids from 54 - 42 will be returned (13 entries)
|
// Since: entries with ids from 54 - 42 will be returned (13 entries)
|
||||||
expected := &model.PagedMessages{
|
expected := &model.PagedMessages{
|
||||||
Paging: model.Paging{Limit: 13, Size: 13, Since: 42, Next: "http://example.com/app/2/message?limit=13&since=42"},
|
Paging: model.Paging{Limit: 13, Size: 13, Since: 42, Next: "http://example.com/app/2/message?limit=13&since=42"},
|
||||||
Messages: messages[46 : 46+13],
|
Messages: toExternalMessages(messages[46 : 46+13]),
|
||||||
}
|
}
|
||||||
test.BodyEquals(s.T(), expected, s.recorder)
|
test.BodyEquals(s.T(), expected, s.recorder)
|
||||||
}
|
}
|
||||||
|
|
@ -316,17 +323,17 @@ func (s *MessageSuite) Test_CreateMessage_onJson_allParams() {
|
||||||
|
|
||||||
auth.RegisterAuthentication(s.ctx, nil, 4, "app-token")
|
auth.RegisterAuthentication(s.ctx, nil, 4, "app-token")
|
||||||
s.db.User(4).AppWithToken(7, "app-token")
|
s.db.User(4).AppWithToken(7, "app-token")
|
||||||
s.ctx.Request = httptest.NewRequest("POST", "/token", strings.NewReader(`{"title": "mytitle", "message": "mymessage", "priority": 1}`))
|
s.ctx.Request = httptest.NewRequest("POST", "/message", strings.NewReader(`{"title": "mytitle", "message": "mymessage", "priority": 1}`))
|
||||||
s.ctx.Request.Header.Set("Content-Type", "application/json")
|
s.ctx.Request.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
s.a.CreateMessage(s.ctx)
|
s.a.CreateMessage(s.ctx)
|
||||||
|
|
||||||
msgs := s.db.GetMessagesByApplication(7)
|
msgs := s.db.GetMessagesByApplication(7)
|
||||||
expected := &model.Message{ID: 1, ApplicationID: 7, Title: "mytitle", Message: "mymessage", Priority: 1, Date: t}
|
expected := &model.MessageExternal{ID: 1, ApplicationID: 7, Title: "mytitle", Message: "mymessage", Priority: 1, Date: t}
|
||||||
assert.Len(s.T(), msgs, 1)
|
assert.Len(s.T(), msgs, 1)
|
||||||
assert.Contains(s.T(), msgs, expected)
|
assert.Equal(s.T(), expected, toExternalMessage(msgs[0]))
|
||||||
assert.Equal(s.T(), 200, s.recorder.Code)
|
assert.Equal(s.T(), 200, s.recorder.Code)
|
||||||
assert.True(s.T(), s.notified)
|
assert.Equal(s.T(), expected, s.notifiedMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MessageSuite) Test_CreateMessage_WithTitle() {
|
func (s *MessageSuite) Test_CreateMessage_WithTitle() {
|
||||||
|
|
@ -336,38 +343,38 @@ func (s *MessageSuite) Test_CreateMessage_WithTitle() {
|
||||||
|
|
||||||
auth.RegisterAuthentication(s.ctx, nil, 4, "app-token")
|
auth.RegisterAuthentication(s.ctx, nil, 4, "app-token")
|
||||||
s.db.User(4).AppWithToken(5, "app-token")
|
s.db.User(4).AppWithToken(5, "app-token")
|
||||||
s.ctx.Request = httptest.NewRequest("POST", "/token", strings.NewReader(`{"title": "mytitle", "message": "mymessage"}`))
|
s.ctx.Request = httptest.NewRequest("POST", "/message", strings.NewReader(`{"title": "mytitle", "message": "mymessage"}`))
|
||||||
s.ctx.Request.Header.Set("Content-Type", "application/json")
|
s.ctx.Request.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
s.a.CreateMessage(s.ctx)
|
s.a.CreateMessage(s.ctx)
|
||||||
|
|
||||||
msgs := s.db.GetMessagesByApplication(5)
|
msgs := s.db.GetMessagesByApplication(5)
|
||||||
expected := &model.Message{ID: 1, ApplicationID: 5, Title: "mytitle", Message: "mymessage", Date: t}
|
expected := &model.MessageExternal{ID: 1, ApplicationID: 5, Title: "mytitle", Message: "mymessage", Date: t}
|
||||||
assert.Len(s.T(), msgs, 1)
|
assert.Len(s.T(), msgs, 1)
|
||||||
assert.Contains(s.T(), msgs, expected)
|
assert.Equal(s.T(), expected, toExternalMessage(msgs[0]))
|
||||||
assert.Equal(s.T(), 200, s.recorder.Code)
|
assert.Equal(s.T(), 200, s.recorder.Code)
|
||||||
assert.True(s.T(), s.notified)
|
assert.Equal(s.T(), expected, s.notifiedMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MessageSuite) Test_CreateMessage_failWhenNoMessage() {
|
func (s *MessageSuite) Test_CreateMessage_failWhenNoMessage() {
|
||||||
auth.RegisterAuthentication(s.ctx, nil, 4, "app-token")
|
auth.RegisterAuthentication(s.ctx, nil, 4, "app-token")
|
||||||
s.db.User(4).AppWithToken(1, "app-token")
|
s.db.User(4).AppWithToken(1, "app-token")
|
||||||
|
|
||||||
s.ctx.Request = httptest.NewRequest("POST", "/token", strings.NewReader(`{"title": "mytitle"}`))
|
s.ctx.Request = httptest.NewRequest("POST", "/message", strings.NewReader(`{"title": "mytitle"}`))
|
||||||
s.ctx.Request.Header.Set("Content-Type", "application/json")
|
s.ctx.Request.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
s.a.CreateMessage(s.ctx)
|
s.a.CreateMessage(s.ctx)
|
||||||
|
|
||||||
assert.Empty(s.T(), s.db.GetMessagesByApplication(1))
|
assert.Empty(s.T(), s.db.GetMessagesByApplication(1))
|
||||||
assert.Equal(s.T(), 400, s.recorder.Code)
|
assert.Equal(s.T(), 400, s.recorder.Code)
|
||||||
assert.False(s.T(), s.notified)
|
assert.Nil(s.T(), s.notifiedMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MessageSuite) Test_CreateMessage_WithoutTitle() {
|
func (s *MessageSuite) Test_CreateMessage_WithoutTitle() {
|
||||||
auth.RegisterAuthentication(s.ctx, nil, 4, "app-token")
|
auth.RegisterAuthentication(s.ctx, nil, 4, "app-token")
|
||||||
s.db.User(4).AppWithTokenAndName(8, "app-token", "Application name")
|
s.db.User(4).AppWithTokenAndName(8, "app-token", "Application name")
|
||||||
|
|
||||||
s.ctx.Request = httptest.NewRequest("POST", "/token", strings.NewReader(`{"message": "mymessage"}`))
|
s.ctx.Request = httptest.NewRequest("POST", "/message", strings.NewReader(`{"message": "mymessage"}`))
|
||||||
s.ctx.Request.Header.Set("Content-Type", "application/json")
|
s.ctx.Request.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
s.a.CreateMessage(s.ctx)
|
s.a.CreateMessage(s.ctx)
|
||||||
|
|
@ -376,14 +383,14 @@ func (s *MessageSuite) Test_CreateMessage_WithoutTitle() {
|
||||||
assert.Len(s.T(), msgs, 1)
|
assert.Len(s.T(), msgs, 1)
|
||||||
assert.Equal(s.T(), "Application name", msgs[0].Title)
|
assert.Equal(s.T(), "Application name", msgs[0].Title)
|
||||||
assert.Equal(s.T(), 200, s.recorder.Code)
|
assert.Equal(s.T(), 200, s.recorder.Code)
|
||||||
assert.True(s.T(), s.notified)
|
assert.Equal(s.T(), "mymessage", s.notifiedMessage.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MessageSuite) Test_CreateMessage_WithBlankTitle() {
|
func (s *MessageSuite) Test_CreateMessage_WithBlankTitle() {
|
||||||
auth.RegisterAuthentication(s.ctx, nil, 4, "app-token")
|
auth.RegisterAuthentication(s.ctx, nil, 4, "app-token")
|
||||||
s.db.User(4).AppWithTokenAndName(8, "app-token", "Application name")
|
s.db.User(4).AppWithTokenAndName(8, "app-token", "Application name")
|
||||||
|
|
||||||
s.ctx.Request = httptest.NewRequest("POST", "/token", strings.NewReader(`{"message": "mymessage", "title": " "}`))
|
s.ctx.Request = httptest.NewRequest("POST", "/message", strings.NewReader(`{"message": "mymessage", "title": " "}`))
|
||||||
s.ctx.Request.Header.Set("Content-Type", "application/json")
|
s.ctx.Request.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
s.a.CreateMessage(s.ctx)
|
s.a.CreateMessage(s.ctx)
|
||||||
|
|
@ -392,20 +399,56 @@ func (s *MessageSuite) Test_CreateMessage_WithBlankTitle() {
|
||||||
assert.Len(s.T(), msgs, 1)
|
assert.Len(s.T(), msgs, 1)
|
||||||
assert.Equal(s.T(), "Application name", msgs[0].Title)
|
assert.Equal(s.T(), "Application name", msgs[0].Title)
|
||||||
assert.Equal(s.T(), 200, s.recorder.Code)
|
assert.Equal(s.T(), 200, s.recorder.Code)
|
||||||
assert.True(s.T(), s.notified)
|
assert.Equal(s.T(), "mymessage", msgs[0].Message)
|
||||||
|
}
|
||||||
|
func (s *MessageSuite) Test_CreateMessage_WithExtras() {
|
||||||
|
auth.RegisterAuthentication(s.ctx, nil, 4, "app-token")
|
||||||
|
s.db.User(4).AppWithTokenAndName(8, "app-token", "Application name")
|
||||||
|
|
||||||
|
t, _ := time.Parse("2006/01/02", "2017/01/02")
|
||||||
|
timeNow = func() time.Time { return t }
|
||||||
|
defer func() { timeNow = time.Now }()
|
||||||
|
|
||||||
|
s.ctx.Request = httptest.NewRequest("POST", "/message", strings.NewReader(`{"message": "mymessage", "title": "msg with extras", "extras": {"gotify::test":{"int":1,"float":0.5,"string":"test","array":[1,2,3]}}}`))
|
||||||
|
s.ctx.Request.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
s.a.CreateMessage(s.ctx)
|
||||||
|
|
||||||
|
msgs := s.db.GetMessagesByApplication(8)
|
||||||
|
expected := &model.MessageExternal{
|
||||||
|
ID: 1,
|
||||||
|
ApplicationID: 8,
|
||||||
|
Message: "mymessage",
|
||||||
|
Title: "msg with extras",
|
||||||
|
Date: t,
|
||||||
|
Extras: map[string]interface{}{
|
||||||
|
"gotify::test": map[string]interface{}{
|
||||||
|
"string": "test",
|
||||||
|
"array": []interface{}{float64(1), float64(2), float64(3)},
|
||||||
|
"int": float64(1),
|
||||||
|
"float": float64(0.5),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
assert.Len(s.T(), msgs, 1)
|
||||||
|
|
||||||
|
assert.Equal(s.T(), expected, toExternalMessage(msgs[0]))
|
||||||
|
|
||||||
|
assert.Equal(s.T(), 200, s.recorder.Code)
|
||||||
|
assert.Equal(s.T(), uint(1), s.notifiedMessage.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MessageSuite) Test_CreateMessage_failWhenPriorityNotNumber() {
|
func (s *MessageSuite) Test_CreateMessage_failWhenPriorityNotNumber() {
|
||||||
auth.RegisterAuthentication(s.ctx, nil, 4, "app-token")
|
auth.RegisterAuthentication(s.ctx, nil, 4, "app-token")
|
||||||
s.db.User(4).AppWithToken(8, "app-token")
|
s.db.User(4).AppWithToken(8, "app-token")
|
||||||
|
|
||||||
s.ctx.Request = httptest.NewRequest("POST", "/token", strings.NewReader(`{"title": "mytitle", "message": "mymessage", "priority": "asd"}`))
|
s.ctx.Request = httptest.NewRequest("POST", "/message", strings.NewReader(`{"title": "mytitle", "message": "mymessage", "priority": "asd"}`))
|
||||||
s.ctx.Request.Header.Set("Content-Type", "application/json")
|
s.ctx.Request.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
s.a.CreateMessage(s.ctx)
|
s.a.CreateMessage(s.ctx)
|
||||||
|
|
||||||
assert.Equal(s.T(), 400, s.recorder.Code)
|
assert.Equal(s.T(), 400, s.recorder.Code)
|
||||||
assert.False(s.T(), s.notified)
|
assert.Nil(s.T(), s.notifiedMessage)
|
||||||
assert.Empty(s.T(), s.db.GetMessagesByApplication(1))
|
assert.Empty(s.T(), s.db.GetMessagesByApplication(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -417,20 +460,19 @@ func (s *MessageSuite) Test_CreateMessage_onQueryData() {
|
||||||
timeNow = func() time.Time { return t }
|
timeNow = func() time.Time { return t }
|
||||||
defer func() { timeNow = time.Now }()
|
defer func() { timeNow = time.Now }()
|
||||||
|
|
||||||
s.ctx.Request = httptest.NewRequest("POST", "/token?title=mytitle&message=mymessage&priority=1", nil)
|
s.ctx.Request = httptest.NewRequest("POST", "/message?title=mytitle&message=mymessage&priority=1", nil)
|
||||||
s.ctx.Request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
s.ctx.Request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
|
||||||
s.a.CreateMessage(s.ctx)
|
s.a.CreateMessage(s.ctx)
|
||||||
|
|
||||||
expected := &model.Message{ID: 1, ApplicationID: 2, Title: "mytitle", Message: "mymessage", Priority: 1, Date: t}
|
expected := &model.MessageExternal{ID: 1, ApplicationID: 2, Title: "mytitle", Message: "mymessage", Priority: 1, Date: t}
|
||||||
|
|
||||||
msgs := s.db.GetMessagesByApplication(2)
|
msgs := s.db.GetMessagesByApplication(2)
|
||||||
assert.Len(s.T(), msgs, 1)
|
assert.Len(s.T(), msgs, 1)
|
||||||
assert.Contains(s.T(), msgs, expected)
|
assert.Equal(s.T(), expected, toExternalMessage(msgs[0]))
|
||||||
assert.Equal(s.T(), 200, s.recorder.Code)
|
assert.Equal(s.T(), 200, s.recorder.Code)
|
||||||
assert.True(s.T(), s.notified)
|
assert.Equal(s.T(), uint(1), s.notifiedMessage.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MessageSuite) Test_CreateMessage_onFormData() {
|
func (s *MessageSuite) Test_CreateMessage_onFormData() {
|
||||||
auth.RegisterAuthentication(s.ctx, nil, 4, "app-token")
|
auth.RegisterAuthentication(s.ctx, nil, 4, "app-token")
|
||||||
s.db.User(4).AppWithToken(99, "app-token")
|
s.db.User(4).AppWithToken(99, "app-token")
|
||||||
|
|
@ -439,17 +481,17 @@ func (s *MessageSuite) Test_CreateMessage_onFormData() {
|
||||||
timeNow = func() time.Time { return t }
|
timeNow = func() time.Time { return t }
|
||||||
defer func() { timeNow = time.Now }()
|
defer func() { timeNow = time.Now }()
|
||||||
|
|
||||||
s.ctx.Request = httptest.NewRequest("POST", "/token", strings.NewReader("title=mytitle&message=mymessage&priority=1"))
|
s.ctx.Request = httptest.NewRequest("POST", "/message", strings.NewReader(`title=mytitle&message=mymessage&priority=1`))
|
||||||
s.ctx.Request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
s.ctx.Request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
|
||||||
s.a.CreateMessage(s.ctx)
|
s.a.CreateMessage(s.ctx)
|
||||||
|
|
||||||
expected := &model.Message{ID: 1, ApplicationID: 99, Title: "mytitle", Message: "mymessage", Priority: 1, Date: t}
|
expected := &model.MessageExternal{ID: 1, ApplicationID: 99, Title: "mytitle", Message: "mymessage", Priority: 1, Date: t}
|
||||||
msgs := s.db.GetMessagesByApplication(99)
|
msgs := s.db.GetMessagesByApplication(99)
|
||||||
assert.Len(s.T(), msgs, 1)
|
assert.Len(s.T(), msgs, 1)
|
||||||
assert.Contains(s.T(), msgs, expected)
|
assert.Equal(s.T(), expected, toExternalMessage(msgs[0]))
|
||||||
assert.Equal(s.T(), 200, s.recorder.Code)
|
assert.Equal(s.T(), 200, s.recorder.Code)
|
||||||
assert.True(s.T(), s.notified)
|
assert.Equal(s.T(), uint(1), s.notifiedMessage.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MessageSuite) withURL(scheme, host, path, query string) {
|
func (s *MessageSuite) withURL(scheme, host, path, query string) {
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ var writeJSON = func(conn *websocket.Conn, v interface{}) error {
|
||||||
type client struct {
|
type client struct {
|
||||||
conn *websocket.Conn
|
conn *websocket.Conn
|
||||||
onClose func(*client)
|
onClose func(*client)
|
||||||
write chan *model.Message
|
write chan *model.MessageExternal
|
||||||
userID uint
|
userID uint
|
||||||
token string
|
token string
|
||||||
once once
|
once once
|
||||||
|
|
@ -31,7 +31,7 @@ type client struct {
|
||||||
func newClient(conn *websocket.Conn, userID uint, token string, onClose func(*client)) *client {
|
func newClient(conn *websocket.Conn, userID uint, token string, onClose func(*client)) *client {
|
||||||
return &client{
|
return &client{
|
||||||
conn: conn,
|
conn: conn,
|
||||||
write: make(chan *model.Message, 1),
|
write: make(chan *model.MessageExternal, 1),
|
||||||
userID: userID,
|
userID: userID,
|
||||||
token: token,
|
token: token,
|
||||||
onClose: onClose,
|
onClose: onClose,
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ func (a *API) NotifyDeletedClient(userID uint, token string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify notifies the clients with the given userID that a new messages was created.
|
// Notify notifies the clients with the given userID that a new messages was created.
|
||||||
func (a *API) Notify(userID uint, msg *model.Message) {
|
func (a *API) Notify(userID uint, msg *model.MessageExternal) {
|
||||||
a.lock.RLock()
|
a.lock.RLock()
|
||||||
defer a.lock.RUnlock()
|
defer a.lock.RUnlock()
|
||||||
if clients, ok := a.clients[userID]; ok {
|
if clients, ok := a.clients[userID]; ok {
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ func TestWriteMessageFails(t *testing.T) {
|
||||||
clients := clients(api, 1)
|
clients := clients(api, 1)
|
||||||
assert.NotEmpty(t, clients)
|
assert.NotEmpty(t, clients)
|
||||||
|
|
||||||
api.Notify(1, &model.Message{Message: "HI"})
|
api.Notify(1, &model.MessageExternal{Message: "HI"})
|
||||||
user.expectNoMessage()
|
user.expectNoMessage()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -94,7 +94,7 @@ func TestWritePingFails(t *testing.T) {
|
||||||
|
|
||||||
time.Sleep(5 * time.Second) // waiting for ping
|
time.Sleep(5 * time.Second) // waiting for ping
|
||||||
|
|
||||||
api.Notify(1, &model.Message{Message: "HI"})
|
api.Notify(1, &model.MessageExternal{Message: "HI"})
|
||||||
user.expectNoMessage()
|
user.expectNoMessage()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -130,8 +130,8 @@ func TestPing(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
expectNoMessage(user)
|
expectNoMessage(user)
|
||||||
api.Notify(1, &model.Message{Message: "HI"})
|
api.Notify(1, &model.MessageExternal{Message: "HI"})
|
||||||
user.expectMessage(&model.Message{Message: "HI"})
|
user.expectMessage(&model.MessageExternal{Message: "HI"})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCloseClientOnNotReading(t *testing.T) {
|
func TestCloseClientOnNotReading(t *testing.T) {
|
||||||
|
|
@ -169,8 +169,8 @@ func TestMessageDirectlyAfterConnect(t *testing.T) {
|
||||||
defer user.conn.Close()
|
defer user.conn.Close()
|
||||||
// the server may take some time to register the client
|
// the server may take some time to register the client
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
api.Notify(1, &model.Message{Message: "msg"})
|
api.Notify(1, &model.MessageExternal{Message: "msg"})
|
||||||
user.expectMessage(&model.Message{Message: "msg"})
|
user.expectMessage(&model.MessageExternal{Message: "msg"})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDeleteClientShouldCloseConnection(t *testing.T) {
|
func TestDeleteClientShouldCloseConnection(t *testing.T) {
|
||||||
|
|
@ -186,12 +186,12 @@ func TestDeleteClientShouldCloseConnection(t *testing.T) {
|
||||||
defer user.conn.Close()
|
defer user.conn.Close()
|
||||||
// the server may take some time to register the client
|
// the server may take some time to register the client
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
api.Notify(1, &model.Message{Message: "msg"})
|
api.Notify(1, &model.MessageExternal{Message: "msg"})
|
||||||
user.expectMessage(&model.Message{Message: "msg"})
|
user.expectMessage(&model.MessageExternal{Message: "msg"})
|
||||||
|
|
||||||
api.NotifyDeletedClient(1, "customtoken")
|
api.NotifyDeletedClient(1, "customtoken")
|
||||||
|
|
||||||
api.Notify(1, &model.Message{Message: "msg"})
|
api.Notify(1, &model.MessageExternal{Message: "msg"})
|
||||||
user.expectNoMessage()
|
user.expectNoMessage()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -233,28 +233,28 @@ func TestDeleteMultipleClients(t *testing.T) {
|
||||||
// the server may take some time to register the client
|
// the server may take some time to register the client
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
api.Notify(1, &model.Message{ID: 4, Message: "there"})
|
api.Notify(1, &model.MessageExternal{ID: 4, Message: "there"})
|
||||||
expectMessage(&model.Message{ID: 4, Message: "there"}, userOne...)
|
expectMessage(&model.MessageExternal{ID: 4, Message: "there"}, userOne...)
|
||||||
expectNoMessage(userTwo...)
|
expectNoMessage(userTwo...)
|
||||||
expectNoMessage(userThree...)
|
expectNoMessage(userThree...)
|
||||||
|
|
||||||
api.NotifyDeletedClient(1, "1-2")
|
api.NotifyDeletedClient(1, "1-2")
|
||||||
|
|
||||||
api.Notify(1, &model.Message{ID: 2, Message: "there"})
|
api.Notify(1, &model.MessageExternal{ID: 2, Message: "there"})
|
||||||
expectMessage(&model.Message{ID: 2, Message: "there"}, userOneIPhone, userOneOther)
|
expectMessage(&model.MessageExternal{ID: 2, Message: "there"}, userOneIPhone, userOneOther)
|
||||||
expectNoMessage(userOneBrowser, userOneAndroid)
|
expectNoMessage(userOneBrowser, userOneAndroid)
|
||||||
expectNoMessage(userThree...)
|
expectNoMessage(userThree...)
|
||||||
expectNoMessage(userTwo...)
|
expectNoMessage(userTwo...)
|
||||||
|
|
||||||
api.Notify(2, &model.Message{ID: 2, Message: "there"})
|
api.Notify(2, &model.MessageExternal{ID: 2, Message: "there"})
|
||||||
expectNoMessage(userOne...)
|
expectNoMessage(userOne...)
|
||||||
expectMessage(&model.Message{ID: 2, Message: "there"}, userTwo...)
|
expectMessage(&model.MessageExternal{ID: 2, Message: "there"}, userTwo...)
|
||||||
expectNoMessage(userThree...)
|
expectNoMessage(userThree...)
|
||||||
|
|
||||||
api.Notify(3, &model.Message{ID: 5, Message: "there"})
|
api.Notify(3, &model.MessageExternal{ID: 5, Message: "there"})
|
||||||
expectNoMessage(userOne...)
|
expectNoMessage(userOne...)
|
||||||
expectNoMessage(userTwo...)
|
expectNoMessage(userTwo...)
|
||||||
expectMessage(&model.Message{ID: 5, Message: "there"}, userThree...)
|
expectMessage(&model.MessageExternal{ID: 5, Message: "there"}, userThree...)
|
||||||
|
|
||||||
api.Close()
|
api.Close()
|
||||||
}
|
}
|
||||||
|
|
@ -297,27 +297,27 @@ func TestDeleteUser(t *testing.T) {
|
||||||
// the server may take some time to register the client
|
// the server may take some time to register the client
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
api.Notify(1, &model.Message{ID: 4, Message: "there"})
|
api.Notify(1, &model.MessageExternal{ID: 4, Message: "there"})
|
||||||
expectMessage(&model.Message{ID: 4, Message: "there"}, userOne...)
|
expectMessage(&model.MessageExternal{ID: 4, Message: "there"}, userOne...)
|
||||||
expectNoMessage(userTwo...)
|
expectNoMessage(userTwo...)
|
||||||
expectNoMessage(userThree...)
|
expectNoMessage(userThree...)
|
||||||
|
|
||||||
api.NotifyDeletedUser(1)
|
api.NotifyDeletedUser(1)
|
||||||
|
|
||||||
api.Notify(1, &model.Message{ID: 2, Message: "there"})
|
api.Notify(1, &model.MessageExternal{ID: 2, Message: "there"})
|
||||||
expectNoMessage(userOne...)
|
expectNoMessage(userOne...)
|
||||||
expectNoMessage(userThree...)
|
expectNoMessage(userThree...)
|
||||||
expectNoMessage(userTwo...)
|
expectNoMessage(userTwo...)
|
||||||
|
|
||||||
api.Notify(2, &model.Message{ID: 2, Message: "there"})
|
api.Notify(2, &model.MessageExternal{ID: 2, Message: "there"})
|
||||||
expectNoMessage(userOne...)
|
expectNoMessage(userOne...)
|
||||||
expectMessage(&model.Message{ID: 2, Message: "there"}, userTwo...)
|
expectMessage(&model.MessageExternal{ID: 2, Message: "there"}, userTwo...)
|
||||||
expectNoMessage(userThree...)
|
expectNoMessage(userThree...)
|
||||||
|
|
||||||
api.Notify(3, &model.Message{ID: 5, Message: "there"})
|
api.Notify(3, &model.MessageExternal{ID: 5, Message: "there"})
|
||||||
expectNoMessage(userOne...)
|
expectNoMessage(userOne...)
|
||||||
expectNoMessage(userTwo...)
|
expectNoMessage(userTwo...)
|
||||||
expectMessage(&model.Message{ID: 5, Message: "there"}, userThree...)
|
expectMessage(&model.MessageExternal{ID: 5, Message: "there"}, userThree...)
|
||||||
|
|
||||||
api.Close()
|
api.Close()
|
||||||
}
|
}
|
||||||
|
|
@ -362,15 +362,15 @@ func TestMultipleClients(t *testing.T) {
|
||||||
expectNoMessage(userTwo...)
|
expectNoMessage(userTwo...)
|
||||||
expectNoMessage(userThree...)
|
expectNoMessage(userThree...)
|
||||||
|
|
||||||
api.Notify(1, &model.Message{ID: 1, Message: "hello"})
|
api.Notify(1, &model.MessageExternal{ID: 1, Message: "hello"})
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
expectMessage(&model.Message{ID: 1, Message: "hello"}, userOne...)
|
expectMessage(&model.MessageExternal{ID: 1, Message: "hello"}, userOne...)
|
||||||
expectNoMessage(userTwo...)
|
expectNoMessage(userTwo...)
|
||||||
expectNoMessage(userThree...)
|
expectNoMessage(userThree...)
|
||||||
|
|
||||||
api.Notify(2, &model.Message{ID: 2, Message: "there"})
|
api.Notify(2, &model.MessageExternal{ID: 2, Message: "there"})
|
||||||
expectNoMessage(userOne...)
|
expectNoMessage(userOne...)
|
||||||
expectMessage(&model.Message{ID: 2, Message: "there"}, userTwo...)
|
expectMessage(&model.MessageExternal{ID: 2, Message: "there"}, userTwo...)
|
||||||
expectNoMessage(userThree...)
|
expectNoMessage(userThree...)
|
||||||
|
|
||||||
userOneIPhone.conn.Close()
|
userOneIPhone.conn.Close()
|
||||||
|
|
@ -379,21 +379,21 @@ func TestMultipleClients(t *testing.T) {
|
||||||
expectNoMessage(userTwo...)
|
expectNoMessage(userTwo...)
|
||||||
expectNoMessage(userThree...)
|
expectNoMessage(userThree...)
|
||||||
|
|
||||||
api.Notify(1, &model.Message{ID: 3, Message: "how"})
|
api.Notify(1, &model.MessageExternal{ID: 3, Message: "how"})
|
||||||
expectMessage(&model.Message{ID: 3, Message: "how"}, userOneAndroid, userOneBrowser)
|
expectMessage(&model.MessageExternal{ID: 3, Message: "how"}, userOneAndroid, userOneBrowser)
|
||||||
expectNoMessage(userOneIPhone)
|
expectNoMessage(userOneIPhone)
|
||||||
expectNoMessage(userTwo...)
|
expectNoMessage(userTwo...)
|
||||||
expectNoMessage(userThree...)
|
expectNoMessage(userThree...)
|
||||||
|
|
||||||
api.Notify(2, &model.Message{ID: 4, Message: "are"})
|
api.Notify(2, &model.MessageExternal{ID: 4, Message: "are"})
|
||||||
|
|
||||||
expectNoMessage(userOne...)
|
expectNoMessage(userOne...)
|
||||||
expectMessage(&model.Message{ID: 4, Message: "are"}, userTwo...)
|
expectMessage(&model.MessageExternal{ID: 4, Message: "are"}, userTwo...)
|
||||||
expectNoMessage(userThree...)
|
expectNoMessage(userThree...)
|
||||||
|
|
||||||
api.Close()
|
api.Close()
|
||||||
|
|
||||||
api.Notify(2, &model.Message{ID: 5, Message: "you"})
|
api.Notify(2, &model.MessageExternal{ID: 5, Message: "you"})
|
||||||
|
|
||||||
expectNoMessage(userOne...)
|
expectNoMessage(userOne...)
|
||||||
expectNoMessage(userTwo...)
|
expectNoMessage(userTwo...)
|
||||||
|
|
@ -481,7 +481,7 @@ func startReading(client *testingClient) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
actual := &model.Message{}
|
actual := &model.MessageExternal{}
|
||||||
json.NewDecoder(bytes.NewBuffer(payload)).Decode(actual)
|
json.NewDecoder(bytes.NewBuffer(payload)).Decode(actual)
|
||||||
client.readMessage <- *actual
|
client.readMessage <- *actual
|
||||||
}
|
}
|
||||||
|
|
@ -492,18 +492,18 @@ func createClient(t *testing.T, url string) *testingClient {
|
||||||
ws, _, err := websocket.DefaultDialer.Dial(url, nil)
|
ws, _, err := websocket.DefaultDialer.Dial(url, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
readMessages := make(chan model.Message)
|
readMessages := make(chan model.MessageExternal)
|
||||||
|
|
||||||
return &testingClient{conn: ws, readMessage: readMessages, t: t}
|
return &testingClient{conn: ws, readMessage: readMessages, t: t}
|
||||||
}
|
}
|
||||||
|
|
||||||
type testingClient struct {
|
type testingClient struct {
|
||||||
conn *websocket.Conn
|
conn *websocket.Conn
|
||||||
readMessage chan model.Message
|
readMessage chan model.MessageExternal
|
||||||
t *testing.T
|
t *testing.T
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *testingClient) expectMessage(expected *model.Message) {
|
func (c *testingClient) expectMessage(expected *model.MessageExternal) {
|
||||||
select {
|
select {
|
||||||
case <-time.After(50 * time.Millisecond):
|
case <-time.After(50 * time.Millisecond):
|
||||||
assert.Fail(c.t, "Expected message but none was send :(")
|
assert.Fail(c.t, "Expected message but none was send :(")
|
||||||
|
|
@ -512,7 +512,7 @@ func (c *testingClient) expectMessage(expected *model.Message) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func expectMessage(expected *model.Message, clients ...*testingClient) {
|
func expectMessage(expected *model.MessageExternal, clients ...*testingClient) {
|
||||||
for _, client := range clients {
|
for _, client := range clients {
|
||||||
client.expectMessage(expected)
|
client.expectMessage(expected)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
18
api/user.go
18
api/user.go
|
|
@ -54,7 +54,7 @@ func (a *UserAPI) GetUsers(ctx *gin.Context) {
|
||||||
|
|
||||||
var resp []*model.UserExternal
|
var resp []*model.UserExternal
|
||||||
for _, user := range users {
|
for _, user := range users {
|
||||||
resp = append(resp, toExternal(user))
|
resp = append(resp, toExternalUser(user))
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.JSON(200, resp)
|
ctx.JSON(200, resp)
|
||||||
|
|
@ -83,7 +83,7 @@ func (a *UserAPI) GetUsers(ctx *gin.Context) {
|
||||||
// $ref: "#/definitions/Error"
|
// $ref: "#/definitions/Error"
|
||||||
func (a *UserAPI) GetCurrentUser(ctx *gin.Context) {
|
func (a *UserAPI) GetCurrentUser(ctx *gin.Context) {
|
||||||
user := a.DB.GetUserByID(auth.GetUserID(ctx))
|
user := a.DB.GetUserByID(auth.GetUserID(ctx))
|
||||||
ctx.JSON(200, toExternal(user))
|
ctx.JSON(200, toExternalUser(user))
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateUser creates a user
|
// CreateUser creates a user
|
||||||
|
|
@ -122,10 +122,10 @@ func (a *UserAPI) GetCurrentUser(ctx *gin.Context) {
|
||||||
func (a *UserAPI) CreateUser(ctx *gin.Context) {
|
func (a *UserAPI) CreateUser(ctx *gin.Context) {
|
||||||
user := model.UserExternalWithPass{}
|
user := model.UserExternalWithPass{}
|
||||||
if err := ctx.Bind(&user); err == nil {
|
if err := ctx.Bind(&user); err == nil {
|
||||||
internal := a.toInternal(&user, []byte{})
|
internal := a.toInternalUser(&user, []byte{})
|
||||||
if a.DB.GetUserByName(internal.Name) == nil {
|
if a.DB.GetUserByName(internal.Name) == nil {
|
||||||
a.DB.CreateUser(internal)
|
a.DB.CreateUser(internal)
|
||||||
ctx.JSON(200, toExternal(internal))
|
ctx.JSON(200, toExternalUser(internal))
|
||||||
} else {
|
} else {
|
||||||
ctx.AbortWithError(400, errors.New("username already exists"))
|
ctx.AbortWithError(400, errors.New("username already exists"))
|
||||||
}
|
}
|
||||||
|
|
@ -171,7 +171,7 @@ func (a *UserAPI) CreateUser(ctx *gin.Context) {
|
||||||
func (a *UserAPI) GetUserByID(ctx *gin.Context) {
|
func (a *UserAPI) GetUserByID(ctx *gin.Context) {
|
||||||
withID(ctx, "id", func(id uint) {
|
withID(ctx, "id", func(id uint) {
|
||||||
if user := a.DB.GetUserByID(uint(id)); user != nil {
|
if user := a.DB.GetUserByID(uint(id)); user != nil {
|
||||||
ctx.JSON(200, toExternal(user))
|
ctx.JSON(200, toExternalUser(user))
|
||||||
} else {
|
} else {
|
||||||
ctx.AbortWithError(404, errors.New("user does not exist"))
|
ctx.AbortWithError(404, errors.New("user does not exist"))
|
||||||
}
|
}
|
||||||
|
|
@ -309,10 +309,10 @@ func (a *UserAPI) UpdateUserByID(ctx *gin.Context) {
|
||||||
var user *model.UserExternalWithPass
|
var user *model.UserExternalWithPass
|
||||||
if err := ctx.Bind(&user); err == nil {
|
if err := ctx.Bind(&user); err == nil {
|
||||||
if oldUser := a.DB.GetUserByID(id); oldUser != nil {
|
if oldUser := a.DB.GetUserByID(id); oldUser != nil {
|
||||||
internal := a.toInternal(user, oldUser.Pass)
|
internal := a.toInternalUser(user, oldUser.Pass)
|
||||||
internal.ID = id
|
internal.ID = id
|
||||||
a.DB.UpdateUser(internal)
|
a.DB.UpdateUser(internal)
|
||||||
ctx.JSON(200, toExternal(internal))
|
ctx.JSON(200, toExternalUser(internal))
|
||||||
} else {
|
} else {
|
||||||
ctx.AbortWithError(404, errors.New("user does not exist"))
|
ctx.AbortWithError(404, errors.New("user does not exist"))
|
||||||
}
|
}
|
||||||
|
|
@ -320,7 +320,7 @@ func (a *UserAPI) UpdateUserByID(ctx *gin.Context) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *UserAPI) toInternal(response *model.UserExternalWithPass, pw []byte) *model.User {
|
func (a *UserAPI) toInternalUser(response *model.UserExternalWithPass, pw []byte) *model.User {
|
||||||
user := &model.User{
|
user := &model.User{
|
||||||
Name: response.Name,
|
Name: response.Name,
|
||||||
Admin: response.Admin,
|
Admin: response.Admin,
|
||||||
|
|
@ -333,7 +333,7 @@ func (a *UserAPI) toInternal(response *model.UserExternalWithPass, pw []byte) *m
|
||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
|
|
||||||
func toExternal(internal *model.User) *model.UserExternal {
|
func toExternalUser(internal *model.User) *model.UserExternal {
|
||||||
return &model.UserExternal{
|
return &model.UserExternal{
|
||||||
Name: internal.Name,
|
Name: internal.Name,
|
||||||
Admin: internal.Admin,
|
Admin: internal.Admin,
|
||||||
|
|
|
||||||
|
|
@ -1490,9 +1490,9 @@
|
||||||
"x-go-package": "github.com/gotify/server/model"
|
"x-go-package": "github.com/gotify/server/model"
|
||||||
},
|
},
|
||||||
"Message": {
|
"Message": {
|
||||||
"description": "The Message holds information about a message which was sent by an Application.",
|
"description": "The MessageExternal holds information about a message which was sent by an Application.",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"title": "Message Model",
|
"title": "MessageExternal Model",
|
||||||
"required": [
|
"required": [
|
||||||
"id",
|
"id",
|
||||||
"appid",
|
"appid",
|
||||||
|
|
@ -1516,6 +1516,22 @@
|
||||||
"readOnly": true,
|
"readOnly": true,
|
||||||
"example": "2018-02-27T19:36:10.5045044+01:00"
|
"example": "2018-02-27T19:36:10.5045044+01:00"
|
||||||
},
|
},
|
||||||
|
"extras": {
|
||||||
|
"description": "The extra data sent along the message.\n\nThe extra fields are stored in a key-value scheme. Only accepted in CreateMessage requests with application/json content-type.\n\nThe keys should be in the following format: \u0026lt;top-namespace\u0026gt;::[\u0026lt;sub-namespace\u0026gt;::]\u0026lt;action\u0026gt;\n\nThese namespaces are reserved and might be used in the official clients: gotify android ios web server client. Do not use them for other purposes.",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"x-go-name": "Extras",
|
||||||
|
"example": {
|
||||||
|
"home::appliances::lighting::on": {
|
||||||
|
"brightness": 15
|
||||||
|
},
|
||||||
|
"home::appliances::thermostat::change_temperature": {
|
||||||
|
"temperature": 23
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"description": "The message id.",
|
"description": "The message id.",
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
|
|
@ -1544,6 +1560,7 @@
|
||||||
"example": "Backup"
|
"example": "Backup"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"x-go-name": "MessageExternal",
|
||||||
"x-go-package": "github.com/gotify/server/model"
|
"x-go-package": "github.com/gotify/server/model"
|
||||||
},
|
},
|
||||||
"PagedMessages": {
|
"PagedMessages": {
|
||||||
|
|
|
||||||
|
|
@ -35,5 +35,5 @@ type Application struct {
|
||||||
// required: true
|
// required: true
|
||||||
// example: https://example.com/image.jpeg
|
// example: https://example.com/image.jpeg
|
||||||
Image string `json:"image"`
|
Image string `json:"image"`
|
||||||
Messages []Message `json:"-"`
|
Messages []MessageExternal `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,32 @@
|
||||||
package model
|
package model
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
// Message Model
|
// Message holds information about a message
|
||||||
|
type Message struct {
|
||||||
|
ID uint `gorm:"AUTO_INCREMENT;primary_key;index"`
|
||||||
|
ApplicationID uint
|
||||||
|
Message string
|
||||||
|
Title string
|
||||||
|
Priority int
|
||||||
|
Extras []byte
|
||||||
|
Date time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// MessageExternal Model
|
||||||
//
|
//
|
||||||
// The Message holds information about a message which was sent by an Application.
|
// The MessageExternal holds information about a message which was sent by an Application.
|
||||||
//
|
//
|
||||||
// swagger:model Message
|
// swagger:model Message
|
||||||
type Message struct {
|
type MessageExternal struct {
|
||||||
// The message id.
|
// The message id.
|
||||||
//
|
//
|
||||||
// read only: true
|
// read only: true
|
||||||
// required: true
|
// required: true
|
||||||
// example: 25
|
// example: 25
|
||||||
ID uint `gorm:"AUTO_INCREMENT;primary_key;index" json:"id"`
|
ID uint `json:"id"`
|
||||||
// The application id that send this message.
|
// The application id that send this message.
|
||||||
//
|
//
|
||||||
// read only: true
|
// read only: true
|
||||||
|
|
@ -33,6 +46,16 @@ type Message struct {
|
||||||
//
|
//
|
||||||
// example: 2
|
// example: 2
|
||||||
Priority int `form:"priority" query:"priority" json:"priority"`
|
Priority int `form:"priority" query:"priority" json:"priority"`
|
||||||
|
// The extra data sent along the message.
|
||||||
|
//
|
||||||
|
// The extra fields are stored in a key-value scheme. Only accepted in CreateMessage requests with application/json content-type.
|
||||||
|
//
|
||||||
|
// The keys should be in the following format: <top-namespace>::[<sub-namespace>::]<action>
|
||||||
|
//
|
||||||
|
// These namespaces are reserved and might be used in the official clients: gotify android ios web server client. Do not use them for other purposes.
|
||||||
|
//
|
||||||
|
// example: {"home::appliances::thermostat::change_temperature":{"temperature":23},"home::appliances::lighting::on":{"brightness":15}}
|
||||||
|
Extras map[string]interface{} `form:"-" query:"-" json:"extras,omitempty"`
|
||||||
// The date the message was created.
|
// The date the message was created.
|
||||||
//
|
//
|
||||||
// read only: true
|
// read only: true
|
||||||
|
|
|
||||||
|
|
@ -50,5 +50,5 @@ type PagedMessages struct {
|
||||||
//
|
//
|
||||||
// read only: true
|
// read only: true
|
||||||
// required: true
|
// required: true
|
||||||
Messages []*Message `json:"messages"`
|
Messages []*MessageExternal `json:"messages"`
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,11 @@ describe('Messages', () => {
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
const m = (title: string, message: string) => ({title, message});
|
const m = (title: string, message: string, extras?: IMessageExtras) => ({
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
extras,
|
||||||
|
});
|
||||||
|
|
||||||
const windows1 = m('Login', 'User jmattheis logged in.');
|
const windows1 = m('Login', 'User jmattheis logged in.');
|
||||||
const windows2 = m('Shutdown', 'Windows will be shut down.');
|
const windows2 = m('Shutdown', 'Windows will be shut down.');
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,11 @@ interface IMessage {
|
||||||
priority: number;
|
priority: number;
|
||||||
date: string;
|
date: string;
|
||||||
image?: string;
|
image?: string;
|
||||||
|
extras?: IMessageExtras;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IMessageExtras {
|
||||||
|
[key: string]: any; // tslint:disable-line no-any
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IPagedMessages {
|
interface IPagedMessages {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue