diff --git a/scnserver/.idea/inspectionProfiles/Project_Default.xml b/scnserver/.idea/inspectionProfiles/Project_Default.xml
index 4696d94..c1d0146 100644
--- a/scnserver/.idea/inspectionProfiles/Project_Default.xml
+++ b/scnserver/.idea/inspectionProfiles/Project_Default.xml
@@ -7,5 +7,6 @@
+
\ No newline at end of file
diff --git a/scnserver/README.md b/scnserver/README.md
index 348f6bf..b2f0b38 100644
--- a/scnserver/README.md
+++ b/scnserver/README.md
@@ -23,6 +23,8 @@
-> how does it work with existing data?
-> do i care, there are only 2 active users... (are there?)
+ - convert existing user-ids on compat /send endpoint
+
- error logging as goroutine, gets all errors via channel,
(channel buffered - nonblocking send, second channel that gets a message when sender failed )
(then all errors end up in _second_ sqlite table)
@@ -57,10 +59,14 @@
- (?) desktop client for notifications
+- (?) add querylog (similar to requestlog/errorlog) - only for main-db
+
#### LATER
-- Pagination for ListChannels / ListSubscriptions / ListClients / ListChannelSubscriptions / ListUserSubscriptions
+ - weblogin, webapp, ...
-- cannot open sqlite in dbbrowsr (cannot parse schema?)
- -> https://github.com/sqlitebrowser/sqlitebrowser/issues/292 -> https://github.com/sqlitebrowser/sqlitebrowser/issues/29266
+ - Pagination for ListChannels / ListSubscriptions / ListClients / ListChannelSubscriptions / ListUserSubscriptions
+
+ - cannot open sqlite in dbbrowsr (cannot parse schema?)
+ -> https://github.com/sqlitebrowser/sqlitebrowser/issues/292 -> https://github.com/sqlitebrowser/sqlitebrowser/issues/29266
diff --git a/scnserver/api/handler/api.go b/scnserver/api/handler/api.go
index 72c5a1b..60397e0 100644
--- a/scnserver/api/handler/api.go
+++ b/scnserver/api/handler/api.go
@@ -151,7 +151,7 @@ func (h APIHandler) CreateUser(g *gin.Context) ginresp.HTTPResponse {
// @Router /api/users/{uid} [GET]
func (h APIHandler) GetUser(g *gin.Context) ginresp.HTTPResponse {
type uri struct {
- UserID models.UserID `uri:"uid"`
+ UserID models.UserID `uri:"uid" binding:"entityid"`
}
var u uri
@@ -200,7 +200,7 @@ func (h APIHandler) GetUser(g *gin.Context) ginresp.HTTPResponse {
// @Router /api/users/{uid} [PATCH]
func (h APIHandler) UpdateUser(g *gin.Context) ginresp.HTTPResponse {
type uri struct {
- UserID models.UserID `uri:"uid"`
+ UserID models.UserID `uri:"uid" binding:"entityid"`
}
type body struct {
Username *string `json:"username"`
@@ -306,7 +306,7 @@ func (h APIHandler) UpdateUser(g *gin.Context) ginresp.HTTPResponse {
// @Router /api/users/{uid}/clients [GET]
func (h APIHandler) ListClients(g *gin.Context) ginresp.HTTPResponse {
type uri struct {
- UserID models.UserID `uri:"uid"`
+ UserID models.UserID `uri:"uid" binding:"entityid"`
}
type response struct {
Clients []models.ClientJSON `json:"clients"`
@@ -351,8 +351,8 @@ func (h APIHandler) ListClients(g *gin.Context) ginresp.HTTPResponse {
// @Router /api/users/{uid}/clients/{cid} [GET]
func (h APIHandler) GetClient(g *gin.Context) ginresp.HTTPResponse {
type uri struct {
- UserID models.UserID `uri:"uid"`
- ClientID models.ClientID `uri:"cid"`
+ UserID models.UserID `uri:"uid" binding:"entityid"`
+ ClientID models.ClientID `uri:"cid" binding:"entityid"`
}
var u uri
@@ -395,7 +395,7 @@ func (h APIHandler) GetClient(g *gin.Context) ginresp.HTTPResponse {
// @Router /api/users/{uid}/clients [POST]
func (h APIHandler) AddClient(g *gin.Context) ginresp.HTTPResponse {
type uri struct {
- UserID models.UserID `uri:"uid"`
+ UserID models.UserID `uri:"uid" binding:"entityid"`
}
type body struct {
FCMToken string `json:"fcm_token" binding:"required"`
@@ -456,8 +456,8 @@ func (h APIHandler) AddClient(g *gin.Context) ginresp.HTTPResponse {
// @Router /api/users/{uid}/clients/{cid} [DELETE]
func (h APIHandler) DeleteClient(g *gin.Context) ginresp.HTTPResponse {
type uri struct {
- UserID models.UserID `uri:"uid"`
- ClientID models.ClientID `uri:"cid"`
+ UserID models.UserID `uri:"uid" binding:"entityid"`
+ ClientID models.ClientID `uri:"cid" binding:"entityid"`
}
var u uri
@@ -511,7 +511,7 @@ func (h APIHandler) DeleteClient(g *gin.Context) ginresp.HTTPResponse {
// @Router /api/users/{uid}/channels [GET]
func (h APIHandler) ListChannels(g *gin.Context) ginresp.HTTPResponse {
type uri struct {
- UserID models.UserID `uri:"uid"`
+ UserID models.UserID `uri:"uid" binding:"entityid"`
}
type query struct {
Selector *string `json:"selector" form:"selector" enums:"owned,subscribed_any,all_any,subscribed,all"`
@@ -603,8 +603,8 @@ func (h APIHandler) ListChannels(g *gin.Context) ginresp.HTTPResponse {
// @Router /api/users/{uid}/channels/{cid} [GET]
func (h APIHandler) GetChannel(g *gin.Context) ginresp.HTTPResponse {
type uri struct {
- UserID models.UserID `uri:"uid"`
- ChannelID models.ChannelID `uri:"cid"`
+ UserID models.UserID `uri:"uid" binding:"entityid"`
+ ChannelID models.ChannelID `uri:"cid" binding:"entityid"`
}
var u uri
@@ -647,7 +647,7 @@ func (h APIHandler) GetChannel(g *gin.Context) ginresp.HTTPResponse {
// @Router /api/users/{uid}/channels [POST]
func (h APIHandler) CreateChannel(g *gin.Context) ginresp.HTTPResponse {
type uri struct {
- UserID models.UserID `uri:"uid"`
+ UserID models.UserID `uri:"uid" binding:"entityid"`
}
type body struct {
Name string `json:"name"`
@@ -744,8 +744,8 @@ func (h APIHandler) CreateChannel(g *gin.Context) ginresp.HTTPResponse {
// @Router /api/users/{uid}/channels/{cid} [PATCH]
func (h APIHandler) UpdateChannel(g *gin.Context) ginresp.HTTPResponse {
type uri struct {
- UserID models.UserID `uri:"uid"`
- ChannelID models.ChannelID `uri:"cid"`
+ UserID models.UserID `uri:"uid" binding:"entityid"`
+ ChannelID models.ChannelID `uri:"cid" binding:"entityid"`
}
type body struct {
RefreshSubscribeKey *bool `json:"subscribe_key"`
@@ -869,8 +869,8 @@ func (h APIHandler) UpdateChannel(g *gin.Context) ginresp.HTTPResponse {
// @Router /api/users/{uid}/channels/{cid}/messages [GET]
func (h APIHandler) ListChannelMessages(g *gin.Context) ginresp.HTTPResponse {
type uri struct {
- ChannelUserID models.UserID `uri:"uid"`
- ChannelID models.ChannelID `uri:"cid"`
+ ChannelUserID models.UserID `uri:"uid" binding:"entityid"`
+ ChannelID models.ChannelID `uri:"cid" binding:"entityid"`
}
type query struct {
PageSize *int `json:"page_size" form:"page_size"`
@@ -972,7 +972,7 @@ func (h APIHandler) ListChannelMessages(g *gin.Context) ginresp.HTTPResponse {
// @Router /api/users/{uid}/subscriptions [GET]
func (h APIHandler) ListUserSubscriptions(g *gin.Context) ginresp.HTTPResponse {
type uri struct {
- UserID models.UserID `uri:"uid"`
+ UserID models.UserID `uri:"uid" binding:"entityid"`
}
type query struct {
Selector *string `json:"selector" form:"selector" enums:"owner_all,owner_confirmed,owner_unconfirmed,incoming_all,incoming_confirmed,incoming_unconfirmed"`
@@ -1069,8 +1069,8 @@ func (h APIHandler) ListUserSubscriptions(g *gin.Context) ginresp.HTTPResponse {
// @Router /api/users/{uid}/channels/{cid}/subscriptions [GET]
func (h APIHandler) ListChannelSubscriptions(g *gin.Context) ginresp.HTTPResponse {
type uri struct {
- UserID models.UserID `uri:"uid"`
- ChannelID models.ChannelID `uri:"cid"`
+ UserID models.UserID `uri:"uid" binding:"entityid"`
+ ChannelID models.ChannelID `uri:"cid" binding:"entityid"`
}
type response struct {
Subscriptions []models.SubscriptionJSON `json:"subscriptions"`
@@ -1123,8 +1123,8 @@ func (h APIHandler) ListChannelSubscriptions(g *gin.Context) ginresp.HTTPRespons
// @Router /api/users/{uid}/subscriptions/{sid} [GET]
func (h APIHandler) GetSubscription(g *gin.Context) ginresp.HTTPResponse {
type uri struct {
- UserID models.UserID `uri:"uid"`
- SubscriptionID models.SubscriptionID `uri:"sid"`
+ UserID models.UserID `uri:"uid" binding:"entityid"`
+ SubscriptionID models.SubscriptionID `uri:"sid" binding:"entityid"`
}
var u uri
@@ -1170,8 +1170,8 @@ func (h APIHandler) GetSubscription(g *gin.Context) ginresp.HTTPResponse {
// @Router /api/users/{uid}/subscriptions/{sid} [DELETE]
func (h APIHandler) CancelSubscription(g *gin.Context) ginresp.HTTPResponse {
type uri struct {
- UserID models.UserID `uri:"uid"`
- SubscriptionID models.SubscriptionID `uri:"sid"`
+ UserID models.UserID `uri:"uid" binding:"entityid"`
+ SubscriptionID models.SubscriptionID `uri:"sid" binding:"entityid"`
}
var u uri
@@ -1223,12 +1223,12 @@ func (h APIHandler) CancelSubscription(g *gin.Context) ginresp.HTTPResponse {
// @Router /api/users/{uid}/subscriptions [POST]
func (h APIHandler) CreateSubscription(g *gin.Context) ginresp.HTTPResponse {
type uri struct {
- UserID models.UserID `uri:"uid"`
+ UserID models.UserID `uri:"uid" binding:"entityid"`
}
type body struct {
- ChannelOwnerUserID *models.UserID `json:"channel_owner_user_id"`
+ ChannelOwnerUserID *models.UserID `json:"channel_owner_user_id" binding:"entityid"`
ChannelInternalName *string `json:"channel_internal_name"`
- ChannelID *models.ChannelID `json:"channel_id"`
+ ChannelID *models.ChannelID `json:"channel_id" binding:"entityid"`
}
type query struct {
ChanSubscribeKey *string `json:"chan_subscribe_key" form:"chan_subscribe_key"`
@@ -1312,8 +1312,8 @@ func (h APIHandler) CreateSubscription(g *gin.Context) ginresp.HTTPResponse {
// @Router /api/users/{uid}/subscriptions/{sid} [PATCH]
func (h APIHandler) UpdateSubscription(g *gin.Context) ginresp.HTTPResponse {
type uri struct {
- UserID models.UserID `uri:"uid"`
- SubscriptionID models.SubscriptionID `uri:"sid"`
+ UserID models.UserID `uri:"uid" binding:"entityid"`
+ SubscriptionID models.SubscriptionID `uri:"sid" binding:"entityid"`
}
type body struct {
Confirmed *bool `form:"confirmed"`
@@ -1454,7 +1454,7 @@ func (h APIHandler) ListMessages(g *gin.Context) ginresp.HTTPResponse {
// @ID api-messages-get
// @Tags API-v2
//
-// @Param mid path int true "SCNMessageID"
+// @Param mid path int true "MessageID"
//
// @Success 200 {object} models.MessageJSON
// @Failure 400 {object} ginresp.apiError "supplied values/parameters cannot be parsed / are invalid"
@@ -1465,7 +1465,7 @@ func (h APIHandler) ListMessages(g *gin.Context) ginresp.HTTPResponse {
// @Router /api/messages/{mid} [PATCH]
func (h APIHandler) GetMessage(g *gin.Context) ginresp.HTTPResponse {
type uri struct {
- MessageID models.SCNMessageID `uri:"mid"`
+ MessageID models.MessageID `uri:"mid" binding:"entityid"`
}
var u uri
@@ -1524,7 +1524,7 @@ func (h APIHandler) GetMessage(g *gin.Context) ginresp.HTTPResponse {
// @ID api-messages-delete
// @Tags API-v2
//
-// @Param mid path int true "SCNMessageID"
+// @Param mid path int true "MessageID"
//
// @Success 200 {object} models.MessageJSON
// @Failure 400 {object} ginresp.apiError "supplied values/parameters cannot be parsed / are invalid"
@@ -1535,7 +1535,7 @@ func (h APIHandler) GetMessage(g *gin.Context) ginresp.HTTPResponse {
// @Router /api/messages/{mid} [DELETE]
func (h APIHandler) DeleteMessage(g *gin.Context) ginresp.HTTPResponse {
type uri struct {
- MessageID models.SCNMessageID `uri:"mid"`
+ MessageID models.MessageID `uri:"mid" binding:"entityid"`
}
var u uri
@@ -1561,12 +1561,12 @@ func (h APIHandler) DeleteMessage(g *gin.Context) ginresp.HTTPResponse {
return ginresp.APIError(g, 401, apierr.USER_AUTH_FAILED, "You are not authorized for this action", nil)
}
- err = h.database.DeleteMessage(ctx, msg.SCNMessageID)
+ err = h.database.DeleteMessage(ctx, msg.MessageID)
if err != nil {
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to delete message", err)
}
- err = h.database.CancelPendingDeliveries(ctx, msg.SCNMessageID)
+ err = h.database.CancelPendingDeliveries(ctx, msg.MessageID)
if err != nil {
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to cancel deliveries", err)
}
diff --git a/scnserver/api/handler/compat.go b/scnserver/api/handler/compat.go
index 65c4119..3abfbe3 100644
--- a/scnserver/api/handler/compat.go
+++ b/scnserver/api/handler/compat.go
@@ -1,6 +1,8 @@
package handler
import (
+ "blackforestbytes.com/simplecloudnotifier/api/apierr"
+ hl "blackforestbytes.com/simplecloudnotifier/api/apihighlight"
"blackforestbytes.com/simplecloudnotifier/api/ginresp"
primarydb "blackforestbytes.com/simplecloudnotifier/db/impl/primary"
"blackforestbytes.com/simplecloudnotifier/logic"
@@ -24,6 +26,56 @@ func NewCompatHandler(app *logic.Application) CompatHandler {
}
}
+// SendMessageCompat swaggerdoc
+//
+// @Deprecated
+//
+// @Summary Send a new message (compatibility)
+// @Description All parameter can be set via query-parameter or form-data body. Only UserID, UserKey and Title are required
+// @Tags External
+//
+// @Param query_data query handler.SendMessageCompat.combined false " "
+// @Param form_data formData handler.SendMessageCompat.combined false " "
+//
+// @Success 200 {object} handler.sendMessageInternal.response
+// @Failure 400 {object} ginresp.apiError
+// @Failure 401 {object} ginresp.apiError
+// @Failure 403 {object} ginresp.apiError
+// @Failure 500 {object} ginresp.apiError
+//
+// @Router /send.php [POST]
+func (h MessageHandler) SendMessageCompat(g *gin.Context) ginresp.HTTPResponse {
+ type combined struct {
+ UserID *int64 `json:"user_id" form:"user_id"`
+ UserKey *string `json:"user_key" form:"user_key"`
+ Title *string `json:"title" form:"title"`
+ Content *string `json:"content" form:"content"`
+ Priority *int `json:"priority" form:"priority"`
+ UserMessageID *string `json:"msg_id" form:"msg_id"`
+ SendTimestamp *float64 `json:"timestamp" form:"timestamp"`
+ }
+
+ var f combined
+ var q combined
+ ctx, errResp := h.app.StartRequest(g, nil, &q, nil, &f)
+ if errResp != nil {
+ return *errResp
+ }
+ defer ctx.Cancel()
+
+ data := dataext.ObjectMerge(f, q)
+
+ newid, err := h.database.ConvertCompatID(ctx, langext.Coalesce(data.UserID, -1), "userid")
+ if err != nil {
+ return ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to query userid", err)
+ }
+ if newid == nil {
+ return ginresp.SendAPIError(g, 400, apierr.USER_NOT_FOUND, hl.USER_ID, "User not found (compat)", nil)
+ }
+
+ return h.sendMessageInternal(g, ctx, langext.Ptr(models.UserID(*newid)), data.UserKey, nil, nil, data.Title, data.Content, data.Priority, data.UserMessageID, data.SendTimestamp, nil)
+}
+
// Register swaggerdoc
//
// @Summary Register a new account
@@ -121,10 +173,15 @@ func (h CompatHandler) Register(g *gin.Context) ginresp.HTTPResponse {
return ginresp.CompatAPIError(0, "Failed to create client in db")
}
+ oldid, err := h.database.CreateCompatID(ctx, "userid", user.UserID.String())
+ if err != nil {
+ return ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to create userid", err)
+ }
+
return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, response{
Success: true,
Message: "New user registered",
- UserID: user.UserID.IntID(),
+ UserID: oldid,
UserKey: user.AdminKey,
QuotaUsed: user.QuotaUsedToday(),
QuotaMax: user.QuotaPerDay(),
@@ -184,7 +241,15 @@ func (h CompatHandler) Info(g *gin.Context) ginresp.HTTPResponse {
return ginresp.CompatAPIError(102, "Missing parameter [[user_key]]")
}
- user, err := h.database.GetUser(ctx, models.UserID(*data.UserID))
+ useridCompNew, err := h.database.ConvertCompatID(ctx, *data.UserID, "userid")
+ if err != nil {
+ return ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to query userid", err)
+ }
+ if useridCompNew == nil {
+ return ginresp.SendAPIError(g, 400, apierr.USER_NOT_FOUND, hl.USER_ID, "User not found (compat)", nil)
+ }
+
+ user, err := h.database.GetUser(ctx, models.UserID(*useridCompNew))
if err == sql.ErrNoRows {
return ginresp.CompatAPIError(201, "User not found")
}
@@ -206,7 +271,7 @@ func (h CompatHandler) Info(g *gin.Context) ginresp.HTTPResponse {
return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, response{
Success: true,
Message: "ok",
- UserID: user.UserID.IntID(),
+ UserID: *data.UserID,
UserKey: user.AdminKey,
QuotaUsed: user.QuotaUsedToday(),
QuotaMax: user.QuotaPerDay(),
@@ -269,7 +334,15 @@ func (h CompatHandler) Ack(g *gin.Context) ginresp.HTTPResponse {
return ginresp.CompatAPIError(103, "Missing parameter [[scn_msg_id]]")
}
- user, err := h.database.GetUser(ctx, models.UserID(*data.UserID))
+ useridCompNew, err := h.database.ConvertCompatID(ctx, *data.UserID, "userid")
+ if err != nil {
+ return ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to query userid", err)
+ }
+ if useridCompNew == nil {
+ return ginresp.SendAPIError(g, 400, apierr.USER_NOT_FOUND, hl.USER_ID, "User not found (compat)", nil)
+ }
+
+ user, err := h.database.GetUser(ctx, models.UserID(*useridCompNew))
if err == sql.ErrNoRows {
return ginresp.CompatAPIError(201, "User not found")
}
@@ -336,7 +409,15 @@ func (h CompatHandler) Requery(g *gin.Context) ginresp.HTTPResponse {
return ginresp.CompatAPIError(102, "Missing parameter [[user_key]]")
}
- user, err := h.database.GetUser(ctx, models.UserID(*data.UserID))
+ useridCompNew, err := h.database.ConvertCompatID(ctx, *data.UserID, "userid")
+ if err != nil {
+ return ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to query userid", err)
+ }
+ if useridCompNew == nil {
+ return ginresp.SendAPIError(g, 400, apierr.USER_NOT_FOUND, hl.USER_ID, "User not found (compat)", nil)
+ }
+
+ user, err := h.database.GetUser(ctx, models.UserID(*useridCompNew))
if err == sql.ErrNoRows {
return ginresp.CompatAPIError(201, "User not found")
}
@@ -409,7 +490,15 @@ func (h CompatHandler) Update(g *gin.Context) ginresp.HTTPResponse {
return ginresp.CompatAPIError(102, "Missing parameter [[user_key]]")
}
- user, err := h.database.GetUser(ctx, models.UserID(*data.UserID))
+ useridCompNew, err := h.database.ConvertCompatID(ctx, *data.UserID, "userid")
+ if err != nil {
+ return ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to query userid", err)
+ }
+ if useridCompNew == nil {
+ return ginresp.SendAPIError(g, 400, apierr.USER_NOT_FOUND, hl.USER_ID, "User not found (compat)", nil)
+ }
+
+ user, err := h.database.GetUser(ctx, models.UserID(*useridCompNew))
if err == sql.ErrNoRows {
return ginresp.CompatAPIError(201, "User not found")
}
@@ -461,7 +550,7 @@ func (h CompatHandler) Update(g *gin.Context) ginresp.HTTPResponse {
return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, response{
Success: true,
Message: "user updated",
- UserID: user.UserID.IntID(),
+ UserID: *data.UserID,
UserKey: user.AdminKey,
QuotaUsed: user.QuotaUsedToday(),
QuotaMax: user.QuotaPerDay(),
@@ -521,7 +610,15 @@ func (h CompatHandler) Expand(g *gin.Context) ginresp.HTTPResponse {
return ginresp.CompatAPIError(103, "Missing parameter [[scn_msg_id]]")
}
- user, err := h.database.GetUser(ctx, models.UserID(*data.UserID))
+ useridCompNew, err := h.database.ConvertCompatID(ctx, *data.UserID, "userid")
+ if err != nil {
+ return ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to query userid", err)
+ }
+ if useridCompNew == nil {
+ return ginresp.SendAPIError(g, 400, apierr.USER_NOT_FOUND, hl.USER_ID, "User not found (compat)", nil)
+ }
+
+ user, err := h.database.GetUser(ctx, models.UserID(*useridCompNew))
if err == sql.ErrNoRows {
return ginresp.CompatAPIError(201, "User not found")
}
@@ -533,7 +630,15 @@ func (h CompatHandler) Expand(g *gin.Context) ginresp.HTTPResponse {
return ginresp.CompatAPIError(204, "Authentification failed")
}
- msg, err := h.database.GetMessage(ctx, models.SCNMessageID(*data.MessageID), false)
+ messageCompNew, err := h.database.ConvertCompatID(ctx, *data.MessageID, "messageid")
+ if err != nil {
+ return ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to query messagid", err)
+ }
+ if messageCompNew == nil {
+ return ginresp.CompatAPIError(301, "Message not found")
+ }
+
+ msg, err := h.database.GetMessage(ctx, models.MessageID(*messageCompNew), false)
if err == sql.ErrNoRows {
return ginresp.CompatAPIError(301, "Message not found")
}
@@ -551,7 +656,7 @@ func (h CompatHandler) Expand(g *gin.Context) ginresp.HTTPResponse {
Priority: msg.Priority,
Timestamp: msg.Timestamp().Unix(),
UserMessageID: msg.UserMessageID,
- SCNMessageID: msg.SCNMessageID.IntID(),
+ SCNMessageID: *data.MessageID,
},
}))
}
@@ -617,7 +722,15 @@ func (h CompatHandler) Upgrade(g *gin.Context) ginresp.HTTPResponse {
return ginresp.CompatAPIError(104, "Missing parameter [[pro_token]]")
}
- user, err := h.database.GetUser(ctx, models.UserID(*data.UserID))
+ useridCompNew, err := h.database.ConvertCompatID(ctx, *data.UserID, "userid")
+ if err != nil {
+ return ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to query userid", err)
+ }
+ if useridCompNew == nil {
+ return ginresp.SendAPIError(g, 400, apierr.USER_NOT_FOUND, hl.USER_ID, "User not found (compat)", nil)
+ }
+
+ user, err := h.database.GetUser(ctx, models.UserID(*useridCompNew))
if err == sql.ErrNoRows {
return ginresp.CompatAPIError(201, "User not found")
}
@@ -662,7 +775,7 @@ func (h CompatHandler) Upgrade(g *gin.Context) ginresp.HTTPResponse {
return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, response{
Success: true,
Message: "user updated",
- UserID: user.UserID.IntID(),
+ UserID: *data.UserID,
QuotaUsed: user.QuotaUsedToday(),
QuotaMax: user.QuotaPerDay(),
IsPro: user.IsPro,
diff --git a/scnserver/api/handler/message.go b/scnserver/api/handler/message.go
index a712ab8..b20b8a6 100644
--- a/scnserver/api/handler/message.go
+++ b/scnserver/api/handler/message.go
@@ -31,48 +31,6 @@ func NewMessageHandler(app *logic.Application) MessageHandler {
}
}
-// SendMessageCompat swaggerdoc
-//
-// @Deprecated
-//
-// @Summary Send a new message (compatibility)
-// @Description All parameter can be set via query-parameter or form-data body. Only UserID, UserKey and Title are required
-// @Tags External
-//
-// @Param query_data query handler.SendMessageCompat.combined false " "
-// @Param form_data formData handler.SendMessageCompat.combined false " "
-//
-// @Success 200 {object} handler.sendMessageInternal.response
-// @Failure 400 {object} ginresp.apiError
-// @Failure 401 {object} ginresp.apiError
-// @Failure 403 {object} ginresp.apiError
-// @Failure 500 {object} ginresp.apiError
-//
-// @Router /send.php [POST]
-func (h MessageHandler) SendMessageCompat(g *gin.Context) ginresp.HTTPResponse {
- type combined struct {
- UserID *models.UserID `json:"user_id" form:"user_id"`
- UserKey *string `json:"user_key" form:"user_key"`
- Title *string `json:"title" form:"title"`
- Content *string `json:"content" form:"content"`
- Priority *int `json:"priority" form:"priority"`
- UserMessageID *string `json:"msg_id" form:"msg_id"`
- SendTimestamp *float64 `json:"timestamp" form:"timestamp"`
- }
-
- var f combined
- var q combined
- ctx, errResp := h.app.StartRequest(g, nil, &q, nil, &f)
- if errResp != nil {
- return *errResp
- }
- defer ctx.Cancel()
-
- data := dataext.ObjectMerge(f, q)
-
- return h.sendMessageInternal(g, ctx, data.UserID, data.UserKey, nil, nil, data.Title, data.Content, data.Priority, data.UserMessageID, data.SendTimestamp, nil)
-}
-
// SendMessage swaggerdoc
//
// @Summary Send a new message
@@ -123,16 +81,16 @@ func (h MessageHandler) SendMessage(g *gin.Context) ginresp.HTTPResponse {
func (h MessageHandler) sendMessageInternal(g *gin.Context, ctx *logic.AppContext, UserID *models.UserID, UserKey *string, Channel *string, ChanKey *string, Title *string, Content *string, Priority *int, UserMessageID *string, SendTimestamp *float64, SenderName *string) ginresp.HTTPResponse {
type response struct {
- Success bool `json:"success"`
- ErrorID apierr.APIError `json:"error"`
- ErrorHighlight int `json:"errhighlight"`
- Message string `json:"message"`
- SuppressSend bool `json:"suppress_send"`
- MessageCount int `json:"messagecount"`
- Quota int `json:"quota"`
- IsPro bool `json:"is_pro"`
- QuotaMax int `json:"quota_max"`
- SCNMessageID models.SCNMessageID `json:"scn_msg_id"`
+ Success bool `json:"success"`
+ ErrorID apierr.APIError `json:"error"`
+ ErrorHighlight int `json:"errhighlight"`
+ Message string `json:"message"`
+ SuppressSend bool `json:"suppress_send"`
+ MessageCount int `json:"messagecount"`
+ Quota int `json:"quota"`
+ IsPro bool `json:"is_pro"`
+ QuotaMax int `json:"quota_max"`
+ SCNMessageID models.MessageID `json:"scn_msg_id"`
}
if Title != nil {
@@ -212,7 +170,7 @@ func (h MessageHandler) sendMessageInternal(g *gin.Context, ctx *logic.AppContex
Quota: user.QuotaUsedToday(),
IsPro: user.IsPro,
QuotaMax: user.QuotaPerDay(),
- SCNMessageID: msg.SCNMessageID,
+ SCNMessageID: msg.MessageID,
}))
}
}
@@ -317,6 +275,6 @@ func (h MessageHandler) sendMessageInternal(g *gin.Context, ctx *logic.AppContex
Quota: user.QuotaUsedToday() + 1,
IsPro: user.IsPro,
QuotaMax: user.QuotaPerDay(),
- SCNMessageID: msg.SCNMessageID,
+ SCNMessageID: msg.MessageID,
}))
}
diff --git a/scnserver/api/router.go b/scnserver/api/router.go
index 3941e11..1676a12 100644
--- a/scnserver/api/router.go
+++ b/scnserver/api/router.go
@@ -5,8 +5,12 @@ import (
"blackforestbytes.com/simplecloudnotifier/api/ginresp"
"blackforestbytes.com/simplecloudnotifier/api/handler"
"blackforestbytes.com/simplecloudnotifier/logic"
+ "blackforestbytes.com/simplecloudnotifier/models"
"blackforestbytes.com/simplecloudnotifier/swagger"
+ "errors"
"github.com/gin-gonic/gin"
+ "github.com/gin-gonic/gin/binding"
+ "github.com/go-playground/validator/v10"
)
type Router struct {
@@ -44,7 +48,16 @@ func NewRouter(app *logic.Application) *Router {
// @tag.name Common
//
// @BasePath /
-func (r *Router) Init(e *gin.Engine) {
+func (r *Router) Init(e *gin.Engine) error {
+
+ if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
+ err := v.RegisterValidation("entityid", models.ValidateEntityID, true)
+ if err != nil {
+ return err
+ }
+ } else {
+ return errors.New("failed to add validators - wrong engine")
+ }
// ================ General ================
@@ -94,7 +107,7 @@ func (r *Router) Init(e *gin.Engine) {
// ================ Compat (v1) ================
- compat := e.Group("/api/")
+ compat := e.Group("/api")
{
compat.GET("/register.php", r.Wrap(r.compatHandler.Register))
compat.GET("/info.php", r.Wrap(r.compatHandler.Info))
@@ -150,6 +163,9 @@ func (r *Router) Init(e *gin.Engine) {
e.NoRoute(r.Wrap(r.commonHandler.NoRoute))
}
+ // ================
+
+ return nil
}
func (r *Router) Wrap(fn ginresp.WHandlerFunc) gin.HandlerFunc {
diff --git a/scnserver/cmd/scnserver/main.go b/scnserver/cmd/scnserver/main.go
index 08da34d..204f658 100644
--- a/scnserver/cmd/scnserver/main.go
+++ b/scnserver/cmd/scnserver/main.go
@@ -63,7 +63,11 @@ func main() {
app.Init(conf, ginengine, nc, apc, []logic.Job{jobRetry, jobReqCollector})
- router.Init(ginengine)
+ err = router.Init(ginengine)
+ if err != nil {
+ log.Fatal().Err(err).Msg("failed to init router")
+ return
+ }
app.Run()
}
diff --git a/scnserver/config.go b/scnserver/config.go
index ea01048..c8e9c37 100644
--- a/scnserver/config.go
+++ b/scnserver/config.go
@@ -54,6 +54,7 @@ type DBConfig struct {
ConnMaxIdleTime time.Duration `env:"CONNEXTIONMAXIDLETIME"`
CheckForeignKeys bool `env:"CHECKFOREIGNKEYS"`
SingleConn bool `env:"SINGLECONNECTION"`
+ BusyTimeout time.Duration `env:"BUSYTIMEOUT"`
}
var Conf Config
@@ -76,6 +77,7 @@ var configLocHost = func() Config {
MaxIdleConns: 5,
ConnMaxLifetime: 60 * time.Minute,
ConnMaxIdleTime: 60 * time.Minute,
+ BusyTimeout: 100 * time.Millisecond,
},
DBRequests: DBConfig{
File: ".run-data/loc_requests.sqlite3",
@@ -87,6 +89,7 @@ var configLocHost = func() Config {
MaxIdleConns: 5,
ConnMaxLifetime: 60 * time.Minute,
ConnMaxIdleTime: 60 * time.Minute,
+ BusyTimeout: 500 * time.Millisecond,
},
DBLogs: DBConfig{
File: ".run-data/loc_logs.sqlite3",
@@ -98,6 +101,7 @@ var configLocHost = func() Config {
MaxIdleConns: 5,
ConnMaxLifetime: 60 * time.Minute,
ConnMaxIdleTime: 60 * time.Minute,
+ BusyTimeout: 500 * time.Millisecond,
},
RequestTimeout: 16 * time.Second,
RequestMaxRetry: 8,
@@ -142,6 +146,7 @@ var configLocDocker = func() Config {
MaxIdleConns: 5,
ConnMaxLifetime: 60 * time.Minute,
ConnMaxIdleTime: 60 * time.Minute,
+ BusyTimeout: 100 * time.Millisecond,
},
DBRequests: DBConfig{
File: "/data/docker_scn_requests.sqlite3",
@@ -153,6 +158,7 @@ var configLocDocker = func() Config {
MaxIdleConns: 5,
ConnMaxLifetime: 60 * time.Minute,
ConnMaxIdleTime: 60 * time.Minute,
+ BusyTimeout: 500 * time.Millisecond,
},
DBLogs: DBConfig{
File: "/data/docker_scn_logs.sqlite3",
@@ -164,6 +170,7 @@ var configLocDocker = func() Config {
MaxIdleConns: 5,
ConnMaxLifetime: 60 * time.Minute,
ConnMaxIdleTime: 60 * time.Minute,
+ BusyTimeout: 500 * time.Millisecond,
},
RequestTimeout: 16 * time.Second,
RequestMaxRetry: 8,
@@ -207,6 +214,7 @@ var configDev = func() Config {
MaxIdleConns: 5,
ConnMaxLifetime: 60 * time.Minute,
ConnMaxIdleTime: 60 * time.Minute,
+ BusyTimeout: 100 * time.Millisecond,
},
DBRequests: DBConfig{
File: "/data/scn_requests.sqlite3",
@@ -218,6 +226,7 @@ var configDev = func() Config {
MaxIdleConns: 5,
ConnMaxLifetime: 60 * time.Minute,
ConnMaxIdleTime: 60 * time.Minute,
+ BusyTimeout: 500 * time.Millisecond,
},
DBLogs: DBConfig{
File: "/data/scn_logs.sqlite3",
@@ -229,6 +238,7 @@ var configDev = func() Config {
MaxIdleConns: 5,
ConnMaxLifetime: 60 * time.Minute,
ConnMaxIdleTime: 60 * time.Minute,
+ BusyTimeout: 500 * time.Millisecond,
},
RequestTimeout: 16 * time.Second,
RequestMaxRetry: 8,
@@ -272,6 +282,7 @@ var configStag = func() Config {
MaxIdleConns: 5,
ConnMaxLifetime: 60 * time.Minute,
ConnMaxIdleTime: 60 * time.Minute,
+ BusyTimeout: 100 * time.Millisecond,
},
DBRequests: DBConfig{
File: "/data/scn_requests.sqlite3",
@@ -283,6 +294,7 @@ var configStag = func() Config {
MaxIdleConns: 5,
ConnMaxLifetime: 60 * time.Minute,
ConnMaxIdleTime: 60 * time.Minute,
+ BusyTimeout: 500 * time.Millisecond,
},
DBLogs: DBConfig{
File: "/data/scn_logs.sqlite3",
@@ -294,6 +306,7 @@ var configStag = func() Config {
MaxIdleConns: 5,
ConnMaxLifetime: 60 * time.Minute,
ConnMaxIdleTime: 60 * time.Minute,
+ BusyTimeout: 500 * time.Millisecond,
},
RequestTimeout: 16 * time.Second,
RequestMaxRetry: 8,
@@ -337,6 +350,7 @@ var configProd = func() Config {
MaxIdleConns: 5,
ConnMaxLifetime: 60 * time.Minute,
ConnMaxIdleTime: 60 * time.Minute,
+ BusyTimeout: 100 * time.Millisecond,
},
DBRequests: DBConfig{
File: "/data/scn_requests.sqlite3",
@@ -348,6 +362,7 @@ var configProd = func() Config {
MaxIdleConns: 5,
ConnMaxLifetime: 60 * time.Minute,
ConnMaxIdleTime: 60 * time.Minute,
+ BusyTimeout: 500 * time.Millisecond,
},
DBLogs: DBConfig{
File: "/data/scn_logs.sqlite3",
@@ -359,6 +374,7 @@ var configProd = func() Config {
MaxIdleConns: 5,
ConnMaxLifetime: 60 * time.Minute,
ConnMaxIdleTime: 60 * time.Minute,
+ BusyTimeout: 500 * time.Millisecond,
},
RequestTimeout: 16 * time.Second,
RequestMaxRetry: 8,
diff --git a/scnserver/db/cursortoken/token.go b/scnserver/db/cursortoken/token.go
index 113654a..7db0fd0 100644
--- a/scnserver/db/cursortoken/token.go
+++ b/scnserver/db/cursortoken/token.go
@@ -19,14 +19,14 @@ const (
type CursorToken struct {
Mode Mode
Timestamp int64
- Id int64
+ Id string
Direction string
FilterHash string
}
type cursorTokenSerialize struct {
Timestamp *int64 `json:"ts,omitempty"`
- Id *int64 `json:"id,omitempty"`
+ Id *string `json:"id,omitempty"`
Direction *string `json:"dir,omitempty"`
FilterHash *string `json:"f,omitempty"`
}
@@ -35,7 +35,7 @@ func Start() CursorToken {
return CursorToken{
Mode: CTMStart,
Timestamp: 0,
- Id: 0,
+ Id: "",
Direction: "",
FilterHash: "",
}
@@ -45,13 +45,13 @@ func End() CursorToken {
return CursorToken{
Mode: CTMEnd,
Timestamp: 0,
- Id: 0,
+ Id: "",
Direction: "",
FilterHash: "",
}
}
-func Normal(ts time.Time, id int64, dir string, filter string) CursorToken {
+func Normal(ts time.Time, id string, dir string, filter string) CursorToken {
return CursorToken{
Mode: CTMNormal,
Timestamp: ts.UnixMilli(),
@@ -76,7 +76,7 @@ func (c *CursorToken) Token() string {
sertok := cursorTokenSerialize{}
- if c.Id != 0 {
+ if c.Id != "" {
sertok.Id = &c.Id
}
diff --git a/scnserver/db/impl/logs/database.go b/scnserver/db/impl/logs/database.go
index a922f49..7941c46 100644
--- a/scnserver/db/impl/logs/database.go
+++ b/scnserver/db/impl/logs/database.go
@@ -24,7 +24,7 @@ type Database struct {
func NewLogsDatabase(cfg server.Config) (*Database, error) {
conf := cfg.DBLogs
- url := fmt.Sprintf("file:%s?_journal=%s&_timeout=%d&_fk=%s", conf.File, conf.Journal, conf.Timeout.Milliseconds(), langext.FormatBool(conf.CheckForeignKeys, "true", "false"))
+ url := fmt.Sprintf("file:%s?_journal=%s&_timeout=%d&_fk=%s&_busy_timeout=%d", conf.File, conf.Journal, conf.Timeout.Milliseconds(), langext.FormatBool(conf.CheckForeignKeys, "true", "false"), conf.BusyTimeout.Milliseconds())
xdb, err := sqlx.Open("sqlite3", url)
if err != nil {
diff --git a/scnserver/db/impl/logs/schema/schema_1.ddl b/scnserver/db/impl/logs/schema/schema_1.ddl
index d0afab7..7debdc4 100644
--- a/scnserver/db/impl/logs/schema/schema_1.ddl
+++ b/scnserver/db/impl/logs/schema/schema_1.ddl
@@ -1,9 +1,10 @@
CREATE TABLE `logs`
(
- log_id INTEGER PRIMARY KEY,
- timestamp_created INTEGER NOT NULL
+ log_id TEXT NOT NULL,
+ timestamp_created INTEGER NOT NULL,
+ PRIMARY KEY (log_id)
) STRICT;
diff --git a/scnserver/db/impl/primary/channels.go b/scnserver/db/impl/primary/channels.go
index 49a3a65..395d648 100644
--- a/scnserver/db/impl/primary/channels.go
+++ b/scnserver/db/impl/primary/channels.go
@@ -89,7 +89,10 @@ func (db *Database) CreateChannel(ctx TxContext, userid models.UserID, dispName
now := time.Now().UTC()
- res, err := tx.Exec(ctx, "INSERT INTO channels (owner_user_id, display_name, internal_name, subscribe_key, send_key, timestamp_created) VALUES (:ouid, :dnam, :inam, :subkey, :sendkey, :ts)", sq.PP{
+ channelid := models.NewChannelID()
+
+ _, err = tx.Exec(ctx, "INSERT INTO channels (channel_id, owner_user_id, display_name, internal_name, subscribe_key, send_key, timestamp_created) VALUES (:cid, :ouid, :dnam, :inam, :subkey, :sendkey, :ts)", sq.PP{
+ "cid": channelid,
"ouid": userid,
"dnam": dispName,
"inam": intName,
@@ -101,13 +104,8 @@ func (db *Database) CreateChannel(ctx TxContext, userid models.UserID, dispName
return models.Channel{}, err
}
- liid, err := res.LastInsertId()
- if err != nil {
- return models.Channel{}, err
- }
-
return models.Channel{
- ChannelID: models.ChannelID(liid),
+ ChannelID: channelid,
OwnerUserID: userid,
DisplayName: dispName,
InternalName: intName,
@@ -125,7 +123,9 @@ func (db *Database) ListChannelsByOwner(ctx TxContext, userid models.UserID, sub
return nil, err
}
- rows, err := tx.Query(ctx, "SELECT channels.*, sub.* FROM channels LEFT JOIN subscriptions AS sub ON channels.channel_id = sub.channel_id AND sub.subscriber_user_id = :subuid WHERE owner_user_id = :ouid", sq.PP{
+ order := " ORDER BY channels.timestamp_created ASC, channels.channel_id ASC "
+
+ rows, err := tx.Query(ctx, "SELECT channels.*, sub.* FROM channels LEFT JOIN subscriptions AS sub ON channels.channel_id = sub.channel_id AND sub.subscriber_user_id = :subuid WHERE owner_user_id = :ouid"+order, sq.PP{
"ouid": userid,
"subuid": subUserID,
})
@@ -154,7 +154,9 @@ func (db *Database) ListChannelsBySubscriber(ctx TxContext, userid models.UserID
confCond = " AND sub.confirmed = 0"
}
- rows, err := tx.Query(ctx, "SELECT channels.*, sub.* FROM channels LEFT JOIN subscriptions AS sub on channels.channel_id = sub.channel_id AND sub.subscriber_user_id = :subuid WHERE sub.subscription_id IS NOT NULL "+confCond, sq.PP{
+ order := " ORDER BY channels.timestamp_created ASC, channels.channel_id ASC "
+
+ rows, err := tx.Query(ctx, "SELECT channels.*, sub.* FROM channels LEFT JOIN subscriptions AS sub on channels.channel_id = sub.channel_id AND sub.subscriber_user_id = :subuid WHERE sub.subscription_id IS NOT NULL "+confCond+order, sq.PP{
"subuid": userid,
})
if err != nil {
@@ -182,7 +184,9 @@ func (db *Database) ListChannelsByAccess(ctx TxContext, userid models.UserID, co
confCond = "OR (sub.subscription_id IS NOT NULL AND sub.confirmed = 0)"
}
- rows, err := tx.Query(ctx, "SELECT channels.*, sub.* FROM channels LEFT JOIN subscriptions AS sub on channels.channel_id = sub.channel_id AND sub.subscriber_user_id = :subuid WHERE owner_user_id = :ouid "+confCond, sq.PP{
+ order := " ORDER BY channels.timestamp_created ASC, channels.channel_id ASC "
+
+ rows, err := tx.Query(ctx, "SELECT channels.*, sub.* FROM channels LEFT JOIN subscriptions AS sub on channels.channel_id = sub.channel_id AND sub.subscriber_user_id = :subuid WHERE owner_user_id = :ouid "+confCond+order, sq.PP{
"ouid": userid,
"subuid": userid,
})
diff --git a/scnserver/db/impl/primary/clients.go b/scnserver/db/impl/primary/clients.go
index d216a7b..71bc9b8 100644
--- a/scnserver/db/impl/primary/clients.go
+++ b/scnserver/db/impl/primary/clients.go
@@ -15,7 +15,10 @@ func (db *Database) CreateClient(ctx TxContext, userid models.UserID, ctype mode
now := time.Now().UTC()
- res, err := tx.Exec(ctx, "INSERT INTO clients (user_id, type, fcm_token, timestamp_created, agent_model, agent_version) VALUES (:uid, :typ, :fcm, :ts, :am, :av)", sq.PP{
+ clientid := models.NewClientID()
+
+ _, err = tx.Exec(ctx, "INSERT INTO clients (client_id, user_id, type, fcm_token, timestamp_created, agent_model, agent_version) VALUES (:cid, :uid, :typ, :fcm, :ts, :am, :av)", sq.PP{
+ "cid": clientid,
"uid": userid,
"typ": string(ctype),
"fcm": fcmToken,
@@ -27,13 +30,8 @@ func (db *Database) CreateClient(ctx TxContext, userid models.UserID, ctype mode
return models.Client{}, err
}
- liid, err := res.LastInsertId()
- if err != nil {
- return models.Client{}, err
- }
-
return models.Client{
- ClientID: models.ClientID(liid),
+ ClientID: clientid,
UserID: userid,
Type: ctype,
FCMToken: langext.Ptr(fcmToken),
@@ -63,7 +61,7 @@ func (db *Database) ListClients(ctx TxContext, userid models.UserID) ([]models.C
return nil, err
}
- rows, err := tx.Query(ctx, "SELECT * FROM clients WHERE user_id = :uid", sq.PP{"uid": userid})
+ rows, err := tx.Query(ctx, "SELECT * FROM clients WHERE user_id = :uid ORDER BY clients.timestamp_created DESC, clients.client_id ASC", sq.PP{"uid": userid})
if err != nil {
return nil, err
}
diff --git a/scnserver/db/impl/primary/compat.go b/scnserver/db/impl/primary/compat.go
new file mode 100644
index 0000000..c169312
--- /dev/null
+++ b/scnserver/db/impl/primary/compat.go
@@ -0,0 +1,100 @@
+package primary
+
+import (
+ "database/sql"
+ "errors"
+ "gogs.mikescher.com/BlackForestBytes/goext/sq"
+)
+
+func (db *Database) CreateCompatID(ctx TxContext, idtype string, newid string) (int64, error) {
+ tx, err := ctx.GetOrCreateTransaction(db)
+ if err != nil {
+ return 0, err
+ }
+
+ rows, err := tx.Query(ctx, "SELECT COALESCE(MAX(old), 0) FROM compat_ids", sq.PP{})
+ if err != nil {
+ return 0, err
+ }
+
+ if !rows.Next() {
+ return 0, errors.New("failed to query MAX(old)")
+ }
+
+ var oldid int64
+ err = rows.Scan(&oldid)
+ if err != nil {
+ return 0, err
+ }
+
+ oldid++
+
+ _, err = tx.Exec(ctx, "INSERT INTO compat_ids (old, new, type) VALUES (:old, :new, :typ)", sq.PP{
+ "old": oldid,
+ "new": newid,
+ "typ": idtype,
+ })
+ if err != nil {
+ return 0, err
+ }
+
+ return oldid, nil
+}
+
+func (db *Database) ConvertCompatID(ctx TxContext, oldid int64, idtype string) (*string, error) {
+ tx, err := ctx.GetOrCreateTransaction(db)
+ if err != nil {
+ return nil, err
+ }
+
+ rows, err := tx.Query(ctx, "SELECT new FROM compat_ids WHERE old = :old AND type = :typ", sq.PP{
+ "old": oldid,
+ "typ": idtype,
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ if !rows.Next() {
+ return nil, nil
+ }
+
+ var newid string
+ err = rows.Scan(&newid)
+ if err == sql.ErrNoRows {
+ return nil, nil
+ }
+ if err != nil {
+ return nil, err
+ }
+
+ return &newid, nil
+}
+
+func (db *Database) ConvertToCompatID(ctx TxContext, newid string) (*int64, *string, error) {
+ tx, err := ctx.GetOrCreateTransaction(db)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ rows, err := tx.Query(ctx, "SELECT old, type FROM compat_ids WHERE new = :new", sq.PP{"new": newid})
+ if err != nil {
+ return nil, nil, err
+ }
+
+ if !rows.Next() {
+ return nil, nil, nil
+ }
+
+ var oldid int64
+ var idtype string
+ err = rows.Scan(&oldid, &idtype)
+ if err == sql.ErrNoRows {
+ return nil, nil, nil
+ }
+ if err != nil {
+ return nil, nil, err
+ }
+
+ return &oldid, &idtype, nil
+}
diff --git a/scnserver/db/impl/primary/database.go b/scnserver/db/impl/primary/database.go
index af2efd1..8b1908f 100644
--- a/scnserver/db/impl/primary/database.go
+++ b/scnserver/db/impl/primary/database.go
@@ -24,7 +24,7 @@ type Database struct {
func NewPrimaryDatabase(cfg server.Config) (*Database, error) {
conf := cfg.DBMain
- url := fmt.Sprintf("file:%s?_journal=%s&_timeout=%d&_fk=%s", conf.File, conf.Journal, conf.Timeout.Milliseconds(), langext.FormatBool(conf.CheckForeignKeys, "true", "false"))
+ url := fmt.Sprintf("file:%s?_journal=%s&_timeout=%d&_fk=%s&_busy_timeout=%d", conf.File, conf.Journal, conf.Timeout.Milliseconds(), langext.FormatBool(conf.CheckForeignKeys, "true", "false"), conf.BusyTimeout.Milliseconds())
xdb, err := sqlx.Open("sqlite3", url)
if err != nil {
diff --git a/scnserver/db/impl/primary/deliveries.go b/scnserver/db/impl/primary/deliveries.go
index f85c46d..a85055d 100644
--- a/scnserver/db/impl/primary/deliveries.go
+++ b/scnserver/db/impl/primary/deliveries.go
@@ -17,8 +17,11 @@ func (db *Database) CreateRetryDelivery(ctx TxContext, client models.Client, msg
now := time.Now().UTC()
next := scn.NextDeliveryTimestamp(now)
- res, err := tx.Exec(ctx, "INSERT INTO deliveries (scn_message_id, receiver_user_id, receiver_client_id, timestamp_created, timestamp_finalized, status, fcm_message_id, next_delivery) VALUES (:mid, :ruid, :rcid, :tsc, :tsf, :stat, :fcm, :next)", sq.PP{
- "mid": msg.SCNMessageID,
+ deliveryid := models.NewDeliveryID()
+
+ _, err = tx.Exec(ctx, "INSERT INTO deliveries (delivery_id, message_id, receiver_user_id, receiver_client_id, timestamp_created, timestamp_finalized, status, fcm_message_id, next_delivery) VALUES (:did, :mid, :ruid, :rcid, :tsc, :tsf, :stat, :fcm, :next)", sq.PP{
+ "did": deliveryid,
+ "mid": msg.MessageID,
"ruid": client.UserID,
"rcid": client.ClientID,
"tsc": time2DB(now),
@@ -31,14 +34,9 @@ func (db *Database) CreateRetryDelivery(ctx TxContext, client models.Client, msg
return models.Delivery{}, err
}
- liid, err := res.LastInsertId()
- if err != nil {
- return models.Delivery{}, err
- }
-
return models.Delivery{
- DeliveryID: models.DeliveryID(liid),
- SCNMessageID: msg.SCNMessageID,
+ DeliveryID: deliveryid,
+ MessageID: msg.MessageID,
ReceiverUserID: client.UserID,
ReceiverClientID: client.ClientID,
TimestampCreated: now,
@@ -58,8 +56,11 @@ func (db *Database) CreateSuccessDelivery(ctx TxContext, client models.Client, m
now := time.Now().UTC()
- res, err := tx.Exec(ctx, "INSERT INTO deliveries (scn_message_id, receiver_user_id, receiver_client_id, timestamp_created, timestamp_finalized, status, fcm_message_id, next_delivery) VALUES (:mid, :ruid, :rcid, :tsc, :tsf, :stat, :fcm, :next)", sq.PP{
- "mid": msg.SCNMessageID,
+ deliveryid := models.NewDeliveryID()
+
+ _, err = tx.Exec(ctx, "INSERT INTO deliveries (delivery_id, message_id, receiver_user_id, receiver_client_id, timestamp_created, timestamp_finalized, status, fcm_message_id, next_delivery) VALUES (:did, :mid, :ruid, :rcid, :tsc, :tsf, :stat, :fcm, :next)", sq.PP{
+ "did": deliveryid,
+ "mid": msg.MessageID,
"ruid": client.UserID,
"rcid": client.ClientID,
"tsc": time2DB(now),
@@ -72,14 +73,9 @@ func (db *Database) CreateSuccessDelivery(ctx TxContext, client models.Client, m
return models.Delivery{}, err
}
- liid, err := res.LastInsertId()
- if err != nil {
- return models.Delivery{}, err
- }
-
return models.Delivery{
- DeliveryID: models.DeliveryID(liid),
- SCNMessageID: msg.SCNMessageID,
+ DeliveryID: deliveryid,
+ MessageID: msg.MessageID,
ReceiverUserID: client.UserID,
ReceiverClientID: client.ClientID,
TimestampCreated: now,
@@ -97,7 +93,7 @@ func (db *Database) ListRetrieableDeliveries(ctx TxContext, pageSize int) ([]mod
return nil, err
}
- rows, err := tx.Query(ctx, "SELECT * FROM deliveries WHERE status = 'RETRY' AND next_delivery < :next LIMIT :lim", sq.PP{
+ rows, err := tx.Query(ctx, "SELECT * FROM deliveries WHERE status = 'RETRY' AND next_delivery < :next LIMIT :lim ORDER BY next_delivery ASC", sq.PP{
"next": time2DB(time.Now()),
"lim": pageSize,
})
@@ -169,15 +165,15 @@ func (db *Database) SetDeliveryRetry(ctx TxContext, delivery models.Delivery) er
return nil
}
-func (db *Database) CancelPendingDeliveries(ctx TxContext, scnMessageID models.SCNMessageID) error {
+func (db *Database) CancelPendingDeliveries(ctx TxContext, messageID models.MessageID) error {
tx, err := ctx.GetOrCreateTransaction(db)
if err != nil {
return err
}
- _, err = tx.Exec(ctx, "UPDATE deliveries SET status = 'FAILED', next_delivery = NULL, timestamp_finalized = :ts WHERE scn_message_id = :mid AND status = 'RETRY'", sq.PP{
+ _, err = tx.Exec(ctx, "UPDATE deliveries SET status = 'FAILED', next_delivery = NULL, timestamp_finalized = :ts WHERE message_id = :mid AND status = 'RETRY'", sq.PP{
"ts": time.Now(),
- "mid": scnMessageID,
+ "mid": messageID,
})
if err != nil {
return err
diff --git a/scnserver/db/impl/primary/messages.go b/scnserver/db/impl/primary/messages.go
index 319c79f..959773e 100644
--- a/scnserver/db/impl/primary/messages.go
+++ b/scnserver/db/impl/primary/messages.go
@@ -30,7 +30,7 @@ func (db *Database) GetMessageByUserMessageID(ctx TxContext, usrMsgId string) (*
return &msg, nil
}
-func (db *Database) GetMessage(ctx TxContext, scnMessageID models.SCNMessageID, allowDeleted bool) (models.Message, error) {
+func (db *Database) GetMessage(ctx TxContext, scnMessageID models.MessageID, allowDeleted bool) (models.Message, error) {
tx, err := ctx.GetOrCreateTransaction(db)
if err != nil {
return models.Message{}, err
@@ -38,9 +38,9 @@ func (db *Database) GetMessage(ctx TxContext, scnMessageID models.SCNMessageID,
var sqlcmd string
if allowDeleted {
- sqlcmd = "SELECT * FROM messages WHERE scn_message_id = :mid LIMIT 1"
+ sqlcmd = "SELECT * FROM messages WHERE message_id = :mid LIMIT 1"
} else {
- sqlcmd = "SELECT * FROM messages WHERE scn_message_id = :mid AND deleted=0 LIMIT 1"
+ sqlcmd = "SELECT * FROM messages WHERE message_id = :mid AND deleted=0 LIMIT 1"
}
rows, err := tx.Query(ctx, sqlcmd, sq.PP{"mid": scnMessageID})
@@ -64,7 +64,10 @@ func (db *Database) CreateMessage(ctx TxContext, senderUserID models.UserID, cha
now := time.Now().UTC()
- res, err := tx.Exec(ctx, "INSERT INTO messages (sender_user_id, owner_user_id, channel_internal_name, channel_id, timestamp_real, timestamp_client, title, content, priority, usr_message_id, sender_ip, sender_name) VALUES (:suid, :ouid, :cnam, :cid, :tsr, :tsc, :tit, :cnt, :prio, :umid, :ip, :snam)", sq.PP{
+ messageid := models.NewMessageID()
+
+ _, err = tx.Exec(ctx, "INSERT INTO messages (message_id, sender_user_id, owner_user_id, channel_internal_name, channel_id, timestamp_real, timestamp_client, title, content, priority, usr_message_id, sender_ip, sender_name) VALUES (:mid, :suid, :ouid, :cnam, :cid, :tsr, :tsc, :tit, :cnt, :prio, :umid, :ip, :snam)", sq.PP{
+ "mid": messageid,
"suid": senderUserID,
"ouid": channel.OwnerUserID,
"cnam": channel.InternalName,
@@ -82,13 +85,8 @@ func (db *Database) CreateMessage(ctx TxContext, senderUserID models.UserID, cha
return models.Message{}, err
}
- liid, err := res.LastInsertId()
- if err != nil {
- return models.Message{}, err
- }
-
return models.Message{
- SCNMessageID: models.SCNMessageID(liid),
+ MessageID: messageid,
SenderUserID: senderUserID,
OwnerUserID: channel.OwnerUserID,
ChannelInternalName: channel.InternalName,
@@ -104,13 +102,13 @@ func (db *Database) CreateMessage(ctx TxContext, senderUserID models.UserID, cha
}, nil
}
-func (db *Database) DeleteMessage(ctx TxContext, scnMessageID models.SCNMessageID) error {
+func (db *Database) DeleteMessage(ctx TxContext, messageID models.MessageID) error {
tx, err := ctx.GetOrCreateTransaction(db)
if err != nil {
return err
}
- _, err = tx.Exec(ctx, "UPDATE messages SET deleted=1 WHERE scn_message_id = :mid AND deleted=0", sq.PP{"mid": scnMessageID})
+ _, err = tx.Exec(ctx, "UPDATE messages SET deleted=1 WHERE message_id = :mid AND deleted=0", sq.PP{"mid": messageID})
if err != nil {
return err
}
@@ -130,12 +128,12 @@ func (db *Database) ListMessages(ctx TxContext, filter models.MessageFilter, pag
pageCond := "1=1"
if inTok.Mode == cursortoken.CTMNormal {
- pageCond = "timestamp_real < :tokts OR (timestamp_real = :tokts AND scn_message_id < :tokid )"
+ pageCond = "timestamp_real < :tokts OR (timestamp_real = :tokts AND message_id < :tokid )"
}
filterCond, filterJoin, prepParams, err := filter.SQL()
- orderClause := "ORDER BY COALESCE(timestamp_client, timestamp_real) DESC LIMIT :lim"
+ orderClause := "ORDER BY COALESCE(timestamp_client, timestamp_real) DESC, message_id DESC LIMIT :lim"
sqlQuery := "SELECT " + "messages.*" + " FROM messages " + filterJoin + " WHERE ( " + pageCond + " ) AND ( " + filterCond + " ) " + orderClause
@@ -156,7 +154,7 @@ func (db *Database) ListMessages(ctx TxContext, filter models.MessageFilter, pag
if len(data) <= pageSize {
return data, cursortoken.End(), nil
} else {
- outToken := cursortoken.Normal(data[pageSize-1].Timestamp(), data[pageSize-1].SCNMessageID.IntID(), "DESC", filter.Hash())
+ outToken := cursortoken.Normal(data[pageSize-1].Timestamp(), data[pageSize-1].MessageID.String(), "DESC", filter.Hash())
return data[0:pageSize], outToken, nil
}
}
diff --git a/scnserver/db/impl/primary/schema/schema_1.ddl b/scnserver/db/impl/primary/schema/schema_1.ddl
index 504798d..416ea2a 100644
--- a/scnserver/db/impl/primary/schema/schema_1.ddl
+++ b/scnserver/db/impl/primary/schema/schema_1.ddl
@@ -20,7 +20,7 @@ CREATE TABLE `users`
DROP TABLE IF EXISTS `messages`;
CREATE TABLE `messages`
(
- `scn_message_id` INT(11) NOT NULL AUTO_INCREMENT,
+ `message_id` INT(11) NOT NULL AUTO_INCREMENT,
`sender_user_id` INT(11) NOT NULL,
`timestamp_real` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
@@ -34,5 +34,5 @@ CREATE TABLE `messages`
`fcm_message_id` VARCHAR(256) NULL,
`usr_message_id` VARCHAR(256) NULL,
- PRIMARY KEY (`scn_message_id`)
+ PRIMARY KEY (`message_id`)
);
\ No newline at end of file
diff --git a/scnserver/db/impl/primary/schema/schema_2.ddl b/scnserver/db/impl/primary/schema/schema_2.ddl
index 2032092..0efc08d 100644
--- a/scnserver/db/impl/primary/schema/schema_2.ddl
+++ b/scnserver/db/impl/primary/schema/schema_2.ddl
@@ -18,7 +18,7 @@ CREATE TABLE `users`
CREATE TABLE `messages`
(
- `scn_message_id` INTEGER AUTO_INCREMENT,
+ `message_id` INTEGER AUTO_INCREMENT,
`sender_user_id` INTEGER NOT NULL,
`timestamp_real` TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
@@ -32,7 +32,7 @@ CREATE TABLE `messages`
`fcm_message_id` TEXT NULL,
`usr_message_id` TEXT NULL,
- PRIMARY KEY (`scn_message_id`)
+ PRIMARY KEY (`message_id`)
);
CREATE TABLE `meta`
diff --git a/scnserver/db/impl/primary/schema/schema_3.ddl b/scnserver/db/impl/primary/schema/schema_3.ddl
index b2ddddf..11babce 100644
--- a/scnserver/db/impl/primary/schema/schema_3.ddl
+++ b/scnserver/db/impl/primary/schema/schema_3.ddl
@@ -1,6 +1,6 @@
CREATE TABLE users
(
- user_id INTEGER PRIMARY KEY AUTOINCREMENT,
+ user_id TEXT NOT NULL,
username TEXT NULL DEFAULT NULL,
@@ -18,23 +18,27 @@ CREATE TABLE users
quota_used_day TEXT NULL DEFAULT NULL,
is_pro INTEGER CHECK(is_pro IN (0, 1)) NOT NULL DEFAULT 0,
- pro_token TEXT NULL DEFAULT NULL
+ pro_token TEXT NULL DEFAULT NULL,
+
+ PRIMARY KEY (user_id)
) STRICT;
CREATE UNIQUE INDEX "idx_users_protoken" ON users (pro_token) WHERE pro_token IS NOT NULL;
CREATE TABLE clients
(
- client_id INTEGER PRIMARY KEY AUTOINCREMENT,
+ client_id TEXT NOT NULL,
- user_id INTEGER NOT NULL,
+ user_id TEXT NOT NULL,
type TEXT CHECK(type IN ('ANDROID', 'IOS')) NOT NULL,
fcm_token TEXT NULL,
timestamp_created INTEGER NOT NULL,
agent_model TEXT NOT NULL,
- agent_version TEXT NOT NULL
+ agent_version TEXT NOT NULL,
+
+ PRIMARY KEY (client_id)
) STRICT;
CREATE INDEX "idx_clients_userid" ON clients (user_id);
CREATE UNIQUE INDEX "idx_clients_fcmtoken" ON clients (fcm_token);
@@ -42,9 +46,9 @@ CREATE UNIQUE INDEX "idx_clients_fcmtoken" ON clients (fcm_token);
CREATE TABLE channels
(
- channel_id INTEGER PRIMARY KEY AUTOINCREMENT,
+ channel_id TEXT NOT NULL,
- owner_user_id INTEGER NOT NULL,
+ owner_user_id TEXT NOT NULL,
internal_name TEXT NOT NULL,
display_name TEXT NOT NULL,
@@ -56,22 +60,26 @@ CREATE TABLE channels
timestamp_created INTEGER NOT NULL,
timestamp_lastsent INTEGER NULL DEFAULT NULL,
- messages_sent INTEGER NOT NULL DEFAULT '0'
+ messages_sent INTEGER NOT NULL DEFAULT '0',
+
+ PRIMARY KEY (channel_id)
) STRICT;
CREATE UNIQUE INDEX "idx_channels_identity" ON channels (owner_user_id, internal_name);
CREATE TABLE subscriptions
(
- subscription_id INTEGER PRIMARY KEY AUTOINCREMENT,
+ subscription_id TEXT NOT NULL,
- subscriber_user_id INTEGER NOT NULL,
- channel_owner_user_id INTEGER NOT NULL,
+ subscriber_user_id TEXT NOT NULL,
+ channel_owner_user_id TEXT NOT NULL,
channel_internal_name TEXT NOT NULL,
- channel_id INTEGER NOT NULL,
+ channel_id TEXT NOT NULL,
timestamp_created INTEGER NOT NULL,
- confirmed INTEGER CHECK(confirmed IN (0, 1)) NOT NULL
+ confirmed INTEGER CHECK(confirmed IN (0, 1)) NOT NULL,
+
+ PRIMARY KEY (subscription_id)
) STRICT;
CREATE UNIQUE INDEX "idx_subscriptions_ref" ON subscriptions (subscriber_user_id, channel_owner_user_id, channel_internal_name);
CREATE INDEX "idx_subscriptions_chan" ON subscriptions (channel_id);
@@ -83,11 +91,11 @@ CREATE INDEX "idx_subscriptions_conf" ON subscriptions (confirmed);
CREATE TABLE messages
(
- scn_message_id INTEGER PRIMARY KEY AUTOINCREMENT,
- sender_user_id INTEGER NOT NULL,
- owner_user_id INTEGER NOT NULL,
+ message_id TEXT NOT NULL,
+ sender_user_id TEXT NOT NULL,
+ owner_user_id TEXT NOT NULL,
channel_internal_name TEXT NOT NULL,
- channel_id INTEGER NOT NULL,
+ channel_id TEXT NOT NULL,
sender_ip TEXT NOT NULL,
sender_name TEXT NULL,
@@ -99,7 +107,9 @@ CREATE TABLE messages
priority INTEGER CHECK(priority IN (0, 1, 2)) NOT NULL,
usr_message_id TEXT NULL,
- deleted INTEGER CHECK(deleted IN (0, 1)) NOT NULL DEFAULT '0'
+ deleted INTEGER CHECK(deleted IN (0, 1)) NOT NULL DEFAULT '0',
+
+ PRIMARY KEY (message_id)
) STRICT;
CREATE INDEX "idx_messages_owner_channel" ON messages (owner_user_id, channel_internal_name COLLATE BINARY);
CREATE INDEX "idx_messages_owner_channel_nc" ON messages (owner_user_id, channel_internal_name COLLATE NOCASE);
@@ -123,31 +133,30 @@ CREATE VIRTUAL TABLE messages_fts USING fts5
tokenize = unicode61,
content = 'messages',
- content_rowid = 'scn_message_id'
+ content_rowid = 'rowid'
);
CREATE TRIGGER fts_insert AFTER INSERT ON messages BEGIN
- INSERT INTO messages_fts (rowid, channel_internal_name, sender_name, title, content) VALUES (new.scn_message_id, new.channel_internal_name, new.sender_name, new.title, new.content);
+ INSERT INTO messages_fts (rowid, channel_internal_name, sender_name, title, content) VALUES (new.rowid, new.channel_internal_name, new.sender_name, new.title, new.content);
END;
CREATE TRIGGER fts_update AFTER UPDATE ON messages BEGIN
- INSERT INTO messages_fts (messages_fts, rowid, channel_internal_name, sender_name, title, content) VALUES ('delete', old.scn_message_id, old.channel_internal_name, old.sender_name, old.title, old.content);
- INSERT INTO messages_fts ( rowid, channel_internal_name, sender_name, title, content) VALUES ( new.scn_message_id, new.channel_internal_name, new.sender_name, new.title, new.content);
+ INSERT INTO messages_fts (messages_fts, rowid, channel_internal_name, sender_name, title, content) VALUES ('delete', old.rowid, old.channel_internal_name, old.sender_name, old.title, old.content);
+ INSERT INTO messages_fts ( rowid, channel_internal_name, sender_name, title, content) VALUES ( new.rowid, new.channel_internal_name, new.sender_name, new.title, new.content);
END;
CREATE TRIGGER fts_delete AFTER DELETE ON messages BEGIN
- INSERT INTO messages_fts (messages_fts, rowid, channel_internal_name, sender_name, title, content) VALUES ('delete', old.scn_message_id, old.channel_internal_name, old.sender_name, old.title, old.content);
+ INSERT INTO messages_fts (messages_fts, rowid, channel_internal_name, sender_name, title, content) VALUES ('delete', old.rowid, old.channel_internal_name, old.sender_name, old.title, old.content);
END;
-
CREATE TABLE deliveries
(
- delivery_id INTEGER PRIMARY KEY AUTOINCREMENT,
+ delivery_id TEXT NOT NULL,
- scn_message_id INTEGER NOT NULL,
- receiver_user_id INTEGER NOT NULL,
- receiver_client_id INTEGER NOT NULL,
+ message_id TEXT NOT NULL,
+ receiver_user_id TEXT NOT NULL,
+ receiver_client_id TEXT NOT NULL,
timestamp_created INTEGER NOT NULL,
timestamp_finalized INTEGER NULL,
@@ -157,9 +166,21 @@ CREATE TABLE deliveries
retry_count INTEGER NOT NULL DEFAULT 0,
next_delivery INTEGER NULL DEFAULT NULL,
- fcm_message_id TEXT NULL
+ fcm_message_id TEXT NULL,
+
+ PRIMARY KEY (delivery_id)
) STRICT;
-CREATE INDEX "idx_deliveries_receiver" ON deliveries (scn_message_id, receiver_client_id);
+CREATE INDEX "idx_deliveries_receiver" ON deliveries (message_id, receiver_client_id);
+
+
+CREATE TABLE compat_ids
+(
+ old INTEGER NOT NULL,
+ new TEXT NOT NULL,
+ type TEXT NOT NULL
+) STRICT;
+CREATE UNIQUE INDEX "idx_compatids_new" ON compat_ids (new);
+CREATE UNIQUE INDEX "idx_compatids_old" ON compat_ids (old, type);
CREATE TABLE `meta`
diff --git a/scnserver/db/impl/primary/subscriptions.go b/scnserver/db/impl/primary/subscriptions.go
index e1d7ed7..19aadcf 100644
--- a/scnserver/db/impl/primary/subscriptions.go
+++ b/scnserver/db/impl/primary/subscriptions.go
@@ -15,7 +15,10 @@ func (db *Database) CreateSubscription(ctx TxContext, subscriberUID models.UserI
now := time.Now().UTC()
- res, err := tx.Exec(ctx, "INSERT INTO subscriptions (subscriber_user_id, channel_owner_user_id, channel_internal_name, channel_id, timestamp_created, confirmed) VALUES (:suid, :ouid, :cnam, :cid, :ts, :conf)", sq.PP{
+ subscriptionid := models.NewSubscriptionID()
+
+ _, err = tx.Exec(ctx, "INSERT INTO subscriptions (subscription_id, subscriber_user_id, channel_owner_user_id, channel_internal_name, channel_id, timestamp_created, confirmed) VALUES (:sid, :suid, :ouid, :cnam, :cid, :ts, :conf)", sq.PP{
+ "sid": subscriptionid,
"suid": subscriberUID,
"ouid": channel.OwnerUserID,
"cnam": channel.InternalName,
@@ -27,13 +30,8 @@ func (db *Database) CreateSubscription(ctx TxContext, subscriberUID models.UserI
return models.Subscription{}, err
}
- liid, err := res.LastInsertId()
- if err != nil {
- return models.Subscription{}, err
- }
-
return models.Subscription{
- SubscriptionID: models.SubscriptionID(liid),
+ SubscriptionID: subscriptionid,
SubscriberUserID: subscriberUID,
ChannelOwnerUserID: channel.OwnerUserID,
ChannelID: channel.ChannelID,
@@ -49,7 +47,9 @@ func (db *Database) ListSubscriptionsByChannel(ctx TxContext, channelID models.C
return nil, err
}
- rows, err := tx.Query(ctx, "SELECT * FROM subscriptions WHERE channel_id = :cid", sq.PP{"cid": channelID})
+ order := " ORDER BY subscriptions.timestamp_created DESC, subscriptions.subscription_id DESC "
+
+ rows, err := tx.Query(ctx, "SELECT * FROM subscriptions WHERE channel_id = :cid"+order, sq.PP{"cid": channelID})
if err != nil {
return nil, err
}
@@ -75,7 +75,9 @@ func (db *Database) ListSubscriptionsByChannelOwner(ctx TxContext, ownerUserID m
cond = " AND confirmed = 0"
}
- rows, err := tx.Query(ctx, "SELECT * FROM subscriptions WHERE channel_owner_user_id = :ouid"+cond, sq.PP{"ouid": ownerUserID})
+ order := " ORDER BY subscriptions.timestamp_created DESC, subscriptions.subscription_id DESC "
+
+ rows, err := tx.Query(ctx, "SELECT * FROM subscriptions WHERE channel_owner_user_id = :ouid"+cond+order, sq.PP{"ouid": ownerUserID})
if err != nil {
return nil, err
}
@@ -101,7 +103,9 @@ func (db *Database) ListSubscriptionsBySubscriber(ctx TxContext, subscriberUserI
cond = " AND confirmed = 0"
}
- rows, err := tx.Query(ctx, "SELECT * FROM subscriptions WHERE subscriber_user_id = :suid"+cond, sq.PP{"suid": subscriberUserID})
+ order := " ORDER BY subscriptions.timestamp_created DESC, subscriptions.subscription_id DESC "
+
+ rows, err := tx.Query(ctx, "SELECT * FROM subscriptions WHERE subscriber_user_id = :suid"+cond+order, sq.PP{"suid": subscriberUserID})
if err != nil {
return nil, err
}
diff --git a/scnserver/db/impl/primary/users.go b/scnserver/db/impl/primary/users.go
index f8dd031..525a77f 100644
--- a/scnserver/db/impl/primary/users.go
+++ b/scnserver/db/impl/primary/users.go
@@ -16,7 +16,10 @@ func (db *Database) CreateUser(ctx TxContext, readKey string, sendKey string, ad
now := time.Now().UTC()
- res, err := tx.Exec(ctx, "INSERT INTO users (username, read_key, send_key, admin_key, is_pro, pro_token, timestamp_created) VALUES (:un, :rk, :sk, :ak, :pro, :tok, :ts)", sq.PP{
+ userid := models.NewUserID()
+
+ _, err = tx.Exec(ctx, "INSERT INTO users (user_id, username, read_key, send_key, admin_key, is_pro, pro_token, timestamp_created) VALUES (:uid, :un, :rk, :sk, :ak, :pro, :tok, :ts)", sq.PP{
+ "uid": userid,
"un": username,
"rk": readKey,
"sk": sendKey,
@@ -29,13 +32,8 @@ func (db *Database) CreateUser(ctx TxContext, readKey string, sendKey string, ad
return models.User{}, err
}
- liid, err := res.LastInsertId()
- if err != nil {
- return models.User{}, err
- }
-
return models.User{
- UserID: models.UserID(liid),
+ UserID: userid,
Username: username,
ReadKey: readKey,
SendKey: sendKey,
diff --git a/scnserver/db/impl/requests/database.go b/scnserver/db/impl/requests/database.go
index 771d79f..0e0aaba 100644
--- a/scnserver/db/impl/requests/database.go
+++ b/scnserver/db/impl/requests/database.go
@@ -24,7 +24,7 @@ type Database struct {
func NewRequestsDatabase(cfg server.Config) (*Database, error) {
conf := cfg.DBRequests
- url := fmt.Sprintf("file:%s?_journal=%s&_timeout=%d&_fk=%s", conf.File, conf.Journal, conf.Timeout.Milliseconds(), langext.FormatBool(conf.CheckForeignKeys, "true", "false"))
+ url := fmt.Sprintf("file:%s?_journal=%s&_timeout=%d&_fk=%s&_busy_timeout=%d", conf.File, conf.Journal, conf.Timeout.Milliseconds(), langext.FormatBool(conf.CheckForeignKeys, "true", "false"), conf.BusyTimeout.Milliseconds())
xdb, err := sqlx.Open("sqlite3", url)
if err != nil {
diff --git a/scnserver/db/impl/requests/requestlogs.go b/scnserver/db/impl/requests/requestlogs.go
index d7b89d1..d451185 100644
--- a/scnserver/db/impl/requests/requestlogs.go
+++ b/scnserver/db/impl/requests/requestlogs.go
@@ -7,11 +7,12 @@ import (
"time"
)
-func (db *Database) InsertRequestLog(ctx context.Context, data models.RequestLogDB) (models.RequestLogDB, error) {
+func (db *Database) InsertRequestLog(ctx context.Context, requestid models.RequestID, data models.RequestLogDB) (models.RequestLogDB, error) {
now := time.Now()
- res, err := db.db.Exec(ctx, "INSERT INTO requests (method, uri, user_agent, authentication, request_body, request_body_size, request_content_type, remote_ip, userid, permissions, response_statuscode, response_body_size, response_body, response_content_type, retry_count, panicked, panic_str, processing_time, timestamp_created, timestamp_start, timestamp_finish) VALUES (:method, :uri, :user_agent, :authentication, :request_body, :request_body_size, :request_content_type, :remote_ip, :userid, :permissions, :response_statuscode, :response_body_size, :response_body, :response_content_type, :retry_count, :panicked, :panic_str, :processing_time, :timestamp_created, :timestamp_start, :timestamp_finish)", sq.PP{
+ _, err := db.db.Exec(ctx, "INSERT INTO requests (request_id, method, uri, user_agent, authentication, request_body, request_body_size, request_content_type, remote_ip, userid, permissions, response_statuscode, response_body_size, response_body, response_content_type, retry_count, panicked, panic_str, processing_time, timestamp_created, timestamp_start, timestamp_finish) VALUES (:request_id, :method, :uri, :user_agent, :authentication, :request_body, :request_body_size, :request_content_type, :remote_ip, :userid, :permissions, :response_statuscode, :response_body_size, :response_body, :response_content_type, :retry_count, :panicked, :panic_str, :processing_time, :timestamp_created, :timestamp_start, :timestamp_finish)", sq.PP{
+ "request_id": requestid,
"method": data.Method,
"uri": data.URI,
"user_agent": data.UserAgent,
@@ -38,13 +39,8 @@ func (db *Database) InsertRequestLog(ctx context.Context, data models.RequestLog
return models.RequestLogDB{}, err
}
- liid, err := res.LastInsertId()
- if err != nil {
- return models.RequestLogDB{}, err
- }
-
return models.RequestLogDB{
- RequestID: models.RequestID(liid),
+ RequestID: requestid,
Method: data.Method,
URI: data.URI,
UserAgent: data.UserAgent,
diff --git a/scnserver/db/impl/requests/schema/schema_1.ddl b/scnserver/db/impl/requests/schema/schema_1.ddl
index efe073b..64859d2 100644
--- a/scnserver/db/impl/requests/schema/schema_1.ddl
+++ b/scnserver/db/impl/requests/schema/schema_1.ddl
@@ -1,7 +1,7 @@
CREATE TABLE `requests`
(
- request_id INTEGER PRIMARY KEY AUTOINCREMENT,
+ request_id TEXT NOT NULL,
method TEXT NOT NULL,
uri TEXT NOT NULL,
@@ -15,8 +15,8 @@ CREATE TABLE `requests`
userid TEXT NULL,
permissions TEXT NULL,
- response_statuscode INTEGER NOT NULL,
- response_body_size INTEGER NOT NULL,
+ response_statuscode INTEGER NULL,
+ response_body_size INTEGER NULL,
response_body TEXT NULL,
response_content_type TEXT NOT NULL,
processing_time INTEGER NOT NULL,
@@ -26,8 +26,9 @@ CREATE TABLE `requests`
timestamp_created INTEGER NOT NULL,
timestamp_start INTEGER NOT NULL,
- timestamp_finish INTEGER NOT NULL
+ timestamp_finish INTEGER NOT NULL,
+ PRIMARY KEY (request_id)
) STRICT;
diff --git a/scnserver/jobs/DeliveryRetryJob.go b/scnserver/jobs/DeliveryRetryJob.go
index 6de62dd..237f712 100644
--- a/scnserver/jobs/DeliveryRetryJob.go
+++ b/scnserver/jobs/DeliveryRetryJob.go
@@ -134,14 +134,14 @@ func (j *DeliveryRetryJob) redeliver(ctx *logic.SimpleContext, delivery models.D
client, err := j.app.Database.Primary.GetClient(ctx, delivery.ReceiverUserID, delivery.ReceiverClientID)
if err != nil {
- log.Err(err).Int64("ReceiverUserID", delivery.ReceiverUserID.IntID()).Int64("ReceiverClientID", delivery.ReceiverClientID.IntID()).Msg("Failed to get client")
+ log.Err(err).Str("ReceiverUserID", delivery.ReceiverUserID.String()).Str("ReceiverClientID", delivery.ReceiverClientID.String()).Msg("Failed to get client")
ctx.RollbackTransaction()
return
}
- msg, err := j.app.Database.Primary.GetMessage(ctx, delivery.SCNMessageID, true)
+ msg, err := j.app.Database.Primary.GetMessage(ctx, delivery.MessageID, true)
if err != nil {
- log.Err(err).Int64("SCNMessageID", delivery.SCNMessageID.IntID()).Msg("Failed to get message")
+ log.Err(err).Str("MessageID", delivery.MessageID.String()).Msg("Failed to get message")
ctx.RollbackTransaction()
return
}
@@ -149,7 +149,7 @@ func (j *DeliveryRetryJob) redeliver(ctx *logic.SimpleContext, delivery models.D
if msg.Deleted {
err = j.app.Database.Primary.SetDeliveryFailed(ctx, delivery)
if err != nil {
- log.Err(err).Int64("SCNMessageID", delivery.SCNMessageID.IntID()).Int64("DeliveryID", delivery.DeliveryID.IntID()).Msg("Failed to update delivery")
+ log.Err(err).Str("MessageID", delivery.MessageID.String()).Str("DeliveryID", delivery.DeliveryID.String()).Msg("Failed to update delivery")
ctx.RollbackTransaction()
return
}
@@ -159,22 +159,22 @@ func (j *DeliveryRetryJob) redeliver(ctx *logic.SimpleContext, delivery models.D
if err == nil {
err = j.app.Database.Primary.SetDeliverySuccess(ctx, delivery, *fcmDelivID)
if err != nil {
- log.Err(err).Int64("SCNMessageID", delivery.SCNMessageID.IntID()).Int64("DeliveryID", delivery.DeliveryID.IntID()).Msg("Failed to update delivery")
+ log.Err(err).Str("MessageID", delivery.MessageID.String()).Str("DeliveryID", delivery.DeliveryID.String()).Msg("Failed to update delivery")
ctx.RollbackTransaction()
return
}
} else if delivery.RetryCount+1 > delivery.MaxRetryCount() {
err = j.app.Database.Primary.SetDeliveryFailed(ctx, delivery)
if err != nil {
- log.Err(err).Int64("SCNMessageID", delivery.SCNMessageID.IntID()).Int64("DeliveryID", delivery.DeliveryID.IntID()).Msg("Failed to update delivery")
+ log.Err(err).Str("MessageID", delivery.MessageID.String()).Str("DeliveryID", delivery.DeliveryID.String()).Msg("Failed to update delivery")
ctx.RollbackTransaction()
return
}
- log.Warn().Int64("SCNMessageID", delivery.SCNMessageID.IntID()).Int64("DeliveryID", delivery.DeliveryID.IntID()).Msg("Delivery failed after retries (set to FAILURE)")
+ log.Warn().Str("MessageID", delivery.MessageID.String()).Str("DeliveryID", delivery.DeliveryID.String()).Msg("Delivery failed after retries (set to FAILURE)")
} else {
err = j.app.Database.Primary.SetDeliveryRetry(ctx, delivery)
if err != nil {
- log.Err(err).Int64("SCNMessageID", delivery.SCNMessageID.IntID()).Int64("DeliveryID", delivery.DeliveryID.IntID()).Msg("Failed to update delivery")
+ log.Err(err).Str("MessageID", delivery.MessageID.String()).Str("DeliveryID", delivery.DeliveryID.String()).Msg("Failed to update delivery")
ctx.RollbackTransaction()
return
}
diff --git a/scnserver/jobs/RequestLogCollectorJob.go b/scnserver/jobs/RequestLogCollectorJob.go
index dc13742..b723967 100644
--- a/scnserver/jobs/RequestLogCollectorJob.go
+++ b/scnserver/jobs/RequestLogCollectorJob.go
@@ -72,11 +72,12 @@ mainLoop:
log.Error().Msg(fmt.Sprintf("Received unknown job signal: <%s> in job [%s]", signal, j.name))
}
case obj := <-j.app.RequestLogQueue:
- err := j.insertLog(obj)
+ requestid := models.NewRequestID()
+ err := j.insertLog(requestid, obj)
if err != nil {
- log.Error().Err(err).Msg(fmt.Sprintf("Failed to insert RequestLog {%s} into DB", obj.RequestID))
+ log.Error().Err(err).Msg(fmt.Sprintf("Failed to insert RequestLog {%s} into DB", requestid))
} else {
- log.Debug().Msg(fmt.Sprintf("Inserted RequestLog '%s' into DB", obj.RequestID))
+ log.Debug().Msg(fmt.Sprintf("Inserted RequestLog '%s' into DB", requestid))
}
}
}
@@ -86,12 +87,12 @@ mainLoop:
j.isRunning.Set(false)
}
-func (j *RequestLogCollectorJob) insertLog(rl models.RequestLog) error {
+func (j *RequestLogCollectorJob) insertLog(requestid models.RequestID, rl models.RequestLog) error {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
- _, err := j.app.Database.Requests.InsertRequestLog(ctx, rl.DB())
+ _, err := j.app.Database.Requests.InsertRequestLog(ctx, requestid, rl.DB())
if err != nil {
return err
}
diff --git a/scnserver/logic/application.go b/scnserver/logic/application.go
index 741a706..d9eaa6b 100644
--- a/scnserver/logic/application.go
+++ b/scnserver/logic/application.go
@@ -347,7 +347,7 @@ func (app *Application) DeliverMessage(ctx context.Context, client models.Client
if client.FCMToken != nil {
fcmDelivID, err := app.Pusher.SendNotification(ctx, client, msg)
if err != nil {
- log.Warn().Int64("SCNMessageID", msg.SCNMessageID.IntID()).Int64("ClientID", client.ClientID.IntID()).Err(err).Msg("FCM Delivery failed")
+ log.Warn().Str("MessageID", msg.MessageID.String()).Str("ClientID", client.ClientID.String()).Err(err).Msg("FCM Delivery failed")
return nil, err
}
return langext.Ptr(fcmDelivID), nil
diff --git a/scnserver/models/delivery.go b/scnserver/models/delivery.go
index 5ee71e2..733c979 100644
--- a/scnserver/models/delivery.go
+++ b/scnserver/models/delivery.go
@@ -17,7 +17,7 @@ const (
type Delivery struct {
DeliveryID DeliveryID
- SCNMessageID SCNMessageID
+ MessageID MessageID
ReceiverUserID UserID
ReceiverClientID ClientID
TimestampCreated time.Time
@@ -31,7 +31,7 @@ type Delivery struct {
func (d Delivery) JSON() DeliveryJSON {
return DeliveryJSON{
DeliveryID: d.DeliveryID,
- SCNMessageID: d.SCNMessageID,
+ MessageID: d.MessageID,
ReceiverUserID: d.ReceiverUserID,
ReceiverClientID: d.ReceiverClientID,
TimestampCreated: d.TimestampCreated.Format(time.RFC3339Nano),
@@ -49,7 +49,7 @@ func (d Delivery) MaxRetryCount() int {
type DeliveryJSON struct {
DeliveryID DeliveryID `json:"delivery_id"`
- SCNMessageID SCNMessageID `json:"scn_message_id"`
+ MessageID MessageID `json:"message_id"`
ReceiverUserID UserID `json:"receiver_user_id"`
ReceiverClientID ClientID `json:"receiver_client_id"`
TimestampCreated string `json:"timestamp_created"`
@@ -62,7 +62,7 @@ type DeliveryJSON struct {
type DeliveryDB struct {
DeliveryID DeliveryID `db:"delivery_id"`
- SCNMessageID SCNMessageID `db:"scn_message_id"`
+ MessageID MessageID `db:"message_id"`
ReceiverUserID UserID `db:"receiver_user_id"`
ReceiverClientID ClientID `db:"receiver_client_id"`
TimestampCreated int64 `db:"timestamp_created"`
@@ -76,7 +76,7 @@ type DeliveryDB struct {
func (d DeliveryDB) Model() Delivery {
return Delivery{
DeliveryID: d.DeliveryID,
- SCNMessageID: d.SCNMessageID,
+ MessageID: d.MessageID,
ReceiverUserID: d.ReceiverUserID,
ReceiverClientID: d.ReceiverClientID,
TimestampCreated: time.UnixMilli(d.TimestampCreated),
diff --git a/scnserver/models/ids.go b/scnserver/models/ids.go
index 04487cc..8a19e37 100644
--- a/scnserver/models/ids.go
+++ b/scnserver/models/ids.go
@@ -1,78 +1,376 @@
package models
-import "strconv"
+import (
+ "crypto/rand"
+ "errors"
+ "fmt"
+ "github.com/go-playground/validator/v10"
+ "github.com/rs/zerolog/log"
+ "gogs.mikescher.com/BlackForestBytes/goext/langext"
+ "math/big"
+ "reflect"
+ "regexp"
+ "strings"
+)
type EntityID interface {
- IntID() int64
String() string
+ Valid() error
+ Prefix() string
+ Raw() string
+ CheckString() string
+ Regex() *regexp.Regexp
}
-type UserID int64
+const idlen = 24
-func (id UserID) IntID() int64 {
- return int64(id)
+const checklen = 1
+
+const idCharset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+const idCharsetLen = len(idCharset)
+
+var charSetReverseMap = generateCharsetMap()
+
+const (
+ prefixUserID = "USR"
+ prefixChannelID = "CHA"
+ prefixDeliveryID = "DEL"
+ prefixMessageID = "MSG"
+ prefixSubscriptionID = "SUB"
+ prefixClientID = "CLN"
+ prefixRequestID = "REQ"
+)
+
+var (
+ regexUserID = generateRegex(prefixUserID)
+ regexChannelID = generateRegex(prefixChannelID)
+ regexDeliveryID = generateRegex(prefixDeliveryID)
+ regexMessageID = generateRegex(prefixMessageID)
+ regexSubscriptionID = generateRegex(prefixSubscriptionID)
+ regexClientID = generateRegex(prefixClientID)
+ regexRequestID = generateRegex(prefixRequestID)
+)
+
+func generateRegex(prefix string) *regexp.Regexp {
+ return regexp.MustCompile(fmt.Sprintf("^%s[%s]{%d}[%s]{%d}$", prefix, idCharset, idlen-len(prefix)-checklen, idCharset, checklen))
+}
+
+func generateCharsetMap() []int {
+ result := make([]int, 128)
+ for i := 0; i < len(result); i++ {
+ result[i] = -1
+ }
+ for idx, chr := range idCharset {
+ result[int(chr)] = idx
+ }
+ return result
+}
+
+func generateID(prefix string) string {
+ k := ""
+ max := big.NewInt(int64(idCharsetLen))
+ checksum := 0
+ for i := 0; i < idlen-len(prefix)-checklen; i++ {
+ v, err := rand.Int(rand.Reader, max)
+ if err != nil {
+ panic(err)
+ }
+ v64 := v.Int64()
+ k += string(idCharset[v64])
+ checksum = (checksum + int(v64)) % (idCharsetLen)
+ }
+ checkstr := string(idCharset[checksum%idCharsetLen])
+ return prefix + k + checkstr
+}
+
+func validateID(prefix string, value string) error {
+ if len(value) != idlen {
+ return errors.New("id has the wrong length")
+ }
+
+ if !strings.HasPrefix(value, prefix) {
+ return errors.New("id is missing the correct prefix")
+ }
+
+ checksum := 0
+ for i := len(prefix); i < len(value)-checklen; i++ {
+ ichr := int(value[i])
+ if ichr < 0 || ichr >= len(charSetReverseMap) || charSetReverseMap[ichr] == -1 {
+ return errors.New("id contains invalid characters")
+ }
+ checksum = (checksum + charSetReverseMap[ichr]) % (idCharsetLen)
+ }
+
+ checkstr := string(idCharset[checksum%idCharsetLen])
+
+ if !strings.HasSuffix(value, checkstr) {
+ return errors.New("id checkstring is invalid")
+ }
+
+ return nil
+}
+
+func getRawData(prefix string, value string) string {
+ if len(value) != idlen {
+ return ""
+ }
+ return value[len(prefix) : idlen-checklen]
+}
+
+func getCheckString(prefix string, value string) string {
+ if len(value) != idlen {
+ return ""
+ }
+ return value[idlen-checklen:]
+}
+
+func ValidateEntityID(vfl validator.FieldLevel) bool {
+ if !vfl.Field().CanInterface() {
+ log.Error().Msgf("Failed to validate EntityID (cannot interface ?!?)")
+ return false
+ }
+
+ ifvalue := vfl.Field().Interface()
+
+ if value1, ok := ifvalue.(EntityID); ok {
+
+ if vfl.Field().Type().Kind() == reflect.Pointer && langext.IsNil(value1) {
+ return true
+ }
+
+ if err := value1.Valid(); err != nil {
+ log.Debug().Msgf("Failed to validate EntityID '%s' (%s)", value1.String(), err.Error())
+ return false
+ } else {
+ return true
+ }
+
+ } else {
+ log.Error().Msgf("Failed to validate EntityID (wrong type: %T)", ifvalue)
+ return false
+ }
+}
+
+// ------------------------------------------------------------
+
+type UserID string
+
+func NewUserID() UserID {
+ return UserID(generateID(prefixUserID))
+}
+
+func (id UserID) Valid() error {
+ return validateID(prefixUserID, string(id))
}
func (id UserID) String() string {
- return strconv.FormatInt(int64(id), 10)
+ return string(id)
}
-type ChannelID int64
+func (id UserID) Prefix() string {
+ return prefixUserID
+}
-func (id ChannelID) IntID() int64 {
- return int64(id)
+func (id UserID) Raw() string {
+ return getRawData(prefixUserID, string(id))
+}
+
+func (id UserID) CheckString() string {
+ return getCheckString(prefixUserID, string(id))
+}
+
+func (id UserID) Regex() *regexp.Regexp {
+ return regexUserID
+}
+
+// ------------------------------------------------------------
+
+type ChannelID string
+
+func NewChannelID() ChannelID {
+ return ChannelID(generateID(prefixChannelID))
+}
+
+func (id ChannelID) Valid() error {
+ return validateID(prefixChannelID, string(id))
}
func (id ChannelID) String() string {
- return strconv.FormatInt(int64(id), 10)
+ return string(id)
}
-type DeliveryID int64
+func (id ChannelID) Prefix() string {
+ return prefixChannelID
+}
-func (id DeliveryID) IntID() int64 {
- return int64(id)
+func (id ChannelID) Raw() string {
+ return getRawData(prefixChannelID, string(id))
+}
+
+func (id ChannelID) CheckString() string {
+ return getCheckString(prefixChannelID, string(id))
+}
+
+func (id ChannelID) Regex() *regexp.Regexp {
+ return regexChannelID
+}
+
+// ------------------------------------------------------------
+
+type DeliveryID string
+
+func NewDeliveryID() DeliveryID {
+ return DeliveryID(generateID(prefixDeliveryID))
+}
+
+func (id DeliveryID) Valid() error {
+ return validateID(prefixDeliveryID, string(id))
}
func (id DeliveryID) String() string {
- return strconv.FormatInt(int64(id), 10)
+ return string(id)
}
-type SCNMessageID int64
-
-func (id SCNMessageID) IntID() int64 {
- return int64(id)
+func (id DeliveryID) Prefix() string {
+ return prefixDeliveryID
}
-func (id SCNMessageID) String() string {
- return strconv.FormatInt(int64(id), 10)
+func (id DeliveryID) Raw() string {
+ return getRawData(prefixDeliveryID, string(id))
}
-type SubscriptionID int64
+func (id DeliveryID) CheckString() string {
+ return getCheckString(prefixDeliveryID, string(id))
+}
-func (id SubscriptionID) IntID() int64 {
- return int64(id)
+func (id DeliveryID) Regex() *regexp.Regexp {
+ return regexDeliveryID
+}
+
+// ------------------------------------------------------------
+
+type MessageID string
+
+func NewMessageID() MessageID {
+ return MessageID(generateID(prefixMessageID))
+}
+
+func (id MessageID) Valid() error {
+ return validateID(prefixMessageID, string(id))
+}
+
+func (id MessageID) String() string {
+ return string(id)
+}
+
+func (id MessageID) Prefix() string {
+ return prefixMessageID
+}
+
+func (id MessageID) Raw() string {
+ return getRawData(prefixMessageID, string(id))
+}
+
+func (id MessageID) CheckString() string {
+ return getCheckString(prefixMessageID, string(id))
+}
+
+func (id MessageID) Regex() *regexp.Regexp {
+ return regexMessageID
+}
+
+// ------------------------------------------------------------
+
+type SubscriptionID string
+
+func NewSubscriptionID() SubscriptionID {
+ return SubscriptionID(generateID(prefixSubscriptionID))
+}
+
+func (id SubscriptionID) Valid() error {
+ return validateID(prefixSubscriptionID, string(id))
}
func (id SubscriptionID) String() string {
- return strconv.FormatInt(int64(id), 10)
+ return string(id)
}
-type ClientID int64
+func (id SubscriptionID) Prefix() string {
+ return prefixSubscriptionID
+}
-func (id ClientID) IntID() int64 {
- return int64(id)
+func (id SubscriptionID) Raw() string {
+ return getRawData(prefixSubscriptionID, string(id))
+}
+
+func (id SubscriptionID) CheckString() string {
+ return getCheckString(prefixSubscriptionID, string(id))
+}
+
+func (id SubscriptionID) Regex() *regexp.Regexp {
+ return regexSubscriptionID
+}
+
+// ------------------------------------------------------------
+
+type ClientID string
+
+func NewClientID() ClientID {
+ return ClientID(generateID(prefixClientID))
+}
+
+func (id ClientID) Valid() error {
+ return validateID(prefixClientID, string(id))
}
func (id ClientID) String() string {
- return strconv.FormatInt(int64(id), 10)
+ return string(id)
}
-type RequestID int64
+func (id ClientID) Prefix() string {
+ return prefixClientID
+}
-func (id RequestID) IntID() int64 {
- return int64(id)
+func (id ClientID) Raw() string {
+ return getRawData(prefixClientID, string(id))
+}
+
+func (id ClientID) CheckString() string {
+ return getCheckString(prefixClientID, string(id))
+}
+
+func (id ClientID) Regex() *regexp.Regexp {
+ return regexClientID
+}
+
+// ------------------------------------------------------------
+
+type RequestID string
+
+func NewRequestID() RequestID {
+ return RequestID(generateID(prefixRequestID))
+}
+
+func (id RequestID) Valid() error {
+ return validateID(prefixRequestID, string(id))
}
func (id RequestID) String() string {
- return strconv.FormatInt(int64(id), 10)
+ return string(id)
+}
+
+func (id RequestID) Prefix() string {
+ return prefixRequestID
+}
+
+func (id RequestID) Raw() string {
+ return getRawData(prefixRequestID, string(id))
+}
+
+func (id RequestID) CheckString() string {
+ return getCheckString(prefixRequestID, string(id))
+}
+
+func (id RequestID) Regex() *regexp.Regexp {
+ return regexRequestID
}
diff --git a/scnserver/models/message.go b/scnserver/models/message.go
index 091a3fb..2095052 100644
--- a/scnserver/models/message.go
+++ b/scnserver/models/message.go
@@ -13,7 +13,7 @@ const (
)
type Message struct {
- SCNMessageID SCNMessageID
+ MessageID MessageID
SenderUserID UserID
OwnerUserID UserID
ChannelInternalName string
@@ -31,7 +31,7 @@ type Message struct {
func (m Message) FullJSON() MessageJSON {
return MessageJSON{
- SCNMessageID: m.SCNMessageID,
+ MessageID: m.MessageID,
SenderUserID: m.SenderUserID,
OwnerUserID: m.OwnerUserID,
ChannelInternalName: m.ChannelInternalName,
@@ -49,7 +49,7 @@ func (m Message) FullJSON() MessageJSON {
func (m Message) TrimmedJSON() MessageJSON {
return MessageJSON{
- SCNMessageID: m.SCNMessageID,
+ MessageID: m.MessageID,
SenderUserID: m.SenderUserID,
OwnerUserID: m.OwnerUserID,
ChannelInternalName: m.ChannelInternalName,
@@ -94,41 +94,41 @@ func (m Message) ShortContent() string {
}
type MessageJSON struct {
- SCNMessageID SCNMessageID `json:"scn_message_id"`
- SenderUserID UserID `json:"sender_user_id"`
- OwnerUserID UserID `json:"owner_user_id"`
- ChannelInternalName string `json:"channel_internal_name"`
- ChannelID ChannelID `json:"channel_id"`
- SenderName *string `json:"sender_name"`
- SenderIP string `json:"sender_ip"`
- Timestamp string `json:"timestamp"`
- Title string `json:"title"`
- Content *string `json:"content"`
- Priority int `json:"priority"`
- UserMessageID *string `json:"usr_message_id"`
- Trimmed bool `json:"trimmed"`
+ MessageID MessageID `json:"message_id"`
+ SenderUserID UserID `json:"sender_user_id"`
+ OwnerUserID UserID `json:"owner_user_id"`
+ ChannelInternalName string `json:"channel_internal_name"`
+ ChannelID ChannelID `json:"channel_id"`
+ SenderName *string `json:"sender_name"`
+ SenderIP string `json:"sender_ip"`
+ Timestamp string `json:"timestamp"`
+ Title string `json:"title"`
+ Content *string `json:"content"`
+ Priority int `json:"priority"`
+ UserMessageID *string `json:"usr_message_id"`
+ Trimmed bool `json:"trimmed"`
}
type MessageDB struct {
- SCNMessageID SCNMessageID `db:"scn_message_id"`
- SenderUserID UserID `db:"sender_user_id"`
- OwnerUserID UserID `db:"owner_user_id"`
- ChannelInternalName string `db:"channel_internal_name"`
- ChannelID ChannelID `db:"channel_id"`
- SenderName *string `db:"sender_name"`
- SenderIP string `db:"sender_ip"`
- TimestampReal int64 `db:"timestamp_real"`
- TimestampClient *int64 `db:"timestamp_client"`
- Title string `db:"title"`
- Content *string `db:"content"`
- Priority int `db:"priority"`
- UserMessageID *string `db:"usr_message_id"`
- Deleted int `db:"deleted"`
+ MessageID MessageID `db:"message_id"`
+ SenderUserID UserID `db:"sender_user_id"`
+ OwnerUserID UserID `db:"owner_user_id"`
+ ChannelInternalName string `db:"channel_internal_name"`
+ ChannelID ChannelID `db:"channel_id"`
+ SenderName *string `db:"sender_name"`
+ SenderIP string `db:"sender_ip"`
+ TimestampReal int64 `db:"timestamp_real"`
+ TimestampClient *int64 `db:"timestamp_client"`
+ Title string `db:"title"`
+ Content *string `db:"content"`
+ Priority int `db:"priority"`
+ UserMessageID *string `db:"usr_message_id"`
+ Deleted int `db:"deleted"`
}
func (m MessageDB) Model() Message {
return Message{
- SCNMessageID: m.SCNMessageID,
+ MessageID: m.MessageID,
SenderUserID: m.SenderUserID,
OwnerUserID: m.OwnerUserID,
ChannelInternalName: m.ChannelInternalName,
diff --git a/scnserver/models/messagefilter.go b/scnserver/models/messagefilter.go
index 654f26b..597252d 100644
--- a/scnserver/models/messagefilter.go
+++ b/scnserver/models/messagefilter.go
@@ -48,7 +48,7 @@ func (f MessageFilter) SQL() (string, string, sq.PP, error) {
joinClause += " LEFT JOIN subscriptions AS subs on messages.channel_id = subs.channel_id "
}
if f.SearchString != nil {
- joinClause += " JOIN messages_fts AS mfts on (mfts.rowid = messages.scn_message_id) "
+ joinClause += " JOIN messages_fts AS mfts on (mfts.rowid = messages.rowid) "
}
sqlClauses := make([]string, 0)
diff --git a/scnserver/push/firebase.go b/scnserver/push/firebase.go
index 16d532a..fb43c31 100644
--- a/scnserver/push/firebase.go
+++ b/scnserver/push/firebase.go
@@ -56,7 +56,7 @@ func (fb FirebaseConnector) SendNotification(ctx context.Context, client models.
jsonBody := gin.H{
"data": gin.H{
- "scn_msg_id": msg.SCNMessageID.String(),
+ "scn_msg_id": msg.MessageID.String(),
"usr_msg_id": langext.Coalesce(msg.UserMessageID, ""),
"client_id": client.ClientID.String(),
"timestamp": strconv.FormatInt(msg.Timestamp().Unix(), 10),
diff --git a/scnserver/swagger/swagger.json b/scnserver/swagger/swagger.json
index f1d5eaa..9b1548f 100644
--- a/scnserver/swagger/swagger.json
+++ b/scnserver/swagger/swagger.json
@@ -71,8 +71,8 @@
"in": "query"
},
{
- "type": "integer",
- "example": 7725,
+ "type": "string",
+ "example": "7725",
"name": "user_id",
"in": "query"
},
@@ -144,8 +144,8 @@
"in": "formData"
},
{
- "type": "integer",
- "example": 7725,
+ "type": "string",
+ "example": "7725",
"name": "user_id",
"in": "formData"
},
@@ -478,7 +478,7 @@
"parameters": [
{
"type": "integer",
- "description": "SCNMessageID",
+ "description": "MessageID",
"name": "mid",
"in": "path",
"required": true
@@ -527,7 +527,7 @@
"parameters": [
{
"type": "integer",
- "description": "SCNMessageID",
+ "description": "MessageID",
"name": "mid",
"in": "path",
"required": true
@@ -2093,8 +2093,8 @@
"in": "query"
},
{
- "type": "integer",
- "example": 7725,
+ "type": "string",
+ "example": "7725",
"name": "user_id",
"in": "query"
},
@@ -2166,8 +2166,8 @@
"in": "formData"
},
{
- "type": "integer",
- "example": 7725,
+ "type": "string",
+ "example": "7725",
"name": "user_id",
"in": "formData"
},
@@ -2414,13 +2414,13 @@
"type": "object",
"properties": {
"channel_id": {
- "type": "integer"
+ "type": "string"
},
"channel_internal_name": {
"type": "string"
},
"channel_owner_user_id": {
- "type": "integer"
+ "type": "string"
}
}
},
@@ -2686,8 +2686,8 @@
"example": "Hello World"
},
"user_id": {
- "type": "integer",
- "example": 7725
+ "type": "string",
+ "example": "7725"
},
"user_key": {
"type": "string",
@@ -2828,7 +2828,7 @@
"type": "integer"
},
"scn_msg_id": {
- "type": "integer"
+ "type": "string"
},
"success": {
"type": "boolean"
@@ -2842,7 +2842,7 @@
"type": "object",
"properties": {
"channel_id": {
- "type": "integer"
+ "type": "string"
},
"description_name": {
"type": "string"
@@ -2857,7 +2857,7 @@
"type": "integer"
},
"owner_user_id": {
- "type": "integer"
+ "type": "string"
},
"send_key": {
"description": "can be nil, depending on endpoint",
@@ -2888,7 +2888,7 @@
"type": "string"
},
"client_id": {
- "type": "integer"
+ "type": "string"
},
"fcm_token": {
"type": "string"
@@ -2900,7 +2900,7 @@
"type": "string"
},
"user_id": {
- "type": "integer"
+ "type": "string"
}
}
},
@@ -2934,7 +2934,7 @@
"type": "object",
"properties": {
"channel_id": {
- "type": "integer"
+ "type": "string"
},
"channel_internal_name": {
"type": "string"
@@ -2942,15 +2942,15 @@
"content": {
"type": "string"
},
+ "message_id": {
+ "type": "string"
+ },
"owner_user_id": {
- "type": "integer"
+ "type": "string"
},
"priority": {
"type": "integer"
},
- "scn_message_id": {
- "type": "integer"
- },
"sender_ip": {
"type": "string"
},
@@ -2958,7 +2958,7 @@
"type": "string"
},
"sender_user_id": {
- "type": "integer"
+ "type": "string"
},
"timestamp": {
"type": "string"
@@ -2978,22 +2978,22 @@
"type": "object",
"properties": {
"channel_id": {
- "type": "integer"
+ "type": "string"
},
"channel_internal_name": {
"type": "string"
},
"channel_owner_user_id": {
- "type": "integer"
+ "type": "string"
},
"confirmed": {
"type": "boolean"
},
"subscriber_user_id": {
- "type": "integer"
+ "type": "string"
},
"subscription_id": {
- "type": "integer"
+ "type": "string"
},
"timestamp_created": {
"type": "string"
@@ -3040,7 +3040,7 @@
"type": "string"
},
"user_id": {
- "type": "integer"
+ "type": "string"
},
"username": {
"type": "string"
@@ -3093,7 +3093,7 @@
"type": "string"
},
"user_id": {
- "type": "integer"
+ "type": "string"
},
"username": {
"type": "string"
diff --git a/scnserver/swagger/swagger.yaml b/scnserver/swagger/swagger.yaml
index e9fb753..36e4b37 100644
--- a/scnserver/swagger/swagger.yaml
+++ b/scnserver/swagger/swagger.yaml
@@ -57,11 +57,11 @@ definitions:
handler.CreateSubscription.body:
properties:
channel_id:
- type: integer
+ type: string
channel_internal_name:
type: string
channel_owner_user_id:
- type: integer
+ type: string
type: object
handler.CreateUser.body:
properties:
@@ -237,8 +237,8 @@ definitions:
example: Hello World
type: string
user_id:
- example: 7725
- type: integer
+ example: "7725"
+ type: string
user_key:
example: P3TNH8mvv14fm
type: string
@@ -330,7 +330,7 @@ definitions:
quota_max:
type: integer
scn_msg_id:
- type: integer
+ type: string
success:
type: boolean
suppress_send:
@@ -339,7 +339,7 @@ definitions:
models.ChannelWithSubscriptionJSON:
properties:
channel_id:
- type: integer
+ type: string
description_name:
type: string
display_name:
@@ -349,7 +349,7 @@ definitions:
messages_sent:
type: integer
owner_user_id:
- type: integer
+ type: string
send_key:
description: can be nil, depending on endpoint
type: string
@@ -370,7 +370,7 @@ definitions:
agent_version:
type: string
client_id:
- type: integer
+ type: string
fcm_token:
type: string
timestamp_created:
@@ -378,7 +378,7 @@ definitions:
type:
type: string
user_id:
- type: integer
+ type: string
type: object
models.CompatMessage:
properties:
@@ -400,23 +400,23 @@ definitions:
models.MessageJSON:
properties:
channel_id:
- type: integer
+ type: string
channel_internal_name:
type: string
content:
type: string
+ message_id:
+ type: string
owner_user_id:
- type: integer
+ type: string
priority:
type: integer
- scn_message_id:
- type: integer
sender_ip:
type: string
sender_name:
type: string
sender_user_id:
- type: integer
+ type: string
timestamp:
type: string
title:
@@ -429,17 +429,17 @@ definitions:
models.SubscriptionJSON:
properties:
channel_id:
- type: integer
+ type: string
channel_internal_name:
type: string
channel_owner_user_id:
- type: integer
+ type: string
confirmed:
type: boolean
subscriber_user_id:
- type: integer
+ type: string
subscription_id:
- type: integer
+ type: string
timestamp_created:
type: string
type: object
@@ -470,7 +470,7 @@ definitions:
timestamp_lastsent:
type: string
user_id:
- type: integer
+ type: string
username:
type: string
type: object
@@ -505,7 +505,7 @@ definitions:
timestamp_lastsent:
type: string
user_id:
- type: integer
+ type: string
username:
type: string
type: object
@@ -557,10 +557,10 @@ paths:
in: query
name: title
type: string
- - example: 7725
+ - example: "7725"
in: query
name: user_id
- type: integer
+ type: string
- example: P3TNH8mvv14fm
in: query
name: user_key
@@ -606,10 +606,10 @@ paths:
in: formData
name: title
type: string
- - example: 7725
+ - example: "7725"
in: formData
name: user_id
- type: integer
+ type: string
- example: P3TNH8mvv14fm
in: formData
name: user_key
@@ -836,7 +836,7 @@ paths:
ADMIN Key
operationId: api-messages-delete
parameters:
- - description: SCNMessageID
+ - description: MessageID
in: path
name: mid
required: true
@@ -872,7 +872,7 @@ paths:
The returned message is never trimmed
operationId: api-messages-get
parameters:
- - description: SCNMessageID
+ - description: MessageID
in: path
name: mid
required: true
@@ -1941,10 +1941,10 @@ paths:
in: query
name: title
type: string
- - example: 7725
+ - example: "7725"
in: query
name: user_id
- type: integer
+ type: string
- example: P3TNH8mvv14fm
in: query
name: user_key
@@ -1990,10 +1990,10 @@ paths:
in: formData
name: title
type: string
- - example: 7725
+ - example: "7725"
in: formData
name: user_id
- type: integer
+ type: string
- example: P3TNH8mvv14fm
in: formData
name: user_key
diff --git a/scnserver/test/channel_test.go b/scnserver/test/channel_test.go
index bfc8a9d..e7de047 100644
--- a/scnserver/test/channel_test.go
+++ b/scnserver/test/channel_test.go
@@ -21,7 +21,7 @@ func TestCreateChannel(t *testing.T) {
"fcm_token": "DUMMY_FCM",
})
- uid := int(r0["user_id"].(float64))
+ uid := r0["user_id"].(string)
admintok := r0["admin_key"].(string)
type chanlist struct {
@@ -29,28 +29,28 @@ func TestCreateChannel(t *testing.T) {
}
{
- clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid))
+ clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels", uid))
tt.AssertMappedSet(t, "channels", []string{}, clist.Channels, "display_name")
tt.AssertMappedSet(t, "channels", []string{}, clist.Channels, "internal_name")
}
- tt.RequestAuthPost[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid), gin.H{
+ tt.RequestAuthPost[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels", uid), gin.H{
"name": "test",
})
{
- clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid))
+ clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels", uid))
tt.AssertEqual(t, "chan.len", 1, len(clist.Channels))
tt.AssertMappedSet(t, "channels", []string{"test"}, clist.Channels, "display_name")
tt.AssertMappedSet(t, "channels", []string{"test"}, clist.Channels, "internal_name")
}
- tt.RequestAuthPost[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid), gin.H{
+ tt.RequestAuthPost[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels", uid), gin.H{
"name": "asdf",
})
{
- clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid))
+ clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels", uid))
tt.AssertMappedSet(t, "channels", []string{"asdf", "test"}, clist.Channels, "display_name")
tt.AssertMappedSet(t, "channels", []string{"asdf", "test"}, clist.Channels, "internal_name")
}
@@ -67,10 +67,10 @@ func TestCreateChannelNameTooLong(t *testing.T) {
"fcm_token": "DUMMY_FCM",
})
- uid := int(r0["user_id"].(float64))
+ uid := r0["user_id"].(string)
admintok := r0["admin_key"].(string)
- tt.RequestAuthPostShouldFail(t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid), gin.H{
+ tt.RequestAuthPostShouldFail(t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels", uid), gin.H{
"name": langext.StrRepeat("X", 121),
}, 400, apierr.CHANNEL_TOO_LONG)
}
@@ -86,7 +86,7 @@ func TestChannelNameNormalization(t *testing.T) {
"fcm_token": "DUMMY_FCM",
})
- uid := int(r0["user_id"].(float64))
+ uid := r0["user_id"].(string)
admintok := r0["admin_key"].(string)
type chanlist struct {
@@ -94,57 +94,57 @@ func TestChannelNameNormalization(t *testing.T) {
}
{
- clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid))
+ clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels", uid))
tt.AssertMappedSet(t, "channels", []string{}, clist.Channels, "display_name")
tt.AssertMappedSet(t, "channels", []string{}, clist.Channels, "internal_name")
}
- tt.RequestAuthPost[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid), gin.H{
+ tt.RequestAuthPost[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels", uid), gin.H{
"name": "tESt",
})
{
- clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid))
+ clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels", uid))
tt.AssertMappedSet(t, "channels", []string{"tESt"}, clist.Channels, "display_name")
tt.AssertMappedSet(t, "channels", []string{"test"}, clist.Channels, "internal_name")
}
- tt.RequestAuthPostShouldFail(t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid), gin.H{
+ tt.RequestAuthPostShouldFail(t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels", uid), gin.H{
"name": "test",
}, 409, apierr.CHANNEL_ALREADY_EXISTS)
- tt.RequestAuthPostShouldFail(t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid), gin.H{
+ tt.RequestAuthPostShouldFail(t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels", uid), gin.H{
"name": "TEST",
}, 409, apierr.CHANNEL_ALREADY_EXISTS)
- tt.RequestAuthPostShouldFail(t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid), gin.H{
+ tt.RequestAuthPostShouldFail(t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels", uid), gin.H{
"name": "Test",
}, 409, apierr.CHANNEL_ALREADY_EXISTS)
- tt.RequestAuthPostShouldFail(t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid), gin.H{
+ tt.RequestAuthPostShouldFail(t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels", uid), gin.H{
"name": "Test ",
}, 409, apierr.CHANNEL_ALREADY_EXISTS)
- tt.RequestAuthPostShouldFail(t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid), gin.H{
+ tt.RequestAuthPostShouldFail(t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels", uid), gin.H{
"name": " Test",
}, 409, apierr.CHANNEL_ALREADY_EXISTS)
- tt.RequestAuthPostShouldFail(t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid), gin.H{
+ tt.RequestAuthPostShouldFail(t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels", uid), gin.H{
"name": "\rTeSt\n",
}, 409, apierr.CHANNEL_ALREADY_EXISTS)
{
- clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid))
+ clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels", uid))
tt.AssertMappedSet(t, "channels", []string{"tESt"}, clist.Channels, "display_name")
tt.AssertMappedSet(t, "channels", []string{"test"}, clist.Channels, "internal_name")
}
- tt.RequestAuthPost[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid), gin.H{
+ tt.RequestAuthPost[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels", uid), gin.H{
"name": " WeiRD_[\uF5FF]\\stUFf\r\n\t ",
})
{
- clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid))
+ clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels", uid))
tt.AssertMappedSet(t, "channels", []string{"tESt", "WeiRD_[\uF5FF]\\stUFf"}, clist.Channels, "display_name")
tt.AssertMappedSet(t, "channels", []string{"test", "weird_[\uF5FF]\\stuff"}, clist.Channels, "internal_name")
}
@@ -181,7 +181,7 @@ func TestListChannelsDefault(t *testing.T) {
}
for k, v := range testdata {
- r0 := tt.RequestAuthGet[chanlist](t, data.User[k].AdminKey, baseUrl, fmt.Sprintf("/api/users/%d/channels", data.User[k].UID))
+ r0 := tt.RequestAuthGet[chanlist](t, data.User[k].AdminKey, baseUrl, fmt.Sprintf("/api/users/%s/channels", data.User[k].UID))
tt.AssertMappedSet(t, fmt.Sprintf("%d->chanlist", k), v, r0.Channels, "internal_name")
}
}
@@ -216,7 +216,7 @@ func TestListChannelsOwned(t *testing.T) {
}
for k, v := range testdata {
- r0 := tt.RequestAuthGet[chanlist](t, data.User[k].AdminKey, baseUrl, fmt.Sprintf("/api/users/%d/channels?selector=%s", data.User[k].UID, "owned"))
+ r0 := tt.RequestAuthGet[chanlist](t, data.User[k].AdminKey, baseUrl, fmt.Sprintf("/api/users/%s/channels?selector=%s", data.User[k].UID, "owned"))
tt.AssertMappedSet(t, fmt.Sprintf("%d->chanlist", k), v, r0.Channels, "internal_name")
}
}
@@ -251,7 +251,7 @@ func TestListChannelsSubscribedAny(t *testing.T) {
}
for k, v := range testdata {
- r0 := tt.RequestAuthGet[chanlist](t, data.User[k].AdminKey, baseUrl, fmt.Sprintf("/api/users/%d/channels?selector=%s", data.User[k].UID, "subscribed_any"))
+ r0 := tt.RequestAuthGet[chanlist](t, data.User[k].AdminKey, baseUrl, fmt.Sprintf("/api/users/%s/channels?selector=%s", data.User[k].UID, "subscribed_any"))
tt.AssertMappedSet(t, fmt.Sprintf("%d->chanlist", k), v, r0.Channels, "internal_name")
}
}
@@ -286,7 +286,7 @@ func TestListChannelsAllAny(t *testing.T) {
}
for k, v := range testdata {
- r0 := tt.RequestAuthGet[chanlist](t, data.User[k].AdminKey, baseUrl, fmt.Sprintf("/api/users/%d/channels?selector=%s", data.User[k].UID, "all_any"))
+ r0 := tt.RequestAuthGet[chanlist](t, data.User[k].AdminKey, baseUrl, fmt.Sprintf("/api/users/%s/channels?selector=%s", data.User[k].UID, "all_any"))
tt.AssertMappedSet(t, fmt.Sprintf("%d->chanlist", k), v, r0.Channels, "internal_name")
}
}
@@ -321,7 +321,7 @@ func TestListChannelsSubscribed(t *testing.T) {
}
for k, v := range testdata {
- r0 := tt.RequestAuthGet[chanlist](t, data.User[k].AdminKey, baseUrl, fmt.Sprintf("/api/users/%d/channels?selector=%s", data.User[k].UID, "subscribed"))
+ r0 := tt.RequestAuthGet[chanlist](t, data.User[k].AdminKey, baseUrl, fmt.Sprintf("/api/users/%s/channels?selector=%s", data.User[k].UID, "subscribed"))
tt.AssertMappedSet(t, fmt.Sprintf("%d->chanlist", k), v, r0.Channels, "internal_name")
}
}
@@ -356,7 +356,7 @@ func TestListChannelsAll(t *testing.T) {
}
for k, v := range testdata {
- r0 := tt.RequestAuthGet[chanlist](t, data.User[k].AdminKey, baseUrl, fmt.Sprintf("/api/users/%d/channels?selector=%s", data.User[k].UID, "all"))
+ r0 := tt.RequestAuthGet[chanlist](t, data.User[k].AdminKey, baseUrl, fmt.Sprintf("/api/users/%s/channels?selector=%s", data.User[k].UID, "all"))
tt.AssertMappedSet(t, fmt.Sprintf("%d->chanlist", k), v, r0.Channels, "internal_name")
}
}
@@ -372,7 +372,7 @@ func TestChannelUpdate(t *testing.T) {
"fcm_token": "DUMMY_FCM",
})
- uid := int(r0["user_id"].(float64))
+ uid := r0["user_id"].(string)
admintok := r0["admin_key"].(string)
type chanlist struct {
@@ -380,25 +380,25 @@ func TestChannelUpdate(t *testing.T) {
}
{
- clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid))
+ clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels", uid))
tt.AssertMappedSet(t, "channels", []string{}, clist.Channels, "display_name")
tt.AssertMappedSet(t, "channels", []string{}, clist.Channels, "internal_name")
}
- chan0 := tt.RequestAuthPost[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid), gin.H{
+ chan0 := tt.RequestAuthPost[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels", uid), gin.H{
"name": "server-alerts",
})
chanid := fmt.Sprintf("%v", chan0["channel_id"])
{
- clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid))
+ clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels", uid))
tt.AssertMappedSet(t, "channels", []string{"server-alerts"}, clist.Channels, "display_name")
tt.AssertMappedSet(t, "channels", []string{"server-alerts"}, clist.Channels, "internal_name")
tt.AssertEqual(t, "channels.descr", nil, clist.Channels[0]["description_name"])
}
{
- chan1 := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels/%s", uid, chanid))
+ chan1 := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels/%s", uid, chanid))
tt.AssertEqual(t, "channels.display_name", "server-alerts", chan1["display_name"])
tt.AssertEqual(t, "channels.internal_name", "server-alerts", chan1["internal_name"])
tt.AssertEqual(t, "channels.description_name", nil, chan1["description_name"])
@@ -408,12 +408,12 @@ func TestChannelUpdate(t *testing.T) {
// [1] update display_name
- tt.RequestAuthPatch[tt.Void](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels/%s", uid, chanid), gin.H{
+ tt.RequestAuthPatch[tt.Void](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels/%s", uid, chanid), gin.H{
"display_name": "SERVER-ALERTS",
})
{
- chan1 := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels/%s", uid, chanid))
+ chan1 := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels/%s", uid, chanid))
tt.AssertEqual(t, "channels.display_name", "SERVER-ALERTS", chan1["display_name"])
tt.AssertEqual(t, "channels.internal_name", "server-alerts", chan1["internal_name"])
tt.AssertEqual(t, "channels.description_name", nil, chan1["description_name"])
@@ -423,70 +423,70 @@ func TestChannelUpdate(t *testing.T) {
// [2] fail to update display_name
- tt.RequestAuthPatchShouldFail(t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels/%s", uid, chanid), gin.H{
+ tt.RequestAuthPatchShouldFail(t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels/%s", uid, chanid), gin.H{
"display_name": "SERVER-ALERTS2",
}, 400, apierr.CHANNEL_NAME_WOULD_CHANGE)
// [3] renew subscribe_key
- tt.RequestAuthPatch[tt.Void](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels/%s", uid, chanid), gin.H{
+ tt.RequestAuthPatch[tt.Void](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels/%s", uid, chanid), gin.H{
"subscribe_key": true,
})
{
- chan1 := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels/%s", uid, chanid))
+ chan1 := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels/%s", uid, chanid))
tt.AssertNotEqual(t, "channels.subscribe_key", chan0["subscribe_key"], chan1["subscribe_key"])
tt.AssertEqual(t, "channels.send_key", chan0["send_key"], chan1["send_key"])
}
// [4] renew send_key
- tt.RequestAuthPatch[tt.Void](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels/%s", uid, chanid), gin.H{
+ tt.RequestAuthPatch[tt.Void](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels/%s", uid, chanid), gin.H{
"send_key": true,
})
{
- chan1 := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels/%s", uid, chanid))
+ chan1 := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels/%s", uid, chanid))
tt.AssertNotEqual(t, "channels.subscribe_key", chan0["subscribe_key"], chan1["subscribe_key"])
tt.AssertNotEqual(t, "channels.send_key", chan0["send_key"], chan1["send_key"])
}
// [5] update description_name
- tt.RequestAuthPatch[tt.Void](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels/%s", uid, chanid), gin.H{
+ tt.RequestAuthPatch[tt.Void](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels/%s", uid, chanid), gin.H{
"description_name": "hello World",
})
{
- chan1 := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels/%s", uid, chanid))
+ chan1 := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels/%s", uid, chanid))
tt.AssertEqual(t, "channels.description_name", "hello World", chan1["description_name"])
}
// [6] update description_name
- tt.RequestAuthPatch[tt.Void](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels/%s", uid, chanid), gin.H{
+ tt.RequestAuthPatch[tt.Void](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels/%s", uid, chanid), gin.H{
"description_name": " AXXhello World9 ",
})
{
- chan1 := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels/%s", uid, chanid))
+ chan1 := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels/%s", uid, chanid))
tt.AssertEqual(t, "channels.description_name", "AXXhello World9", chan1["description_name"])
}
// [7] clear description_name
- tt.RequestAuthPatch[tt.Void](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels/%s", uid, chanid), gin.H{
+ tt.RequestAuthPatch[tt.Void](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels/%s", uid, chanid), gin.H{
"description_name": "",
})
{
- chan1 := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels/%s", uid, chanid))
+ chan1 := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels/%s", uid, chanid))
tt.AssertEqual(t, "channels.description_name", nil, chan1["description_name"])
}
// [8] fail to update description_name
- tt.RequestAuthPatchShouldFail(t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels/%s", uid, chanid), gin.H{
+ tt.RequestAuthPatchShouldFail(t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels/%s", uid, chanid), gin.H{
"description_name": strings.Repeat("0123456789", 48),
}, 400, apierr.CHANNEL_DESCRIPTION_TOO_LONG)
diff --git a/scnserver/test/compat_test.go b/scnserver/test/compat_test.go
index 4f7c2f3..3d8a4f9 100644
--- a/scnserver/test/compat_test.go
+++ b/scnserver/test/compat_test.go
@@ -1,3 +1,5 @@
package test
//TODO test compat methods
+
+//TODO also test compat_id mapping
diff --git a/scnserver/test/message_test.go b/scnserver/test/message_test.go
index 054f010..43825c1 100644
--- a/scnserver/test/message_test.go
+++ b/scnserver/test/message_test.go
@@ -2,6 +2,7 @@ package test
import (
"blackforestbytes.com/simplecloudnotifier/api/apierr"
+ "blackforestbytes.com/simplecloudnotifier/models"
tt "blackforestbytes.com/simplecloudnotifier/test/util"
"fmt"
"github.com/gin-gonic/gin"
@@ -48,7 +49,7 @@ func TestDeleteMessage(t *testing.T) {
"fcm_token": "DUMMY_FCM",
})
- uid := int(r0["user_id"].(float64))
+ uid := r0["user_id"].(string)
sendtok := r0["send_key"].(string)
admintok := r0["admin_key"].(string)
@@ -76,7 +77,7 @@ func TestDeleteMessageAndResendUsrMsgId(t *testing.T) {
"fcm_token": "DUMMY_FCM",
})
- uid := int(r0["user_id"].(float64))
+ uid := r0["user_id"].(string)
sendtok := r0["send_key"].(string)
admintok := r0["admin_key"].(string)
@@ -138,7 +139,18 @@ func TestGetMessageNotFound(t *testing.T) {
data := tt.InitDefaultData(t, ws)
- tt.RequestAuthGetShouldFail(t, data.User[0].AdminKey, baseUrl, "/api/messages/8963586", 404, apierr.MESSAGE_NOT_FOUND)
+ tt.RequestAuthGetShouldFail(t, data.User[0].AdminKey, baseUrl, "/api/messages/"+models.NewMessageID().String(), 404, apierr.MESSAGE_NOT_FOUND)
+}
+
+func TestGetMessageInvalidID(t *testing.T) {
+ ws, baseUrl, stop := tt.StartSimpleWebserver(t)
+ defer stop()
+
+ data := tt.InitDefaultData(t, ws)
+
+ tt.RequestAuthGetShouldFail(t, data.User[0].AdminKey, baseUrl, "/api/messages/"+models.NewUserID().String(), 400, apierr.BINDFAIL_URI_PARAM)
+
+ tt.RequestAuthGetShouldFail(t, data.User[0].AdminKey, baseUrl, "/api/messages/"+"asdfxxx", 400, apierr.BINDFAIL_URI_PARAM)
}
func TestGetMessageFull(t *testing.T) {
diff --git a/scnserver/test/send_test.go b/scnserver/test/send_test.go
index e0f464c..ccffdaa 100644
--- a/scnserver/test/send_test.go
+++ b/scnserver/test/send_test.go
@@ -2,6 +2,7 @@ package test
import (
"blackforestbytes.com/simplecloudnotifier/api/apierr"
+ "blackforestbytes.com/simplecloudnotifier/models"
"blackforestbytes.com/simplecloudnotifier/push"
tt "blackforestbytes.com/simplecloudnotifier/test/util"
"fmt"
@@ -24,7 +25,7 @@ func TestSendSimpleMessageJSON(t *testing.T) {
"fcm_token": "DUMMY_FCM",
})
- uid := int(r0["user_id"].(float64))
+ uid := r0["user_id"].(string)
admintok := r0["admin_key"].(string)
readtok := r0["read_key"].(string)
sendtok := r0["send_key"].(string)
@@ -50,7 +51,7 @@ func TestSendSimpleMessageJSON(t *testing.T) {
tt.AssertEqual(t, "messageCount", 1, len(pusher.Data))
tt.AssertStrRepEqual(t, "msg.title", "HelloWorld_001", pusher.Last().Message.Title)
tt.AssertStrRepEqual(t, "msg.content", nil, pusher.Last().Message.Content)
- tt.AssertStrRepEqual(t, "msg.scn_msg_id", msg1["scn_msg_id"], pusher.Last().Message.SCNMessageID)
+ tt.AssertStrRepEqual(t, "msg.scn_msg_id", msg1["scn_msg_id"], pusher.Last().Message.MessageID)
type mglist struct {
Messages []gin.H `json:"messages"`
@@ -77,16 +78,16 @@ func TestSendSimpleMessageQuery(t *testing.T) {
"fcm_token": "DUMMY_FCM",
})
- uid := int(r0["user_id"].(float64))
+ uid := r0["user_id"].(string)
admintok := r0["admin_key"].(string)
sendtok := r0["send_key"].(string)
- msg1 := tt.RequestPost[gin.H](t, baseUrl, fmt.Sprintf("/?user_id=%d&user_key=%s&title=%s", uid, sendtok, url.QueryEscape("Hello World 2134")), nil)
+ msg1 := tt.RequestPost[gin.H](t, baseUrl, fmt.Sprintf("/?user_id=%s&user_key=%s&title=%s", uid, sendtok, url.QueryEscape("Hello World 2134")), nil)
tt.AssertEqual(t, "messageCount", 1, len(pusher.Data))
tt.AssertStrRepEqual(t, "msg.title", "Hello World 2134", pusher.Last().Message.Title)
tt.AssertStrRepEqual(t, "msg.content", nil, pusher.Last().Message.Content)
- tt.AssertStrRepEqual(t, "msg.scn_msg_id", msg1["scn_msg_id"], pusher.Last().Message.SCNMessageID)
+ tt.AssertStrRepEqual(t, "msg.scn_msg_id", msg1["scn_msg_id"], pusher.Last().Message.MessageID)
type mglist struct {
Messages []gin.H `json:"messages"`
@@ -113,20 +114,20 @@ func TestSendSimpleMessageForm(t *testing.T) {
"fcm_token": "DUMMY_FCM",
})
- uid := int(r0["user_id"].(float64))
+ uid := r0["user_id"].(string)
admintok := r0["admin_key"].(string)
sendtok := r0["send_key"].(string)
msg1 := tt.RequestPost[gin.H](t, baseUrl, "/", tt.FormData{
"user_key": sendtok,
- "user_id": fmt.Sprintf("%d", uid),
+ "user_id": uid,
"title": "Hello World 9999 [$$$]",
})
tt.AssertEqual(t, "messageCount", 1, len(pusher.Data))
tt.AssertStrRepEqual(t, "msg.title", "Hello World 9999 [$$$]", pusher.Last().Message.Title)
tt.AssertStrRepEqual(t, "msg.content", nil, pusher.Last().Message.Content)
- tt.AssertStrRepEqual(t, "msg.scn_msg_id", msg1["scn_msg_id"], pusher.Last().Message.SCNMessageID)
+ tt.AssertStrRepEqual(t, "msg.scn_msg_id", msg1["scn_msg_id"], pusher.Last().Message.MessageID)
type mglist struct {
Messages []gin.H `json:"messages"`
@@ -153,10 +154,10 @@ func TestSendSimpleMessageFormAndQuery(t *testing.T) {
"fcm_token": "DUMMY_FCM",
})
- uid := int(r0["user_id"].(float64))
+ uid := r0["user_id"].(string)
sendtok := r0["send_key"].(string)
- msg1 := tt.RequestPost[gin.H](t, baseUrl, fmt.Sprintf("/?user_id=%d&user_key=%s&title=%s", uid, sendtok, url.QueryEscape("1111111")), tt.FormData{
+ msg1 := tt.RequestPost[gin.H](t, baseUrl, fmt.Sprintf("/?user_id=%s&user_key=%s&title=%s", uid, sendtok, url.QueryEscape("1111111")), tt.FormData{
"user_key": "ERR",
"user_id": "999999",
"title": "2222222",
@@ -164,7 +165,7 @@ func TestSendSimpleMessageFormAndQuery(t *testing.T) {
tt.AssertEqual(t, "messageCount", 1, len(pusher.Data))
tt.AssertStrRepEqual(t, "msg.title", "1111111", pusher.Last().Message.Title)
- tt.AssertStrRepEqual(t, "msg.scn_msg_id", msg1["scn_msg_id"], pusher.Last().Message.SCNMessageID)
+ tt.AssertStrRepEqual(t, "msg.scn_msg_id", msg1["scn_msg_id"], pusher.Last().Message.MessageID)
}
func TestSendSimpleMessageJSONAndQuery(t *testing.T) {
@@ -180,18 +181,19 @@ func TestSendSimpleMessageJSONAndQuery(t *testing.T) {
"fcm_token": "DUMMY_FCM",
})
- uid := int(r0["user_id"].(float64))
+ uid := r0["user_id"].(string)
sendtok := r0["send_key"].(string)
- msg1 := tt.RequestPost[gin.H](t, baseUrl, fmt.Sprintf("/?user_id=%d&user_key=%s&title=%s", uid, sendtok, url.QueryEscape("1111111")), gin.H{
+ // query overwrite body
+ msg1 := tt.RequestPost[gin.H](t, baseUrl, fmt.Sprintf("/?user_id=%s&user_key=%s&title=%s", uid, sendtok, url.QueryEscape("1111111")), gin.H{
"user_key": "ERR",
- "user_id": 999999,
+ "user_id": models.NewUserID(),
"title": "2222222",
})
tt.AssertEqual(t, "messageCount", 1, len(pusher.Data))
tt.AssertStrRepEqual(t, "msg.title", "1111111", pusher.Last().Message.Title)
- tt.AssertStrRepEqual(t, "msg.scn_msg_id", msg1["scn_msg_id"], pusher.Last().Message.SCNMessageID)
+ tt.AssertStrRepEqual(t, "msg.scn_msg_id", msg1["scn_msg_id"], pusher.Last().Message.MessageID)
}
func TestSendSimpleMessageAlt1(t *testing.T) {
@@ -207,7 +209,7 @@ func TestSendSimpleMessageAlt1(t *testing.T) {
"fcm_token": "DUMMY_FCM",
})
- uid := int(r0["user_id"].(float64))
+ uid := r0["user_id"].(string)
admintok := r0["admin_key"].(string)
readtok := r0["read_key"].(string)
sendtok := r0["send_key"].(string)
@@ -227,7 +229,7 @@ func TestSendSimpleMessageAlt1(t *testing.T) {
tt.AssertEqual(t, "messageCount", 1, len(pusher.Data))
tt.AssertStrRepEqual(t, "msg.title", "HelloWorld_001", pusher.Last().Message.Title)
tt.AssertStrRepEqual(t, "msg.content", nil, pusher.Last().Message.Content)
- tt.AssertStrRepEqual(t, "msg.scn_msg_id", msg1["scn_msg_id"], pusher.Last().Message.SCNMessageID)
+ tt.AssertStrRepEqual(t, "msg.scn_msg_id", msg1["scn_msg_id"], pusher.Last().Message.MessageID)
type mglist struct {
Messages []gin.H `json:"messages"`
@@ -254,7 +256,7 @@ func TestSendContentMessage(t *testing.T) {
"fcm_token": "DUMMY_FCM",
})
- uid := int(r0["user_id"].(float64))
+ uid := r0["user_id"].(string)
admintok := r0["admin_key"].(string)
sendtok := r0["send_key"].(string)
@@ -268,7 +270,7 @@ func TestSendContentMessage(t *testing.T) {
tt.AssertEqual(t, "messageCount", 1, len(pusher.Data))
tt.AssertStrRepEqual(t, "msg.title", "HelloWorld_042", pusher.Last().Message.Title)
tt.AssertStrRepEqual(t, "msg.content", "I am Content\nasdf", pusher.Last().Message.Content)
- tt.AssertStrRepEqual(t, "msg.scn_msg_id", msg1["scn_msg_id"], pusher.Last().Message.SCNMessageID)
+ tt.AssertStrRepEqual(t, "msg.scn_msg_id", msg1["scn_msg_id"], pusher.Last().Message.MessageID)
type mglist struct {
Messages []gin.H `json:"messages"`
@@ -299,7 +301,7 @@ func TestSendWithSendername(t *testing.T) {
"fcm_token": "DUMMY_FCM",
})
- uid := int(r0["user_id"].(float64))
+ uid := r0["user_id"].(string)
sendtok := r0["send_key"].(string)
admintok := r0["admin_key"].(string)
@@ -315,7 +317,7 @@ func TestSendWithSendername(t *testing.T) {
tt.AssertStrRepEqual(t, "msg.title", "HelloWorld_xyz", pusher.Last().Message.Title)
tt.AssertStrRepEqual(t, "msg.content", "Unicode: 日本 - yäy\000\n\t\x00...", pusher.Last().Message.Content)
tt.AssertStrRepEqual(t, "msg.SenderName", "localhorst", pusher.Last().Message.SenderName)
- tt.AssertStrRepEqual(t, "msg.scn_msg_id", msg1["scn_msg_id"], pusher.Last().Message.SCNMessageID)
+ tt.AssertStrRepEqual(t, "msg.scn_msg_id", msg1["scn_msg_id"], pusher.Last().Message.MessageID)
type mglist struct {
Messages []gin.H `json:"messages"`
@@ -348,7 +350,7 @@ func TestSendLongContent(t *testing.T) {
"fcm_token": "DUMMY_FCM",
})
- uid := int(r0["user_id"].(float64))
+ uid := r0["user_id"].(string)
admintok := r0["admin_key"].(string)
sendtok := r0["send_key"].(string)
@@ -367,7 +369,7 @@ func TestSendLongContent(t *testing.T) {
tt.AssertEqual(t, "messageCount", 1, len(pusher.Data))
tt.AssertStrRepEqual(t, "msg.title", "HelloWorld_042", pusher.Last().Message.Title)
tt.AssertStrRepEqual(t, "msg.content", longContent, pusher.Last().Message.Content)
- tt.AssertStrRepEqual(t, "msg.scn_msg_id", msg1["scn_msg_id"], pusher.Last().Message.SCNMessageID)
+ tt.AssertStrRepEqual(t, "msg.scn_msg_id", msg1["scn_msg_id"], pusher.Last().Message.MessageID)
type mglist struct {
Messages []gin.H `json:"messages"`
@@ -405,7 +407,7 @@ func TestSendTooLongContent(t *testing.T) {
"fcm_token": "DUMMY_FCM",
})
- uid := int(r0["user_id"].(float64))
+ uid := r0["user_id"].(string)
sendtok := r0["send_key"].(string)
longContent := ""
@@ -433,7 +435,7 @@ func TestSendLongContentPro(t *testing.T) {
"pro_token": "ANDROID|v2|PURCHASED:DUMMY_TOK_XX",
})
- uid := int(r0["user_id"].(float64))
+ uid := r0["user_id"].(string)
sendtok := r0["send_key"].(string)
{
@@ -519,7 +521,7 @@ func TestSendTooLongTitle(t *testing.T) {
"fcm_token": "DUMMY_FCM",
})
- uid := int(r0["user_id"].(float64))
+ uid := r0["user_id"].(string)
sendtok := r0["send_key"].(string)
tt.RequestPostShouldFail(t, baseUrl, "/", gin.H{
@@ -542,7 +544,7 @@ func TestSendIdempotent(t *testing.T) {
"fcm_token": "DUMMY_FCM",
})
- uid := int(r0["user_id"].(float64))
+ uid := r0["user_id"].(string)
readtok := r0["admin_key"].(string)
sendtok := r0["send_key"].(string)
@@ -555,7 +557,7 @@ func TestSendIdempotent(t *testing.T) {
})
tt.AssertEqual(t, "messageCount", 1, len(pusher.Data))
- tt.AssertStrRepEqual(t, "msg.scn_msg_id", msg1["scn_msg_id"], pusher.Last().Message.SCNMessageID)
+ tt.AssertStrRepEqual(t, "msg.scn_msg_id", msg1["scn_msg_id"], pusher.Last().Message.MessageID)
tt.AssertStrRepEqual(t, "msg.suppress_send", msg1["suppress_send"], false)
tt.AssertStrRepEqual(t, "msg.msg_id", "c0235a49-dabc-4cdc-a0ce-453966e0c2d5", pusher.Last().Message.UserMessageID)
tt.AssertStrRepEqual(t, "msg.title", "Hello SCN", pusher.Last().Message.Title)
@@ -578,7 +580,7 @@ func TestSendIdempotent(t *testing.T) {
tt.AssertEqual(t, "messageCount", 1, len(pusher.Data))
tt.AssertStrRepEqual(t, "msg.scn_msg_id", msg1["scn_msg_id"], msg2["scn_msg_id"])
- tt.AssertStrRepEqual(t, "msg.scn_msg_id", msg2["scn_msg_id"], pusher.Last().Message.SCNMessageID)
+ tt.AssertStrRepEqual(t, "msg.scn_msg_id", msg2["scn_msg_id"], pusher.Last().Message.MessageID)
tt.AssertStrRepEqual(t, "msg.suppress_send", msg2["suppress_send"], true)
tt.AssertStrRepEqual(t, "msg.msg_id", "c0235a49-dabc-4cdc-a0ce-453966e0c2d5", pusher.Last().Message.UserMessageID)
tt.AssertStrRepEqual(t, "msg.title", "Hello SCN", pusher.Last().Message.Title)
@@ -620,7 +622,7 @@ func TestSendWithPriority(t *testing.T) {
"fcm_token": "DUMMY_FCM",
})
- uid := int(r0["user_id"].(float64))
+ uid := r0["user_id"].(string)
sendtok := r0["send_key"].(string)
admintok := r0["admin_key"].(string)
@@ -713,7 +715,7 @@ func TestSendInvalidPriority(t *testing.T) {
"fcm_token": "DUMMY_FCM",
})
- uid := int(r0["user_id"].(float64))
+ uid := r0["user_id"].(string)
sendtok := r0["send_key"].(string)
admintok := r0["admin_key"].(string)
@@ -765,11 +767,9 @@ func TestSendInvalidPriority(t *testing.T) {
"priority": 9999,
}, 400, apierr.INVALID_PRIO)
- struid := fmt.Sprintf("%d", uid)
-
tt.RequestPostShouldFail(t, baseUrl, "/", tt.FormData{
"user_key": sendtok,
- "user_id": struid,
+ "user_id": uid,
"title": "(title)",
"content": "(content)",
"priority": "-1",
@@ -777,7 +777,7 @@ func TestSendInvalidPriority(t *testing.T) {
tt.RequestPostShouldFail(t, baseUrl, "/", tt.FormData{
"user_key": sendtok,
- "user_id": struid,
+ "user_id": uid,
"title": "(title)",
"content": "(content)",
"priority": "4",
@@ -785,7 +785,7 @@ func TestSendInvalidPriority(t *testing.T) {
tt.RequestPostShouldFail(t, baseUrl, "/", tt.FormData{
"user_key": sendtok,
- "user_id": struid,
+ "user_id": uid,
"title": "(title)",
"content": "(content)",
"priority": "9999",
@@ -793,7 +793,7 @@ func TestSendInvalidPriority(t *testing.T) {
tt.RequestPostShouldFail(t, baseUrl, "/", tt.FormData{
"user_key": admintok,
- "user_id": struid,
+ "user_id": uid,
"title": "(title)",
"content": "(content)",
"priority": "-1",
@@ -801,7 +801,7 @@ func TestSendInvalidPriority(t *testing.T) {
tt.RequestPostShouldFail(t, baseUrl, "/", tt.FormData{
"user_key": admintok,
- "user_id": struid,
+ "user_id": uid,
"title": "(title)",
"content": "(content)",
"priority": "4",
@@ -809,7 +809,7 @@ func TestSendInvalidPriority(t *testing.T) {
tt.RequestPostShouldFail(t, baseUrl, "/", tt.FormData{
"user_key": admintok,
- "user_id": struid,
+ "user_id": uid,
"title": "(title)",
"content": "(content)",
"priority": "9999",
@@ -831,7 +831,7 @@ func TestSendWithTimestamp(t *testing.T) {
"fcm_token": "DUMMY_FCM",
})
- uid := int(r0["user_id"].(float64))
+ uid := r0["user_id"].(string)
sendtok := r0["send_key"].(string)
admintok := r0["admin_key"].(string)
@@ -839,7 +839,7 @@ func TestSendWithTimestamp(t *testing.T) {
msg1 := tt.RequestPost[gin.H](t, baseUrl, "/", tt.FormData{
"user_key": sendtok,
- "user_id": fmt.Sprintf("%d", uid),
+ "user_id": fmt.Sprintf("%s", uid),
"title": "TTT",
"timestamp": fmt.Sprintf("%d", ts),
})
@@ -849,7 +849,7 @@ func TestSendWithTimestamp(t *testing.T) {
tt.AssertStrRepEqual(t, "msg.TimestampClient", ts, pusher.Last().Message.TimestampClient.Unix())
tt.AssertStrRepEqual(t, "msg.Timestamp", ts, pusher.Last().Message.Timestamp().Unix())
tt.AssertNotStrRepEqual(t, "msg.ts", pusher.Last().Message.TimestampClient, pusher.Last().Message.TimestampReal)
- tt.AssertStrRepEqual(t, "msg.scn_msg_id", msg1["scn_msg_id"], pusher.Last().Message.SCNMessageID)
+ tt.AssertStrRepEqual(t, "msg.scn_msg_id", msg1["scn_msg_id"], pusher.Last().Message.MessageID)
type mglist struct {
Messages []gin.H `json:"messages"`
@@ -888,33 +888,33 @@ func TestSendInvalidTimestamp(t *testing.T) {
"fcm_token": "DUMMY_FCM",
})
- uid := int(r0["user_id"].(float64))
+ uid := r0["user_id"].(string)
sendtok := r0["send_key"].(string)
tt.RequestPostShouldFail(t, baseUrl, "/", tt.FormData{
"user_key": sendtok,
- "user_id": fmt.Sprintf("%d", uid),
+ "user_id": fmt.Sprintf("%s", uid),
"title": "TTT",
"timestamp": "-10000",
}, 400, apierr.TIMESTAMP_OUT_OF_RANGE)
tt.RequestPostShouldFail(t, baseUrl, "/", tt.FormData{
"user_key": sendtok,
- "user_id": fmt.Sprintf("%d", uid),
+ "user_id": fmt.Sprintf("%s", uid),
"title": "TTT",
"timestamp": "0",
}, 400, apierr.TIMESTAMP_OUT_OF_RANGE)
tt.RequestPostShouldFail(t, baseUrl, "/", tt.FormData{
"user_key": sendtok,
- "user_id": fmt.Sprintf("%d", uid),
+ "user_id": fmt.Sprintf("%s", uid),
"title": "TTT",
"timestamp": fmt.Sprintf("%d", time.Now().Unix()-int64(25*time.Hour.Seconds())),
}, 400, apierr.TIMESTAMP_OUT_OF_RANGE)
tt.RequestPostShouldFail(t, baseUrl, "/", tt.FormData{
"user_key": sendtok,
- "user_id": fmt.Sprintf("%d", uid),
+ "user_id": fmt.Sprintf("%s", uid),
"title": "TTT",
"timestamp": fmt.Sprintf("%d", time.Now().Unix()+int64(25*time.Hour.Seconds())),
}, 400, apierr.TIMESTAMP_OUT_OF_RANGE)
@@ -947,28 +947,28 @@ func TestSendInvalidTimestamp(t *testing.T) {
"timestamp": time.Now().Unix() + int64(25*time.Hour.Seconds()),
}, 400, apierr.TIMESTAMP_OUT_OF_RANGE)
- tt.RequestPostShouldFail(t, baseUrl, fmt.Sprintf("/?user_key=%s&user_id=%d&title=%s×tamp=%d",
+ tt.RequestPostShouldFail(t, baseUrl, fmt.Sprintf("/?user_key=%s&user_id=%s&title=%s×tamp=%d",
sendtok,
uid,
"TTT",
-10000,
), nil, 400, apierr.TIMESTAMP_OUT_OF_RANGE)
- tt.RequestPostShouldFail(t, baseUrl, fmt.Sprintf("/?user_key=%s&user_id=%d&title=%s×tamp=%d",
+ tt.RequestPostShouldFail(t, baseUrl, fmt.Sprintf("/?user_key=%s&user_id=%s&title=%s×tamp=%d",
sendtok,
uid,
"TTT",
0,
), nil, 400, apierr.TIMESTAMP_OUT_OF_RANGE)
- tt.RequestPostShouldFail(t, baseUrl, fmt.Sprintf("/?user_key=%s&user_id=%d&title=%s×tamp=%d",
+ tt.RequestPostShouldFail(t, baseUrl, fmt.Sprintf("/?user_key=%s&user_id=%s&title=%s×tamp=%d",
sendtok,
uid,
"TTT",
time.Now().Unix()-int64(25*time.Hour.Seconds()),
), nil, 400, apierr.TIMESTAMP_OUT_OF_RANGE)
- tt.RequestPostShouldFail(t, baseUrl, fmt.Sprintf("/?user_key=%s&user_id=%d&title=%s×tamp=%d",
+ tt.RequestPostShouldFail(t, baseUrl, fmt.Sprintf("/?user_key=%s&user_id=%s&title=%s×tamp=%d",
sendtok,
uid,
"TTT",
@@ -978,40 +978,34 @@ func TestSendInvalidTimestamp(t *testing.T) {
tt.AssertEqual(t, "messageCount", 0, len(pusher.Data))
}
-func TestSendCompat(t *testing.T) {
+func TestSendCompatWithOldUser(t *testing.T) {
ws, baseUrl, stop := tt.StartSimpleWebserver(t)
defer stop()
pusher := ws.Pusher.(*push.TestSink)
- r0 := tt.RequestPost[gin.H](t, baseUrl, "/api/users", gin.H{
- "agent_model": "DUMMY_PHONE",
- "agent_version": "4X",
- "client_type": "ANDROID",
- "fcm_token": "DUMMY_FCM",
- })
+ r0 := tt.RequestGet[gin.H](t, baseUrl, "/api/register.php?fcm_token=DUMMY_FCM&pro=0&pro_token=")
- uid := int(r0["user_id"].(float64))
- admintok := r0["admin_key"].(string)
- readtok := r0["read_key"].(string)
- sendtok := r0["send_key"].(string)
+ uidold := int64(r0["user_id"].(float64))
+ admintok := r0["user_key"].(string)
msg1 := tt.RequestPost[gin.H](t, baseUrl, "/send.php", tt.FormData{
- "user_key": sendtok,
- "user_id": fmt.Sprintf("%d", uid),
+ "user_key": admintok,
+ "user_id": fmt.Sprintf("%d", uidold),
"title": "HelloWorld_001",
})
+ // does not allow json - only form & query
tt.RequestPostShouldFail(t, baseUrl, "/send.php", gin.H{
- "user_key": readtok,
- "user_id": uid,
+ "user_key": admintok,
+ "user_id": uidold,
"title": "HelloWorld_001",
- }, 0, 0)
+ }, 400, 0)
tt.AssertEqual(t, "messageCount", 1, len(pusher.Data))
tt.AssertStrRepEqual(t, "msg.title", "HelloWorld_001", pusher.Last().Message.Title)
tt.AssertStrRepEqual(t, "msg.content", nil, pusher.Last().Message.Content)
- tt.AssertStrRepEqual(t, "msg.scn_msg_id", msg1["scn_msg_id"], pusher.Last().Message.SCNMessageID)
+ tt.AssertStrRepEqual(t, "msg.scn_msg_id", msg1["scn_msg_id"], pusher.Last().Message.MessageID)
type mglist struct {
Messages []gin.H `json:"messages"`
@@ -1024,12 +1018,103 @@ func TestSendCompat(t *testing.T) {
tt.AssertStrRepEqual(t, "msg.title", "HelloWorld_001", msg1Get["title"])
tt.AssertStrRepEqual(t, "msg.channel_internal_name", "main", msg1Get["channel_internal_name"])
- msg2 := tt.RequestPost[gin.H](t, baseUrl, fmt.Sprintf("/send.php?user_key=%s&user_id=%d&title=%s", sendtok, uid, "HelloWorld_002"), nil)
+ msg2 := tt.RequestPost[gin.H](t, baseUrl, fmt.Sprintf("/send.php?user_key=%s&user_id=%d&title=%s", admintok, uidold, "HelloWorld_002"), nil)
tt.AssertEqual(t, "messageCount", 2, len(pusher.Data))
tt.AssertStrRepEqual(t, "msg.title", "HelloWorld_002", pusher.Last().Message.Title)
tt.AssertStrRepEqual(t, "msg.content", nil, pusher.Last().Message.Content)
- tt.AssertStrRepEqual(t, "msg.scn_msg_id", msg2["scn_msg_id"], pusher.Last().Message.SCNMessageID)
+ tt.AssertStrRepEqual(t, "msg.scn_msg_id", msg2["scn_msg_id"], pusher.Last().Message.MessageID)
+
+ tt.RequestAuthGet[gin.H](t, admintok, baseUrl, "/api/messages/"+fmt.Sprintf("%v", msg2["scn_msg_id"]))
+
+ content3 := "039c1817-76ee-44ab-972a-4cec0a15a791\n" +
+ "046f59ea-9a49-4060-93e6-8a4e14134faf\n" +
+ "ab566fbe-9020-41b6-afa6-94f3d8d7c7b4\n" +
+ "d52e5f7d-26a8-45b9-befc-da44a3f112da\n" +
+ "d19fae55-d52a-4753-b9f1-66a935d68b1e\n" +
+ "99a4099d-44d5-497a-a69b-18e277400d6e\n" +
+ "a55757aa-afaa-420e-afaf-f3951e9e2434\n" +
+ "ee58f5fc-b384-49f4-bc2c-c5b3c7bd54b7\n" +
+ "5a7008d9-dd15-406a-83d1-fd6209c56141\n"
+ ts3 := time.Now().Unix() - int64(time.Hour.Seconds())
+
+ msg3 := tt.RequestPost[gin.H](t, baseUrl, "/send.php", tt.FormData{
+ "user_key": admintok,
+ "user_id": fmt.Sprintf("%d", uidold),
+ "title": "HelloWorld_003",
+ "content": content3,
+ "priority": "2",
+ "msg_id": "8a2c7e92-86f3-4d69-897a-571286954030",
+ "timestamp": fmt.Sprintf("%d", ts3),
+ })
+
+ tt.RequestAuthGet[gin.H](t, admintok, baseUrl, "/api/messages/"+fmt.Sprintf("%v", msg3["scn_msg_id"]))
+
+ tt.AssertEqual(t, "messageCount", 3, len(pusher.Data))
+ tt.AssertStrRepEqual(t, "msg.Title", "HelloWorld_003", pusher.Last().Message.Title)
+ tt.AssertStrRepEqual(t, "msg.Content", content3, pusher.Last().Message.Content)
+ tt.AssertStrRepEqual(t, "msg.MessageID", msg3["scn_msg_id"], pusher.Last().Message.MessageID)
+ tt.AssertStrRepEqual(t, "msg.Priority", 2, pusher.Last().Message.Priority)
+ tt.AssertStrRepEqual(t, "msg.UserMessageID", "8a2c7e92-86f3-4d69-897a-571286954030", pusher.Last().Message.UserMessageID)
+ tt.AssertStrRepEqual(t, "msg.UserMessageID", ts3, pusher.Last().Message.Timestamp().Unix())
+
+}
+
+func TestSendCompatWithNewUser(t *testing.T) {
+ ws, baseUrl, stop := tt.StartSimpleWebserver(t)
+ defer stop()
+
+ pusher := ws.Pusher.(*push.TestSink)
+
+ r0 := tt.RequestPost[gin.H](t, baseUrl, "/api/users", gin.H{
+ "agent_model": "DUMMY_PHONE",
+ "agent_version": "4X",
+ "client_type": "ANDROID",
+ "fcm_token": "DUMMY_FCM",
+ })
+
+ uid := r0["user_id"].(string)
+ admintok := r0["admin_key"].(string)
+ readtok := r0["read_key"].(string)
+ sendtok := r0["send_key"].(string)
+
+ uidold := tt.CreateCompatID(t, ws, "userid", uid)
+
+ msg1 := tt.RequestPost[gin.H](t, baseUrl, "/send.php", tt.FormData{
+ "user_key": sendtok,
+ "user_id": fmt.Sprintf("%d", uidold),
+ "title": "HelloWorld_001",
+ })
+
+ // does not allow json - only form & query
+ tt.RequestPostShouldFail(t, baseUrl, "/send.php", gin.H{
+ "user_key": readtok,
+ "user_id": uidold,
+ "title": "HelloWorld_001",
+ }, 400, 0)
+
+ tt.AssertEqual(t, "messageCount", 1, len(pusher.Data))
+ tt.AssertStrRepEqual(t, "msg.title", "HelloWorld_001", pusher.Last().Message.Title)
+ tt.AssertStrRepEqual(t, "msg.content", nil, pusher.Last().Message.Content)
+ tt.AssertStrRepEqual(t, "msg.scn_msg_id", msg1["scn_msg_id"], pusher.Last().Message.MessageID)
+
+ type mglist struct {
+ Messages []gin.H `json:"messages"`
+ }
+
+ msgList1 := tt.RequestAuthGet[mglist](t, admintok, baseUrl, "/api/messages")
+ tt.AssertEqual(t, "len(messages)", 1, len(msgList1.Messages))
+
+ msg1Get := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, "/api/messages/"+fmt.Sprintf("%v", msg1["scn_msg_id"]))
+ tt.AssertStrRepEqual(t, "msg.title", "HelloWorld_001", msg1Get["title"])
+ tt.AssertStrRepEqual(t, "msg.channel_internal_name", "main", msg1Get["channel_internal_name"])
+
+ msg2 := tt.RequestPost[gin.H](t, baseUrl, fmt.Sprintf("/send.php?user_key=%s&user_id=%d&title=%s", sendtok, uidold, "HelloWorld_002"), nil)
+
+ tt.AssertEqual(t, "messageCount", 2, len(pusher.Data))
+ tt.AssertStrRepEqual(t, "msg.title", "HelloWorld_002", pusher.Last().Message.Title)
+ tt.AssertStrRepEqual(t, "msg.content", nil, pusher.Last().Message.Content)
+ tt.AssertStrRepEqual(t, "msg.scn_msg_id", msg2["scn_msg_id"], pusher.Last().Message.MessageID)
tt.RequestAuthGet[gin.H](t, admintok, baseUrl, "/api/messages/"+fmt.Sprintf("%v", msg2["scn_msg_id"]))
@@ -1046,7 +1131,7 @@ func TestSendCompat(t *testing.T) {
msg3 := tt.RequestPost[gin.H](t, baseUrl, "/send.php", tt.FormData{
"user_key": sendtok,
- "user_id": fmt.Sprintf("%d", uid),
+ "user_id": fmt.Sprintf("%d", uidold),
"title": "HelloWorld_003",
"content": content3,
"priority": "2",
@@ -1059,7 +1144,7 @@ func TestSendCompat(t *testing.T) {
tt.AssertEqual(t, "messageCount", 3, len(pusher.Data))
tt.AssertStrRepEqual(t, "msg.Title", "HelloWorld_003", pusher.Last().Message.Title)
tt.AssertStrRepEqual(t, "msg.Content", content3, pusher.Last().Message.Content)
- tt.AssertStrRepEqual(t, "msg.SCNMessageID", msg3["scn_msg_id"], pusher.Last().Message.SCNMessageID)
+ tt.AssertStrRepEqual(t, "msg.MessageID", msg3["scn_msg_id"], pusher.Last().Message.MessageID)
tt.AssertStrRepEqual(t, "msg.Priority", 2, pusher.Last().Message.Priority)
tt.AssertStrRepEqual(t, "msg.UserMessageID", "8a2c7e92-86f3-4d69-897a-571286954030", pusher.Last().Message.UserMessageID)
tt.AssertStrRepEqual(t, "msg.UserMessageID", ts3, pusher.Last().Message.Timestamp().Unix())
@@ -1077,7 +1162,7 @@ func TestSendToNewChannel(t *testing.T) {
"fcm_token": "DUMMY_FCM",
})
- uid := int(r0["user_id"].(float64))
+ uid := r0["user_id"].(string)
sendtok := r0["send_key"].(string)
admintok := r0["admin_key"].(string)
@@ -1086,7 +1171,7 @@ func TestSendToNewChannel(t *testing.T) {
}
{
- chan0 := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid))
+ chan0 := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels", uid))
tt.AssertEqual(t, "chan-count", 0, len(chan0.Channels))
}
@@ -1097,7 +1182,7 @@ func TestSendToNewChannel(t *testing.T) {
})
{
- clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid))
+ clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels", uid))
tt.AssertMappedSet(t, "channels", []string{"main"}, clist.Channels, "display_name")
tt.AssertMappedSet(t, "channels", []string{"main"}, clist.Channels, "internal_name")
}
@@ -1111,7 +1196,7 @@ func TestSendToNewChannel(t *testing.T) {
})
{
- clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid))
+ clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels", uid))
tt.AssertMappedSet(t, "channels", []string{"main"}, clist.Channels, "display_name")
tt.AssertMappedSet(t, "channels", []string{"main"}, clist.Channels, "internal_name")
}
@@ -1125,7 +1210,7 @@ func TestSendToNewChannel(t *testing.T) {
})
{
- clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid))
+ clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels", uid))
tt.AssertMappedSet(t, "channels", []string{"main", "test"}, clist.Channels, "display_name")
tt.AssertMappedSet(t, "channels", []string{"main", "test"}, clist.Channels, "internal_name")
}
@@ -1138,7 +1223,7 @@ func TestSendToNewChannel(t *testing.T) {
})
{
- clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid))
+ clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels", uid))
tt.AssertMappedSet(t, "channels", []string{"main", "test"}, clist.Channels, "display_name")
tt.AssertMappedSet(t, "channels", []string{"main", "test"}, clist.Channels, "internal_name")
}
@@ -1155,7 +1240,7 @@ func TestSendToManualChannel(t *testing.T) {
"fcm_token": "DUMMY_FCM",
})
- uid := int(r0["user_id"].(float64))
+ uid := r0["user_id"].(string)
sendtok := r0["send_key"].(string)
admintok := r0["admin_key"].(string)
@@ -1164,7 +1249,7 @@ func TestSendToManualChannel(t *testing.T) {
}
{
- clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid))
+ clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels", uid))
tt.AssertMappedSet(t, "channels", []string{}, clist.Channels, "display_name")
tt.AssertMappedSet(t, "channels", []string{}, clist.Channels, "internal_name")
}
@@ -1176,7 +1261,7 @@ func TestSendToManualChannel(t *testing.T) {
})
{
- clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid))
+ clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels", uid))
tt.AssertMappedSet(t, "channels", []string{"main"}, clist.Channels, "display_name")
tt.AssertMappedSet(t, "channels", []string{"main"}, clist.Channels, "internal_name")
}
@@ -1190,18 +1275,18 @@ func TestSendToManualChannel(t *testing.T) {
})
{
- clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid))
+ clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels", uid))
tt.AssertEqual(t, "chan.len", 1, len(clist.Channels))
tt.AssertEqual(t, "chan.internal_name", "main", clist.Channels[0]["internal_name"])
tt.AssertEqual(t, "chan.display_name", "main", clist.Channels[0]["display_name"])
}
- tt.RequestAuthPost[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid), gin.H{
+ tt.RequestAuthPost[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels", uid), gin.H{
"name": "test",
})
{
- clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid))
+ clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels", uid))
tt.AssertMappedSet(t, "channels", []string{"main", "test"}, clist.Channels, "display_name")
tt.AssertMappedSet(t, "channels", []string{"main", "test"}, clist.Channels, "internal_name")
}
@@ -1215,7 +1300,7 @@ func TestSendToManualChannel(t *testing.T) {
})
{
- clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid))
+ clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels", uid))
tt.AssertMappedSet(t, "channels", []string{"main", "test"}, clist.Channels, "display_name")
tt.AssertMappedSet(t, "channels", []string{"main", "test"}, clist.Channels, "internal_name")
}
@@ -1228,7 +1313,7 @@ func TestSendToManualChannel(t *testing.T) {
})
{
- clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid))
+ clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s/channels", uid))
tt.AssertMappedSet(t, "channels", []string{"main", "test"}, clist.Channels, "display_name")
tt.AssertMappedSet(t, "channels", []string{"main", "test"}, clist.Channels, "internal_name")
}
@@ -1245,7 +1330,7 @@ func TestSendToTooLongChannel(t *testing.T) {
"fcm_token": "DUMMY_FCM",
})
- uid := int(r0["user_id"].(float64))
+ uid := r0["user_id"].(string)
sendtok := r0["send_key"].(string)
tt.RequestPost[tt.Void](t, baseUrl, "/", gin.H{
@@ -1281,7 +1366,7 @@ func TestQuotaExceededNoPro(t *testing.T) {
"fcm_token": "DUMMY_FCM",
})
- uid := int(r0["user_id"].(float64))
+ uid := r0["user_id"].(string)
admintok := r0["admin_key"].(string)
sendtok := r0["send_key"].(string)
@@ -1300,7 +1385,7 @@ func TestQuotaExceededNoPro(t *testing.T) {
}
{
- usr := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d", uid))
+ usr := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s", uid))
tt.AssertStrRepEqual(t, "quota.1", 1, usr["quota_used"])
tt.AssertStrRepEqual(t, "quota.1", 50, usr["quota_max"])
@@ -1317,7 +1402,7 @@ func TestQuotaExceededNoPro(t *testing.T) {
}
{
- usr := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d", uid))
+ usr := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s", uid))
tt.AssertStrRepEqual(t, "quota.49", 49, usr["quota_used"])
tt.AssertStrRepEqual(t, "quota.49", 50, usr["quota_max"])
@@ -1333,7 +1418,7 @@ func TestQuotaExceededNoPro(t *testing.T) {
tt.AssertStrRepEqual(t, "quota.msg.50", 50, msg50["quota_max"])
{
- usr := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d", uid))
+ usr := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s", uid))
tt.AssertStrRepEqual(t, "quota.50", 50, usr["quota_used"])
tt.AssertStrRepEqual(t, "quota.50", 50, usr["quota_max"])
@@ -1359,7 +1444,7 @@ func TestQuotaExceededPro(t *testing.T) {
"pro_token": "ANDROID|v2|PURCHASED:DUMMY_TOK_XX",
})
- uid := int(r0["user_id"].(float64))
+ uid := r0["user_id"].(string)
admintok := r0["admin_key"].(string)
sendtok := r0["send_key"].(string)
@@ -1378,7 +1463,7 @@ func TestQuotaExceededPro(t *testing.T) {
}
{
- usr := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d", uid))
+ usr := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s", uid))
tt.AssertStrRepEqual(t, "quota.1", 1, usr["quota_used"])
tt.AssertStrRepEqual(t, "quota.1", 1000, usr["quota_max"])
@@ -1395,7 +1480,7 @@ func TestQuotaExceededPro(t *testing.T) {
}
{
- usr := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d", uid))
+ usr := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s", uid))
tt.AssertStrRepEqual(t, "quota.999", 999, usr["quota_used"])
tt.AssertStrRepEqual(t, "quota.999", 1000, usr["quota_max"])
@@ -1411,7 +1496,7 @@ func TestQuotaExceededPro(t *testing.T) {
tt.AssertStrRepEqual(t, "quota.msg.1000", 1000, msg50["quota_max"])
{
- usr := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d", uid))
+ usr := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/users/%s", uid))
tt.AssertStrRepEqual(t, "quota.1000", 1000, usr["quota_used"])
tt.AssertStrRepEqual(t, "quota.1000", 1000, usr["quota_max"])
@@ -1437,7 +1522,7 @@ func TestSendParallel(t *testing.T) {
"pro_token": "ANDROID|v2|PURCHASED:DUMMY_TOK_XX",
})
- uid := int(r0["user_id"].(float64))
+ uid := r0["user_id"].(string)
sendtok := r0["send_key"].(string)
count := 128
diff --git a/scnserver/test/util/factory.go b/scnserver/test/util/factory.go
index b8c70af..baefabc 100644
--- a/scnserver/test/util/factory.go
+++ b/scnserver/test/util/factory.go
@@ -59,7 +59,7 @@ type clientex struct {
}
type Userdat struct {
- UID int64
+ UID string
SendKey string
AdminKey string
ReadKey string
@@ -319,7 +319,7 @@ func InitDefaultData(t *testing.T, ws *logic.Application) DefData {
}
user0 := RequestPost[gin.H](t, baseUrl, "/api/users", body)
- uid0 := int64(user0["user_id"].(float64))
+ uid0 := user0["user_id"].(string)
readtok0 := user0["read_key"].(string)
sendtok0 := user0["send_key"].(string)
admintok0 := user0["admin_key"].(string)
@@ -341,7 +341,7 @@ func InitDefaultData(t *testing.T, ws *logic.Application) DefData {
body["agent_version"] = cex.AgentVersion
body["client_type"] = cex.ClientType
body["fcm_token"] = cex.FCMTok
- RequestAuthPost[gin.H](t, users[cex.User].AdminKey, baseUrl, fmt.Sprintf("/api/users/%d/clients", users[cex.User].UID), body)
+ RequestAuthPost[gin.H](t, users[cex.User].AdminKey, baseUrl, fmt.Sprintf("/api/users/%s/clients", users[cex.User].UID), body)
}
// Create Messages
@@ -378,7 +378,7 @@ func InitDefaultData(t *testing.T, ws *logic.Application) DefData {
// create manual channels
{
- RequestAuthPost[Void](t, users[9].AdminKey, baseUrl, fmt.Sprintf("/api/users/%d/channels", users[9].UID), gin.H{"name": "manual@chan"})
+ RequestAuthPost[Void](t, users[9].AdminKey, baseUrl, fmt.Sprintf("/api/users/%s/channels", users[9].UID), gin.H{"name": "manual@chan"})
}
// Sub/Unsub for Users 12+13
@@ -399,7 +399,7 @@ func doSubscribe(t *testing.T, baseUrl string, user Userdat, chanOwner Userdat,
if user == chanOwner {
- RequestAuthPost[Void](t, user.AdminKey, baseUrl, fmt.Sprintf("/api/users/%d/channels", user.UID), gin.H{
+ RequestAuthPost[Void](t, user.AdminKey, baseUrl, fmt.Sprintf("/api/users/%s/channels", user.UID), gin.H{
"channel_owner_user_id": chanOwner.UID,
"channel_internal_name": chanInternalName,
})
@@ -409,7 +409,7 @@ func doSubscribe(t *testing.T, baseUrl string, user Userdat, chanOwner Userdat,
Channels []gin.H `json:"channels"`
}
- clist := RequestAuthGet[chanlist](t, chanOwner.AdminKey, baseUrl, fmt.Sprintf("/api/users/%d/channels?selector=owned", chanOwner.UID))
+ clist := RequestAuthGet[chanlist](t, chanOwner.AdminKey, baseUrl, fmt.Sprintf("/api/users/%s/channels?selector=owned", chanOwner.UID))
var chandat gin.H
for _, v := range clist.Channels {
@@ -419,8 +419,8 @@ func doSubscribe(t *testing.T, baseUrl string, user Userdat, chanOwner Userdat,
}
}
- RequestAuthPost[Void](t, user.AdminKey, baseUrl, fmt.Sprintf("/api/users/%d/subscriptions?chan_subscribe_key=%s", user.UID, chandat["subscribe_key"].(string)), gin.H{
- "channel_id": chandat["channel_id"].(float64),
+ RequestAuthPost[Void](t, user.AdminKey, baseUrl, fmt.Sprintf("/api/users/%s/subscriptions?chan_subscribe_key=%s", user.UID, chandat["subscribe_key"].(string)), gin.H{
+ "channel_id": chandat["channel_id"].(string),
})
}
@@ -433,17 +433,17 @@ func doUnsubscribe(t *testing.T, baseUrl string, user Userdat, chanOwner Userdat
Subscriptions []gin.H `json:"subscriptions"`
}
- slist := RequestAuthGet[chanlist](t, user.AdminKey, baseUrl, fmt.Sprintf("/api/users/%d/subscriptions?selector=outgoing_confirmed", user.UID))
+ slist := RequestAuthGet[chanlist](t, user.AdminKey, baseUrl, fmt.Sprintf("/api/users/%s/subscriptions?selector=outgoing_confirmed", user.UID))
var subdat gin.H
for _, v := range slist.Subscriptions {
- if v["channel_internal_name"].(string) == chanInternalName && int64(v["channel_owner_user_id"].(float64)) == chanOwner.UID {
+ if v["channel_internal_name"].(string) == chanInternalName && v["channel_owner_user_id"].(string) == chanOwner.UID {
subdat = v
break
}
}
- RequestAuthDelete[Void](t, user.AdminKey, baseUrl, fmt.Sprintf("/api/users/%d/subscriptions/%v", user.UID, subdat["subscription_id"]), gin.H{})
+ RequestAuthDelete[Void](t, user.AdminKey, baseUrl, fmt.Sprintf("/api/users/%s/subscriptions/%v", user.UID, subdat["subscription_id"]), gin.H{})
}
@@ -453,17 +453,17 @@ func doAcceptSub(t *testing.T, baseUrl string, user Userdat, subscriber Userdat,
Subscriptions []gin.H `json:"subscriptions"`
}
- slist := RequestAuthGet[chanlist](t, user.AdminKey, baseUrl, fmt.Sprintf("/api/users/%d/subscriptions?selector=incoming_unconfirmed", user.UID))
+ slist := RequestAuthGet[chanlist](t, user.AdminKey, baseUrl, fmt.Sprintf("/api/users/%s/subscriptions?selector=incoming_unconfirmed", user.UID))
var subdat gin.H
for _, v := range slist.Subscriptions {
- if v["channel_internal_name"].(string) == chanInternalName && int64(v["subscriber_user_id"].(float64)) == subscriber.UID {
+ if v["channel_internal_name"].(string) == chanInternalName && v["subscriber_user_id"].(string) == subscriber.UID {
subdat = v
break
}
}
- RequestAuthPatch[Void](t, user.AdminKey, baseUrl, fmt.Sprintf("/api/users/%d/subscriptions/%v", user.UID, subdat["subscription_id"]), gin.H{
+ RequestAuthPatch[Void](t, user.AdminKey, baseUrl, fmt.Sprintf("/api/users/%s/subscriptions/%v", user.UID, subdat["subscription_id"]), gin.H{
"confirmed": true,
})
diff --git a/scnserver/test/util/internals.go b/scnserver/test/util/internals.go
new file mode 100644
index 0000000..799124a
--- /dev/null
+++ b/scnserver/test/util/internals.go
@@ -0,0 +1,45 @@
+package util
+
+import (
+ "blackforestbytes.com/simplecloudnotifier/logic"
+ "testing"
+ "time"
+)
+
+func ConvertToCompatID(t *testing.T, ws *logic.Application, newid string) int64 {
+
+ ctx := ws.NewSimpleTransactionContext(5 * time.Second)
+ defer ctx.Cancel()
+
+ uidold, _, err := ws.Database.Primary.ConvertToCompatID(ctx, newid)
+ TestFailIfErr(t, err)
+
+ if uidold == nil {
+ TestFail(t, "faile to convert newid to oldid (compat)")
+ }
+
+ err = ctx.CommitTransaction()
+ if err != nil {
+ TestFail(t, "failed to commit")
+ return 0
+ }
+
+ return *uidold
+}
+
+func CreateCompatID(t *testing.T, ws *logic.Application, idtype string, newid string) int64 {
+
+ ctx := ws.NewSimpleTransactionContext(5 * time.Second)
+ defer ctx.Cancel()
+
+ uidold, err := ws.Database.Primary.CreateCompatID(ctx, idtype, newid)
+ TestFailIfErr(t, err)
+
+ err = ctx.CommitTransaction()
+ if err != nil {
+ TestFail(t, "failed to commit")
+ return 0
+ }
+
+ return uidold
+}
diff --git a/scnserver/test/util/webserver.go b/scnserver/test/util/webserver.go
index 241929c..1da3625 100644
--- a/scnserver/test/util/webserver.go
+++ b/scnserver/test/util/webserver.go
@@ -124,14 +124,17 @@ func StartSimpleWebserver(t *testing.T) (*logic.Application, string, func()) {
jobs.NewRequestLogCollectorJob(app),
})
- router.Init(ginengine)
+ err = router.Init(ginengine)
+ if err != nil {
+ panic(err)
+ }
stop := func() {
app.Stop()
+ _ = app.IsRunning.WaitWithTimeout(5*time.Second, false)
_ = os.Remove(dbfile1)
_ = os.Remove(dbfile2)
_ = os.Remove(dbfile3)
- _ = app.IsRunning.WaitWithTimeout(400*time.Millisecond, false)
}
go func() { app.Run() }()