Save internal_name and display_name in channel
This commit is contained in:
parent
f65c231ba0
commit
0cb2a977a0
@ -23,6 +23,8 @@
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
- (?) default-priority for channels
|
||||
|
||||
- (?) ack/read deliveries && return ack-count (? or not, how to query?)
|
||||
|
||||
- (?) "login" on website and list/search/filter messages
|
||||
|
@ -20,13 +20,14 @@ const (
|
||||
BINDFAIL_URI_PARAM APIError = 1153
|
||||
INVALID_ENUM_VALUE APIError = 1171
|
||||
|
||||
NO_TITLE APIError = 1201
|
||||
TITLE_TOO_LONG APIError = 1202
|
||||
CONTENT_TOO_LONG APIError = 1203
|
||||
USR_MSG_ID_TOO_LONG APIError = 1204
|
||||
TIMESTAMP_OUT_OF_RANGE APIError = 1205
|
||||
SENDERNAME_TOO_LONG APIError = 1206
|
||||
CHANNEL_TOO_LONG APIError = 1207
|
||||
NO_TITLE APIError = 1201
|
||||
TITLE_TOO_LONG APIError = 1202
|
||||
CONTENT_TOO_LONG APIError = 1203
|
||||
USR_MSG_ID_TOO_LONG APIError = 1204
|
||||
TIMESTAMP_OUT_OF_RANGE APIError = 1205
|
||||
SENDERNAME_TOO_LONG APIError = 1206
|
||||
CHANNEL_TOO_LONG APIError = 1207
|
||||
CHANNEL_NAME_WOULD_CHANGE APIError = 1207
|
||||
|
||||
USER_NOT_FOUND APIError = 1301
|
||||
CLIENT_NOT_FOUND APIError = 1302
|
||||
|
@ -667,9 +667,10 @@ func (h APIHandler) CreateChannel(g *gin.Context) ginresp.HTTPResponse {
|
||||
return *permResp
|
||||
}
|
||||
|
||||
channelName := h.app.NormalizeChannelName(b.Name)
|
||||
channelDisplayName := h.app.NormalizeChannelDisplayName(b.Name)
|
||||
channelInternalName := h.app.NormalizeChannelInternalName(b.Name)
|
||||
|
||||
channelExisting, err := h.database.GetChannelByName(ctx, u.UserID, channelName)
|
||||
channelExisting, err := h.database.GetChannelByName(ctx, u.UserID, channelInternalName)
|
||||
if err != nil {
|
||||
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query channel", err)
|
||||
}
|
||||
@ -682,7 +683,10 @@ func (h APIHandler) CreateChannel(g *gin.Context) ginresp.HTTPResponse {
|
||||
return ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to query user", err)
|
||||
}
|
||||
|
||||
if len(channelName) > user.MaxChannelNameLength() {
|
||||
if len(channelDisplayName) > user.MaxChannelNameLength() {
|
||||
return ginresp.SendAPIError(g, 400, apierr.CHANNEL_TOO_LONG, hl.CHANNEL, fmt.Sprintf("Channel too long (max %d characters)", user.MaxChannelNameLength()), nil)
|
||||
}
|
||||
if len(channelInternalName) > user.MaxChannelNameLength() {
|
||||
return ginresp.SendAPIError(g, 400, apierr.CHANNEL_TOO_LONG, hl.CHANNEL, fmt.Sprintf("Channel too long (max %d characters)", user.MaxChannelNameLength()), nil)
|
||||
}
|
||||
|
||||
@ -693,7 +697,7 @@ func (h APIHandler) CreateChannel(g *gin.Context) ginresp.HTTPResponse {
|
||||
subscribeKey := h.app.GenerateRandomAuthKey()
|
||||
sendKey := h.app.GenerateRandomAuthKey()
|
||||
|
||||
channel, err := h.database.CreateChannel(ctx, u.UserID, channelName, subscribeKey, sendKey)
|
||||
channel, err := h.database.CreateChannel(ctx, u.UserID, channelDisplayName, channelInternalName, subscribeKey, sendKey)
|
||||
if err != nil {
|
||||
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to create channel", err)
|
||||
}
|
||||
@ -726,6 +730,7 @@ func (h APIHandler) CreateChannel(g *gin.Context) ginresp.HTTPResponse {
|
||||
//
|
||||
// @Param subscribe_key body string false "Send `true` to create a new subscribe_key"
|
||||
// @Param send_key body string false "Send `true` to create a new send_key"
|
||||
// @Param display_name body string false "Change the cahnnel display-name (only chnages to lowercase/uppercase are allowed - internal_name must stay the same)"
|
||||
//
|
||||
// @Success 200 {object} models.ChannelWithSubscriptionJSON
|
||||
// @Failure 400 {object} ginresp.apiError "supplied values/parameters cannot be parsed / are invalid"
|
||||
@ -740,8 +745,9 @@ func (h APIHandler) UpdateChannel(g *gin.Context) ginresp.HTTPResponse {
|
||||
ChannelID models.ChannelID `uri:"cid"`
|
||||
}
|
||||
type body struct {
|
||||
RefreshSubscribeKey *bool `json:"subscribe_key"`
|
||||
RefreshSendKey *bool `json:"send_key"`
|
||||
RefreshSubscribeKey *bool `json:"subscribe_key"`
|
||||
RefreshSendKey *bool `json:"send_key"`
|
||||
DisplayName *string `json:"display_name"`
|
||||
}
|
||||
|
||||
var u uri
|
||||
@ -756,7 +762,7 @@ func (h APIHandler) UpdateChannel(g *gin.Context) ginresp.HTTPResponse {
|
||||
return *permResp
|
||||
}
|
||||
|
||||
_, err := h.database.GetChannel(ctx, u.UserID, u.ChannelID)
|
||||
oldChannel, err := h.database.GetChannel(ctx, u.UserID, u.ChannelID)
|
||||
if err == sql.ErrNoRows {
|
||||
return ginresp.APIError(g, 404, apierr.CHANNEL_NOT_FOUND, "Channel not found", err)
|
||||
}
|
||||
@ -769,7 +775,7 @@ func (h APIHandler) UpdateChannel(g *gin.Context) ginresp.HTTPResponse {
|
||||
|
||||
err := h.database.UpdateChannelSendKey(ctx, u.ChannelID, newkey)
|
||||
if err != nil {
|
||||
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to update user", err)
|
||||
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to update channel", err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -778,13 +784,29 @@ func (h APIHandler) UpdateChannel(g *gin.Context) ginresp.HTTPResponse {
|
||||
|
||||
err := h.database.UpdateChannelSubscribeKey(ctx, u.ChannelID, newkey)
|
||||
if err != nil {
|
||||
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to update user", err)
|
||||
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to update channel", err)
|
||||
}
|
||||
}
|
||||
|
||||
if b.DisplayName != nil {
|
||||
|
||||
newDisplayName := h.app.NormalizeChannelDisplayName(*b.DisplayName)
|
||||
newInternalName := h.app.NormalizeChannelInternalName(*b.DisplayName)
|
||||
|
||||
if newInternalName != oldChannel.InternalName {
|
||||
return ginresp.APIError(g, 400, apierr.CHANNEL_NAME_WOULD_CHANGE, "Cannot substantially change the channel name", err)
|
||||
}
|
||||
|
||||
err := h.database.UpdateChannelDisplayName(ctx, u.ChannelID, newDisplayName)
|
||||
if err != nil {
|
||||
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to update channel", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
channel, err := h.database.GetChannel(ctx, u.UserID, u.ChannelID)
|
||||
if err != nil {
|
||||
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query (updated) user", err)
|
||||
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query (updated) channel", err)
|
||||
}
|
||||
|
||||
return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, channel.JSON(true)))
|
||||
@ -1191,7 +1213,9 @@ func (h APIHandler) CreateSubscription(g *gin.Context) ginresp.HTTPResponse {
|
||||
return *permResp
|
||||
}
|
||||
|
||||
channel, err := h.database.GetChannelByName(ctx, b.ChannelOwnerUserID, h.app.NormalizeChannelName(b.Channel))
|
||||
channelInternalName := h.app.NormalizeChannelInternalName(b.Channel)
|
||||
|
||||
channel, err := h.database.GetChannelByName(ctx, b.ChannelOwnerUserID, channelInternalName)
|
||||
if err != nil {
|
||||
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query channel", err)
|
||||
}
|
||||
|
@ -166,9 +166,11 @@ func (h MessageHandler) sendMessageInternal(g *gin.Context, ctx *logic.AppContex
|
||||
return ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to query user", err)
|
||||
}
|
||||
|
||||
channelName := user.DefaultChannel()
|
||||
channelDisplayName := user.DefaultChannel()
|
||||
channelInternalName := user.DefaultChannel()
|
||||
if Channel != nil {
|
||||
channelName = h.app.NormalizeChannelName(*Channel)
|
||||
channelDisplayName = h.app.NormalizeChannelDisplayName(*Channel)
|
||||
channelInternalName = h.app.NormalizeChannelInternalName(*Channel)
|
||||
}
|
||||
|
||||
if len(*Title) > user.MaxTitleLength() {
|
||||
@ -177,7 +179,10 @@ func (h MessageHandler) sendMessageInternal(g *gin.Context, ctx *logic.AppContex
|
||||
if Content != nil && len(*Content) > user.MaxContentLength() {
|
||||
return ginresp.SendAPIError(g, 400, apierr.CONTENT_TOO_LONG, hl.CONTENT, fmt.Sprintf("Content too long (%d characters; max := %d characters)", len(*Content), user.MaxContentLength()), nil)
|
||||
}
|
||||
if len(channelName) > user.MaxChannelNameLength() {
|
||||
if len(channelDisplayName) > user.MaxChannelNameLength() {
|
||||
return ginresp.SendAPIError(g, 400, apierr.CHANNEL_TOO_LONG, hl.CHANNEL, fmt.Sprintf("Channel too long (max %d characters)", user.MaxChannelNameLength()), nil)
|
||||
}
|
||||
if len(channelInternalName) > user.MaxChannelNameLength() {
|
||||
return ginresp.SendAPIError(g, 400, apierr.CHANNEL_TOO_LONG, hl.CHANNEL, fmt.Sprintf("Channel too long (max %d characters)", user.MaxChannelNameLength()), nil)
|
||||
}
|
||||
if SenderName != nil && len(*SenderName) > user.MaxSenderName() {
|
||||
@ -220,7 +225,7 @@ func (h MessageHandler) sendMessageInternal(g *gin.Context, ctx *logic.AppContex
|
||||
if ChanKey != nil {
|
||||
// foreign channel (+ channel send-key)
|
||||
|
||||
foreignChan, err := h.database.GetChannelByNameAndSendKey(ctx, channelName, *ChanKey)
|
||||
foreignChan, err := h.database.GetChannelByNameAndSendKey(ctx, channelInternalName, *ChanKey)
|
||||
if err != nil {
|
||||
return ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to query (foreign) channel", err)
|
||||
}
|
||||
@ -231,7 +236,7 @@ func (h MessageHandler) sendMessageInternal(g *gin.Context, ctx *logic.AppContex
|
||||
} else {
|
||||
// own channel
|
||||
|
||||
channel, err = h.app.GetOrCreateChannel(ctx, *UserID, channelName)
|
||||
channel, err = h.app.GetOrCreateChannel(ctx, *UserID, channelDisplayName, channelInternalName)
|
||||
if err != nil {
|
||||
return ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to query/create (owned) channel", err)
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ func (db *Database) GetChannelByName(ctx TxContext, userid models.UserID, chanNa
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows, err := tx.Query(ctx, "SELECT * FROM channels WHERE owner_user_id = :uid AND name = :nam LIMIT 1", sq.PP{
|
||||
rows, err := tx.Query(ctx, "SELECT * FROM channels WHERE owner_user_id = :uid AND internal_name = :nam LIMIT 1", sq.PP{
|
||||
"uid": userid,
|
||||
"nam": chanName,
|
||||
})
|
||||
@ -38,7 +38,7 @@ func (db *Database) GetChannelByNameAndSendKey(ctx TxContext, chanName string, s
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows, err := tx.Query(ctx, "SELECT * FROM channels WHERE name = :chan_name OR send_key = :send_key LIMIT 1", sq.PP{
|
||||
rows, err := tx.Query(ctx, "SELECT * FROM channels WHERE internal_name = :chan_name OR send_key = :send_key LIMIT 1", sq.PP{
|
||||
"chan_name": chanName,
|
||||
"send_key": sendKey,
|
||||
})
|
||||
@ -57,7 +57,7 @@ func (db *Database) GetChannelByNameAndSendKey(ctx TxContext, chanName string, s
|
||||
return &channel, nil
|
||||
}
|
||||
|
||||
func (db *Database) CreateChannel(ctx TxContext, userid models.UserID, name string, subscribeKey string, sendKey string) (models.Channel, error) {
|
||||
func (db *Database) CreateChannel(ctx TxContext, userid models.UserID, dispName string, intName string, subscribeKey string, sendKey string) (models.Channel, error) {
|
||||
tx, err := ctx.GetOrCreateTransaction(db)
|
||||
if err != nil {
|
||||
return models.Channel{}, err
|
||||
@ -65,9 +65,10 @@ func (db *Database) CreateChannel(ctx TxContext, userid models.UserID, name stri
|
||||
|
||||
now := time.Now().UTC()
|
||||
|
||||
res, err := tx.Exec(ctx, "INSERT INTO channels (owner_user_id, name, subscribe_key, send_key, timestamp_created) VALUES (:ouid, :nam, :subkey, :sendkey, :ts)", sq.PP{
|
||||
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{
|
||||
"ouid": userid,
|
||||
"nam": name,
|
||||
"dnam": dispName,
|
||||
"inam": intName,
|
||||
"subkey": subscribeKey,
|
||||
"sendkey": sendKey,
|
||||
"ts": time2DB(now),
|
||||
@ -84,7 +85,8 @@ func (db *Database) CreateChannel(ctx TxContext, userid models.UserID, name stri
|
||||
return models.Channel{
|
||||
ChannelID: models.ChannelID(liid),
|
||||
OwnerUserID: userid,
|
||||
Name: name,
|
||||
DisplayName: dispName,
|
||||
InternalName: intName,
|
||||
SubscribeKey: subscribeKey,
|
||||
SendKey: sendKey,
|
||||
TimestampCreated: now,
|
||||
@ -246,3 +248,20 @@ func (db *Database) UpdateChannelSubscribeKey(ctx TxContext, channelid models.Ch
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *Database) UpdateChannelDisplayName(ctx TxContext, channelid models.ChannelID, dispname string) error {
|
||||
tx, err := ctx.GetOrCreateTransaction(db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = tx.Exec(ctx, "UPDATE channels SET display_name = :nam WHERE channel_id = :cid", sq.PP{
|
||||
"nam": dispname,
|
||||
"cid": channelid,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -64,10 +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_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{
|
||||
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{
|
||||
"suid": senderUserID,
|
||||
"ouid": channel.OwnerUserID,
|
||||
"cnam": channel.Name,
|
||||
"cnam": channel.InternalName,
|
||||
"cid": channel.ChannelID,
|
||||
"tsr": time2DB(now),
|
||||
"tsc": time2DBOpt(timestampSend),
|
||||
@ -88,19 +88,19 @@ func (db *Database) CreateMessage(ctx TxContext, senderUserID models.UserID, cha
|
||||
}
|
||||
|
||||
return models.Message{
|
||||
SCNMessageID: models.SCNMessageID(liid),
|
||||
SenderUserID: senderUserID,
|
||||
OwnerUserID: channel.OwnerUserID,
|
||||
ChannelName: channel.Name,
|
||||
ChannelID: channel.ChannelID,
|
||||
SenderIP: senderIP,
|
||||
SenderName: senderName,
|
||||
TimestampReal: now,
|
||||
TimestampClient: timestampSend,
|
||||
Title: title,
|
||||
Content: content,
|
||||
Priority: priority,
|
||||
UserMessageID: userMsgId,
|
||||
SCNMessageID: models.SCNMessageID(liid),
|
||||
SenderUserID: senderUserID,
|
||||
OwnerUserID: channel.OwnerUserID,
|
||||
ChannelInternalName: channel.InternalName,
|
||||
ChannelID: channel.ChannelID,
|
||||
SenderIP: senderIP,
|
||||
SenderName: senderName,
|
||||
TimestampReal: now,
|
||||
TimestampClient: timestampSend,
|
||||
Title: title,
|
||||
Content: content,
|
||||
Priority: priority,
|
||||
UserMessageID: userMsgId,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,8 @@ CREATE TABLE channels
|
||||
|
||||
owner_user_id INTEGER NOT NULL,
|
||||
|
||||
name TEXT NOT NULL,
|
||||
internal_name TEXT NOT NULL,
|
||||
display_name TEXT NOT NULL,
|
||||
|
||||
subscribe_key TEXT NOT NULL,
|
||||
send_key TEXT NOT NULL,
|
||||
@ -56,7 +57,7 @@ CREATE TABLE channels
|
||||
|
||||
messages_sent INTEGER NOT NULL DEFAULT '0'
|
||||
) STRICT;
|
||||
CREATE UNIQUE INDEX "idx_channels_identity" ON channels (owner_user_id, name);
|
||||
CREATE UNIQUE INDEX "idx_channels_identity" ON channels (owner_user_id, internal_name);
|
||||
|
||||
CREATE TABLE subscriptions
|
||||
(
|
||||
@ -64,40 +65,45 @@ CREATE TABLE subscriptions
|
||||
|
||||
subscriber_user_id INTEGER NOT NULL,
|
||||
channel_owner_user_id INTEGER NOT NULL,
|
||||
channel_name TEXT NOT NULL,
|
||||
channel_internal_name TEXT NOT NULL,
|
||||
channel_id INTEGER NOT NULL,
|
||||
|
||||
timestamp_created INTEGER NOT NULL,
|
||||
|
||||
confirmed INTEGER CHECK(confirmed IN (0, 1)) NOT NULL
|
||||
) STRICT;
|
||||
CREATE UNIQUE INDEX "idx_subscriptions_ref" ON subscriptions (subscriber_user_id, channel_owner_user_id, channel_name);
|
||||
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);
|
||||
CREATE INDEX "idx_subscriptions_subuser" ON subscriptions (subscriber_user_id);
|
||||
CREATE INDEX "idx_subscriptions_ownuser" ON subscriptions (channel_owner_user_id);
|
||||
CREATE INDEX "idx_subscriptions_tsc" ON subscriptions (timestamp_created);
|
||||
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,
|
||||
channel_name TEXT NOT NULL,
|
||||
channel_id INTEGER NOT NULL,
|
||||
sender_ip TEXT NOT NULL,
|
||||
sender_name TEXT NULL,
|
||||
scn_message_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
sender_user_id INTEGER NOT NULL,
|
||||
owner_user_id INTEGER NOT NULL,
|
||||
channel_internal_name TEXT NOT NULL,
|
||||
channel_id INTEGER NOT NULL,
|
||||
sender_ip TEXT NOT NULL,
|
||||
sender_name TEXT NULL,
|
||||
|
||||
timestamp_real INTEGER NOT NULL,
|
||||
timestamp_client INTEGER NULL,
|
||||
timestamp_real INTEGER NOT NULL,
|
||||
timestamp_client INTEGER NULL,
|
||||
|
||||
title TEXT NOT NULL,
|
||||
content TEXT NULL,
|
||||
priority INTEGER CHECK(priority IN (0, 1, 2)) NOT NULL,
|
||||
usr_message_id TEXT NULL,
|
||||
title TEXT NOT NULL,
|
||||
content TEXT NULL,
|
||||
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'
|
||||
) STRICT;
|
||||
CREATE INDEX "idx_messages_owner_channel" ON messages (owner_user_id, channel_name COLLATE BINARY);
|
||||
CREATE INDEX "idx_messages_owner_channel_nc" ON messages (owner_user_id, channel_name COLLATE NOCASE);
|
||||
CREATE INDEX "idx_messages_channel" ON messages (channel_name COLLATE BINARY);
|
||||
CREATE INDEX "idx_messages_channel_nc" ON messages (channel_name COLLATE NOCASE);
|
||||
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);
|
||||
CREATE INDEX "idx_messages_channel" ON messages (channel_internal_name COLLATE BINARY);
|
||||
CREATE INDEX "idx_messages_channel_nc" ON messages (channel_internal_name COLLATE NOCASE);
|
||||
CREATE UNIQUE INDEX "idx_messages_idempotency" ON messages (owner_user_id, usr_message_id COLLATE BINARY);
|
||||
CREATE INDEX "idx_messages_senderip" ON messages (sender_ip COLLATE BINARY);
|
||||
CREATE INDEX "idx_messages_sendername" ON messages (sender_name COLLATE BINARY);
|
||||
@ -109,7 +115,7 @@ CREATE INDEX "idx_messages_deleted" ON messages (deleted);
|
||||
|
||||
CREATE VIRTUAL TABLE messages_fts USING fts5
|
||||
(
|
||||
channel_name,
|
||||
channel_internal_name,
|
||||
sender_name,
|
||||
title,
|
||||
content,
|
||||
@ -120,16 +126,16 @@ CREATE VIRTUAL TABLE messages_fts USING fts5
|
||||
);
|
||||
|
||||
CREATE TRIGGER fts_insert AFTER INSERT ON messages BEGIN
|
||||
INSERT INTO messages_fts (rowid, channel_name, sender_name, title, content) VALUES (new.scn_message_id, new.channel_name, new.sender_name, new.title, new.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);
|
||||
END;
|
||||
|
||||
CREATE TRIGGER fts_update AFTER UPDATE ON messages BEGIN
|
||||
INSERT INTO messages_fts (messages_fts, rowid, channel_name, sender_name, title, content) VALUES ('delete', old.scn_message_id, old.channel_name, old.sender_name, old.title, old.content);
|
||||
INSERT INTO messages_fts ( rowid, channel_name, sender_name, title, content) VALUES ( new.scn_message_id, new.channel_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.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);
|
||||
END;
|
||||
|
||||
CREATE TRIGGER fts_delete AFTER DELETE ON messages BEGIN
|
||||
INSERT INTO messages_fts (messages_fts, rowid, channel_name, sender_name, title, content) VALUES ('delete', old.scn_message_id, old.channel_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.scn_message_id, old.channel_internal_name, old.sender_name, old.title, old.content);
|
||||
END;
|
||||
|
||||
|
||||
|
@ -15,10 +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_name, channel_id, timestamp_created, confirmed) VALUES (:suid, :ouid, :cnam, :cid, :ts, :conf)", sq.PP{
|
||||
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{
|
||||
"suid": subscriberUID,
|
||||
"ouid": channel.OwnerUserID,
|
||||
"cnam": channel.Name,
|
||||
"cnam": channel.InternalName,
|
||||
"cid": channel.ChannelID,
|
||||
"ts": time2DB(now),
|
||||
"conf": confirmed,
|
||||
@ -33,13 +33,13 @@ func (db *Database) CreateSubscription(ctx TxContext, subscriberUID models.UserI
|
||||
}
|
||||
|
||||
return models.Subscription{
|
||||
SubscriptionID: models.SubscriptionID(liid),
|
||||
SubscriberUserID: subscriberUID,
|
||||
ChannelOwnerUserID: channel.OwnerUserID,
|
||||
ChannelID: channel.ChannelID,
|
||||
ChannelName: channel.Name,
|
||||
TimestampCreated: now,
|
||||
Confirmed: confirmed,
|
||||
SubscriptionID: models.SubscriptionID(liid),
|
||||
SubscriberUserID: subscriberUID,
|
||||
ChannelOwnerUserID: channel.OwnerUserID,
|
||||
ChannelID: channel.ChannelID,
|
||||
ChannelInternalName: channel.InternalName,
|
||||
TimestampCreated: now,
|
||||
Confirmed: confirmed,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -282,10 +282,8 @@ func (app *Application) getPermissions(ctx *AppContext, hdr string) (PermissionS
|
||||
return NewEmptyPermissions(), nil
|
||||
}
|
||||
|
||||
func (app *Application) GetOrCreateChannel(ctx *AppContext, userid models.UserID, chanName string) (models.Channel, error) {
|
||||
chanName = app.NormalizeChannelName(chanName)
|
||||
|
||||
existingChan, err := app.Database.GetChannelByName(ctx, userid, chanName)
|
||||
func (app *Application) GetOrCreateChannel(ctx *AppContext, userid models.UserID, displayChanName string, intChanName string) (models.Channel, error) {
|
||||
existingChan, err := app.Database.GetChannelByName(ctx, userid, intChanName)
|
||||
if err != nil {
|
||||
return models.Channel{}, err
|
||||
}
|
||||
@ -297,7 +295,7 @@ func (app *Application) GetOrCreateChannel(ctx *AppContext, userid models.UserID
|
||||
subscribeKey := app.GenerateRandomAuthKey()
|
||||
sendKey := app.GenerateRandomAuthKey()
|
||||
|
||||
newChan, err := app.Database.CreateChannel(ctx, userid, chanName, subscribeKey, sendKey)
|
||||
newChan, err := app.Database.CreateChannel(ctx, userid, displayChanName, intChanName, subscribeKey, sendKey)
|
||||
if err != nil {
|
||||
return models.Channel{}, err
|
||||
}
|
||||
@ -310,12 +308,22 @@ func (app *Application) GetOrCreateChannel(ctx *AppContext, userid models.UserID
|
||||
return newChan, nil
|
||||
}
|
||||
|
||||
func (app *Application) NormalizeChannelName(v string) string {
|
||||
rex := regexp.MustCompile("[^[:alnum:]\\-_]")
|
||||
var rexWhitespaceStart = regexp.MustCompile("^\\s+")
|
||||
var rexWhitespaceEnd = regexp.MustCompile("\\s+$")
|
||||
|
||||
func (app *Application) NormalizeChannelDisplayName(v string) string {
|
||||
v = strings.TrimSpace(v)
|
||||
v = rexWhitespaceStart.ReplaceAllString(v, "")
|
||||
v = rexWhitespaceEnd.ReplaceAllString(v, "")
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
func (app *Application) NormalizeChannelInternalName(v string) string {
|
||||
v = strings.TrimSpace(v)
|
||||
v = strings.ToLower(v)
|
||||
v = rex.ReplaceAllString(v, "")
|
||||
v = rexWhitespaceStart.ReplaceAllString(v, "")
|
||||
v = rexWhitespaceEnd.ReplaceAllString(v, "")
|
||||
|
||||
return v
|
||||
}
|
||||
|
@ -10,7 +10,8 @@ import (
|
||||
type Channel struct {
|
||||
ChannelID ChannelID
|
||||
OwnerUserID UserID
|
||||
Name string
|
||||
InternalName string
|
||||
DisplayName string
|
||||
SubscribeKey string
|
||||
SendKey string
|
||||
TimestampCreated time.Time
|
||||
@ -22,7 +23,8 @@ func (c Channel) JSON(includeKey bool) ChannelJSON {
|
||||
return ChannelJSON{
|
||||
ChannelID: c.ChannelID,
|
||||
OwnerUserID: c.OwnerUserID,
|
||||
Name: c.Name,
|
||||
InternalName: c.InternalName,
|
||||
DisplayName: c.DisplayName,
|
||||
SubscribeKey: langext.Conditional(includeKey, langext.Ptr(c.SubscribeKey), nil),
|
||||
SendKey: langext.Conditional(includeKey, langext.Ptr(c.SendKey), nil),
|
||||
TimestampCreated: c.TimestampCreated.Format(time.RFC3339Nano),
|
||||
@ -57,7 +59,8 @@ func (c ChannelWithSubscription) JSON(includeChannelKey bool) ChannelWithSubscri
|
||||
type ChannelJSON struct {
|
||||
ChannelID ChannelID `json:"channel_id"`
|
||||
OwnerUserID UserID `json:"owner_user_id"`
|
||||
Name string `json:"name"`
|
||||
InternalName string `json:"internal_name"`
|
||||
DisplayName string `json:"display_name"`
|
||||
SubscribeKey *string `json:"subscribe_key"` // can be nil, depending on endpoint
|
||||
SendKey *string `json:"send_key"` // can be nil, depending on endpoint
|
||||
TimestampCreated string `json:"timestamp_created"`
|
||||
@ -73,7 +76,8 @@ type ChannelWithSubscriptionJSON struct {
|
||||
type ChannelDB struct {
|
||||
ChannelID ChannelID `db:"channel_id"`
|
||||
OwnerUserID UserID `db:"owner_user_id"`
|
||||
Name string `db:"name"`
|
||||
InternalName string `db:"internal_name"`
|
||||
DisplayName string `db:"display_name"`
|
||||
SubscribeKey string `db:"subscribe_key"`
|
||||
SendKey string `db:"send_key"`
|
||||
TimestampCreated int64 `db:"timestamp_created"`
|
||||
@ -86,7 +90,8 @@ func (c ChannelDB) Model() Channel {
|
||||
return Channel{
|
||||
ChannelID: c.ChannelID,
|
||||
OwnerUserID: c.OwnerUserID,
|
||||
Name: c.Name,
|
||||
InternalName: c.InternalName,
|
||||
DisplayName: c.DisplayName,
|
||||
SubscribeKey: c.SubscribeKey,
|
||||
SendKey: c.SendKey,
|
||||
TimestampCreated: time.UnixMilli(c.TimestampCreated),
|
||||
|
@ -13,55 +13,55 @@ const (
|
||||
)
|
||||
|
||||
type Message struct {
|
||||
SCNMessageID SCNMessageID
|
||||
SenderUserID UserID
|
||||
OwnerUserID UserID
|
||||
ChannelName string
|
||||
ChannelID ChannelID
|
||||
SenderName *string
|
||||
SenderIP string
|
||||
TimestampReal time.Time
|
||||
TimestampClient *time.Time
|
||||
Title string
|
||||
Content *string
|
||||
Priority int
|
||||
UserMessageID *string
|
||||
Deleted bool
|
||||
SCNMessageID SCNMessageID
|
||||
SenderUserID UserID
|
||||
OwnerUserID UserID
|
||||
ChannelInternalName string
|
||||
ChannelID ChannelID
|
||||
SenderName *string
|
||||
SenderIP string
|
||||
TimestampReal time.Time
|
||||
TimestampClient *time.Time
|
||||
Title string
|
||||
Content *string
|
||||
Priority int
|
||||
UserMessageID *string
|
||||
Deleted bool
|
||||
}
|
||||
|
||||
func (m Message) FullJSON() MessageJSON {
|
||||
return MessageJSON{
|
||||
SCNMessageID: m.SCNMessageID,
|
||||
SenderUserID: m.SenderUserID,
|
||||
OwnerUserID: m.OwnerUserID,
|
||||
ChannelName: m.ChannelName,
|
||||
ChannelID: m.ChannelID,
|
||||
SenderName: m.SenderName,
|
||||
SenderIP: m.SenderIP,
|
||||
Timestamp: m.Timestamp().Format(time.RFC3339Nano),
|
||||
Title: m.Title,
|
||||
Content: m.Content,
|
||||
Priority: m.Priority,
|
||||
UserMessageID: m.UserMessageID,
|
||||
Trimmed: false,
|
||||
SCNMessageID: m.SCNMessageID,
|
||||
SenderUserID: m.SenderUserID,
|
||||
OwnerUserID: m.OwnerUserID,
|
||||
ChannelInternalName: m.ChannelInternalName,
|
||||
ChannelID: m.ChannelID,
|
||||
SenderName: m.SenderName,
|
||||
SenderIP: m.SenderIP,
|
||||
Timestamp: m.Timestamp().Format(time.RFC3339Nano),
|
||||
Title: m.Title,
|
||||
Content: m.Content,
|
||||
Priority: m.Priority,
|
||||
UserMessageID: m.UserMessageID,
|
||||
Trimmed: false,
|
||||
}
|
||||
}
|
||||
|
||||
func (m Message) TrimmedJSON() MessageJSON {
|
||||
return MessageJSON{
|
||||
SCNMessageID: m.SCNMessageID,
|
||||
SenderUserID: m.SenderUserID,
|
||||
OwnerUserID: m.OwnerUserID,
|
||||
ChannelName: m.ChannelName,
|
||||
ChannelID: m.ChannelID,
|
||||
SenderName: m.SenderName,
|
||||
SenderIP: m.SenderIP,
|
||||
Timestamp: m.Timestamp().Format(time.RFC3339Nano),
|
||||
Title: m.Title,
|
||||
Content: m.TrimmedContent(),
|
||||
Priority: m.Priority,
|
||||
UserMessageID: m.UserMessageID,
|
||||
Trimmed: m.NeedsTrim(),
|
||||
SCNMessageID: m.SCNMessageID,
|
||||
SenderUserID: m.SenderUserID,
|
||||
OwnerUserID: m.OwnerUserID,
|
||||
ChannelInternalName: m.ChannelInternalName,
|
||||
ChannelID: m.ChannelID,
|
||||
SenderName: m.SenderName,
|
||||
SenderIP: m.SenderIP,
|
||||
Timestamp: m.Timestamp().Format(time.RFC3339Nano),
|
||||
Title: m.Title,
|
||||
Content: m.TrimmedContent(),
|
||||
Priority: m.Priority,
|
||||
UserMessageID: m.UserMessageID,
|
||||
Trimmed: m.NeedsTrim(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,54 +94,54 @@ 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"`
|
||||
ChannelName string `json:"channel_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"`
|
||||
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"`
|
||||
}
|
||||
|
||||
type MessageDB struct {
|
||||
SCNMessageID SCNMessageID `db:"scn_message_id"`
|
||||
SenderUserID UserID `db:"sender_user_id"`
|
||||
OwnerUserID UserID `db:"owner_user_id"`
|
||||
ChannelName string `db:"channel_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"`
|
||||
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"`
|
||||
}
|
||||
|
||||
func (m MessageDB) Model() Message {
|
||||
return Message{
|
||||
SCNMessageID: m.SCNMessageID,
|
||||
SenderUserID: m.SenderUserID,
|
||||
OwnerUserID: m.OwnerUserID,
|
||||
ChannelName: m.ChannelName,
|
||||
ChannelID: m.ChannelID,
|
||||
SenderName: m.SenderName,
|
||||
SenderIP: m.SenderIP,
|
||||
TimestampReal: time.UnixMilli(m.TimestampReal),
|
||||
TimestampClient: timeOptFromMilli(m.TimestampClient),
|
||||
Title: m.Title,
|
||||
Content: m.Content,
|
||||
Priority: m.Priority,
|
||||
UserMessageID: m.UserMessageID,
|
||||
Deleted: m.Deleted != 0,
|
||||
SCNMessageID: m.SCNMessageID,
|
||||
SenderUserID: m.SenderUserID,
|
||||
OwnerUserID: m.OwnerUserID,
|
||||
ChannelInternalName: m.ChannelInternalName,
|
||||
ChannelID: m.ChannelID,
|
||||
SenderName: m.SenderName,
|
||||
SenderIP: m.SenderIP,
|
||||
TimestampReal: time.UnixMilli(m.TimestampReal),
|
||||
TimestampClient: timeOptFromMilli(m.TimestampClient),
|
||||
Title: m.Title,
|
||||
Content: m.Content,
|
||||
Priority: m.Priority,
|
||||
UserMessageID: m.UserMessageID,
|
||||
Deleted: m.Deleted != 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,56 +8,56 @@ import (
|
||||
)
|
||||
|
||||
type Subscription struct {
|
||||
SubscriptionID SubscriptionID
|
||||
SubscriberUserID UserID
|
||||
ChannelOwnerUserID UserID
|
||||
ChannelID ChannelID
|
||||
ChannelName string
|
||||
TimestampCreated time.Time
|
||||
Confirmed bool
|
||||
SubscriptionID SubscriptionID
|
||||
SubscriberUserID UserID
|
||||
ChannelOwnerUserID UserID
|
||||
ChannelID ChannelID
|
||||
ChannelInternalName string
|
||||
TimestampCreated time.Time
|
||||
Confirmed bool
|
||||
}
|
||||
|
||||
func (s Subscription) JSON() SubscriptionJSON {
|
||||
return SubscriptionJSON{
|
||||
SubscriptionID: s.SubscriptionID,
|
||||
SubscriberUserID: s.SubscriberUserID,
|
||||
ChannelOwnerUserID: s.ChannelOwnerUserID,
|
||||
ChannelID: s.ChannelID,
|
||||
ChannelName: s.ChannelName,
|
||||
TimestampCreated: s.TimestampCreated.Format(time.RFC3339Nano),
|
||||
Confirmed: s.Confirmed,
|
||||
SubscriptionID: s.SubscriptionID,
|
||||
SubscriberUserID: s.SubscriberUserID,
|
||||
ChannelOwnerUserID: s.ChannelOwnerUserID,
|
||||
ChannelID: s.ChannelID,
|
||||
ChannelInternalName: s.ChannelInternalName,
|
||||
TimestampCreated: s.TimestampCreated.Format(time.RFC3339Nano),
|
||||
Confirmed: s.Confirmed,
|
||||
}
|
||||
}
|
||||
|
||||
type SubscriptionJSON struct {
|
||||
SubscriptionID SubscriptionID `json:"subscription_id"`
|
||||
SubscriberUserID UserID `json:"subscriber_user_id"`
|
||||
ChannelOwnerUserID UserID `json:"channel_owner_user_id"`
|
||||
ChannelID ChannelID `json:"channel_id"`
|
||||
ChannelName string `json:"channel_name"`
|
||||
TimestampCreated string `json:"timestamp_created"`
|
||||
Confirmed bool `json:"confirmed"`
|
||||
SubscriptionID SubscriptionID `json:"subscription_id"`
|
||||
SubscriberUserID UserID `json:"subscriber_user_id"`
|
||||
ChannelOwnerUserID UserID `json:"channel_owner_user_id"`
|
||||
ChannelID ChannelID `json:"channel_id"`
|
||||
ChannelInternalName string `json:"channel_internal_name"`
|
||||
TimestampCreated string `json:"timestamp_created"`
|
||||
Confirmed bool `json:"confirmed"`
|
||||
}
|
||||
|
||||
type SubscriptionDB struct {
|
||||
SubscriptionID SubscriptionID `db:"subscription_id"`
|
||||
SubscriberUserID UserID `db:"subscriber_user_id"`
|
||||
ChannelOwnerUserID UserID `db:"channel_owner_user_id"`
|
||||
ChannelID ChannelID `db:"channel_id"`
|
||||
ChannelName string `db:"channel_name"`
|
||||
TimestampCreated int64 `db:"timestamp_created"`
|
||||
Confirmed int `db:"confirmed"`
|
||||
SubscriptionID SubscriptionID `db:"subscription_id"`
|
||||
SubscriberUserID UserID `db:"subscriber_user_id"`
|
||||
ChannelOwnerUserID UserID `db:"channel_owner_user_id"`
|
||||
ChannelID ChannelID `db:"channel_id"`
|
||||
ChannelInternalName string `db:"channel_internal_name"`
|
||||
TimestampCreated int64 `db:"timestamp_created"`
|
||||
Confirmed int `db:"confirmed"`
|
||||
}
|
||||
|
||||
func (s SubscriptionDB) Model() Subscription {
|
||||
return Subscription{
|
||||
SubscriptionID: s.SubscriptionID,
|
||||
SubscriberUserID: s.SubscriberUserID,
|
||||
ChannelOwnerUserID: s.ChannelOwnerUserID,
|
||||
ChannelID: s.ChannelID,
|
||||
ChannelName: s.ChannelName,
|
||||
TimestampCreated: time.UnixMilli(s.TimestampCreated),
|
||||
Confirmed: s.Confirmed != 0,
|
||||
SubscriptionID: s.SubscriptionID,
|
||||
SubscriberUserID: s.SubscriberUserID,
|
||||
ChannelOwnerUserID: s.ChannelOwnerUserID,
|
||||
ChannelID: s.ChannelID,
|
||||
ChannelInternalName: s.ChannelInternalName,
|
||||
TimestampCreated: time.UnixMilli(s.TimestampCreated),
|
||||
Confirmed: s.Confirmed != 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1356,6 +1356,14 @@
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "Change the cahnnel display-name (only chnages to lowercase/uppercase are allowed - internal_name must stay the same)",
|
||||
"name": "display_name",
|
||||
"in": "body",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@ -2826,12 +2834,15 @@
|
||||
"channel_id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"display_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"internal_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"messages_sent": {
|
||||
"type": "integer"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"owner_user_id": {
|
||||
"type": "integer"
|
||||
},
|
||||
@ -2912,7 +2923,7 @@
|
||||
"channel_id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"channel_name": {
|
||||
"channel_internal_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"content": {
|
||||
@ -2956,7 +2967,7 @@
|
||||
"channel_id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"channel_name": {
|
||||
"channel_internal_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"channel_owner_user_id": {
|
||||
|
@ -340,10 +340,12 @@ definitions:
|
||||
properties:
|
||||
channel_id:
|
||||
type: integer
|
||||
display_name:
|
||||
type: string
|
||||
internal_name:
|
||||
type: string
|
||||
messages_sent:
|
||||
type: integer
|
||||
name:
|
||||
type: string
|
||||
owner_user_id:
|
||||
type: integer
|
||||
send_key:
|
||||
@ -397,7 +399,7 @@ definitions:
|
||||
properties:
|
||||
channel_id:
|
||||
type: integer
|
||||
channel_name:
|
||||
channel_internal_name:
|
||||
type: string
|
||||
content:
|
||||
type: string
|
||||
@ -426,7 +428,7 @@ definitions:
|
||||
properties:
|
||||
channel_id:
|
||||
type: integer
|
||||
channel_name:
|
||||
channel_internal_name:
|
||||
type: string
|
||||
channel_owner_user_id:
|
||||
type: integer
|
||||
@ -1433,6 +1435,12 @@ paths:
|
||||
name: send_key
|
||||
schema:
|
||||
type: string
|
||||
- description: Change the cahnnel display-name (only chnages to lowercase/uppercase
|
||||
are allowed - internal_name must stay the same)
|
||||
in: body
|
||||
name: display_name
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
|
@ -29,7 +29,8 @@ func TestCreateChannel(t *testing.T) {
|
||||
|
||||
{
|
||||
clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid))
|
||||
tt.AssertMappedSet(t, "channels", []string{}, clist.Channels, "name")
|
||||
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{
|
||||
@ -39,7 +40,8 @@ func TestCreateChannel(t *testing.T) {
|
||||
{
|
||||
clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid))
|
||||
tt.AssertEqual(t, "chan.len", 1, len(clist.Channels))
|
||||
tt.AssertMappedSet(t, "channels", []string{"test"}, clist.Channels, "name")
|
||||
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{
|
||||
@ -48,7 +50,8 @@ func TestCreateChannel(t *testing.T) {
|
||||
|
||||
{
|
||||
clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid))
|
||||
tt.AssertMappedSet(t, "channels", []string{"asdf", "test"}, clist.Channels, "name")
|
||||
tt.AssertMappedSet(t, "channels", []string{"asdf", "test"}, clist.Channels, "display_name")
|
||||
tt.AssertMappedSet(t, "channels", []string{"asdf", "test"}, clist.Channels, "internal_name")
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,8 +93,9 @@ func TestChannelNameNormalization(t *testing.T) {
|
||||
}
|
||||
|
||||
{
|
||||
chan0 := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid))
|
||||
tt.AssertEqual(t, "chan-count", 0, len(chan0.Channels))
|
||||
clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/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{
|
||||
@ -100,8 +104,8 @@ func TestChannelNameNormalization(t *testing.T) {
|
||||
|
||||
{
|
||||
clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid))
|
||||
tt.AssertEqual(t, "chan.len", 1, len(clist.Channels))
|
||||
tt.AssertEqual(t, "chan.name", "test", clist.Channels[0]["name"])
|
||||
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{
|
||||
@ -125,14 +129,25 @@ func TestChannelNameNormalization(t *testing.T) {
|
||||
}, 409, apierr.CHANNEL_ALREADY_EXISTS)
|
||||
|
||||
tt.RequestAuthPostShouldFail(t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid), gin.H{
|
||||
"name": " T e s t ",
|
||||
"name": "\rTeSt\n",
|
||||
}, 409, apierr.CHANNEL_ALREADY_EXISTS)
|
||||
|
||||
{
|
||||
clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid))
|
||||
tt.AssertEqual(t, "chan.len", 1, len(clist.Channels))
|
||||
tt.AssertEqual(t, "chan.name", "test", clist.Channels[0]["name"])
|
||||
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{
|
||||
"name": " WeiRD_[\uF5FF]\\stUFf\r\n\t ",
|
||||
})
|
||||
|
||||
{
|
||||
clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/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")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestListChannelsOwned(t *testing.T) {
|
||||
@ -147,24 +162,26 @@ func TestListChannelsOwned(t *testing.T) {
|
||||
|
||||
testdata := map[int][]string{
|
||||
0: {"main", "chattingchamber", "unicdhll", "promotions", "reminders"},
|
||||
1: {"promotions"},
|
||||
2: {},
|
||||
3: {},
|
||||
4: {},
|
||||
5: {},
|
||||
6: {},
|
||||
7: {},
|
||||
8: {},
|
||||
9: {},
|
||||
10: {},
|
||||
11: {},
|
||||
1: {"main", "private"},
|
||||
2: {"main", "ü", "ö", "ä"},
|
||||
3: {"main", "innovations", "reminders"},
|
||||
4: {"main"},
|
||||
5: {"main", "test1", "test2", "test3", "test4", "test5"},
|
||||
6: {"main", "security", "lipsum"},
|
||||
7: {"main"},
|
||||
8: {"main"},
|
||||
9: {"main", "manual@chan"},
|
||||
10: {"main"},
|
||||
11: {"promotions"},
|
||||
12: {},
|
||||
13: {},
|
||||
14: {"", "chan_self_subscribed", "chan_self_unsub"}, //TODO these two have the interesting cases
|
||||
15: {"", "chan_other_nosub", "chan_other_request", "chan_other_request", "chan_other_accepted"}, //TODO these two have the interesting cases
|
||||
}
|
||||
|
||||
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))
|
||||
tt.AssertMappedSet(t, fmt.Sprintf("%d->chanlist", k), v, r0.Channels, "name")
|
||||
tt.AssertMappedSet(t, fmt.Sprintf("%d->chanlist", k), v, r0.Channels, "internal_name")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -166,7 +166,7 @@ func TestGetMessageFull(t *testing.T) {
|
||||
|
||||
tt.AssertEqual(t, "msg.title", "Message_1", msgIn["title"])
|
||||
tt.AssertEqual(t, "msg.content", content, msgIn["content"])
|
||||
tt.AssertEqual(t, "msg.channel", "demo-channel-007", msgIn["channel_name"])
|
||||
tt.AssertEqual(t, "msg.channel", "demo-channel-007", msgIn["channel_internal_name"])
|
||||
tt.AssertEqual(t, "msg.msg_id", "580b5055-a9b5-4cee-b53c-28cf304d25b0", msgIn["usr_message_id"])
|
||||
tt.AssertStrRepEqual(t, "msg.priority", 0, msgIn["priority"])
|
||||
tt.AssertEqual(t, "msg.sender_name", "unit-test-[TestGetMessageFull]", msgIn["sender_name"])
|
||||
|
@ -61,7 +61,7 @@ func TestSendSimpleMessageJSON(t *testing.T) {
|
||||
|
||||
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_name", "main", msg1Get["channel_name"])
|
||||
tt.AssertStrRepEqual(t, "msg.channel_internal_name", "main", msg1Get["channel_internal_name"])
|
||||
}
|
||||
|
||||
func TestSendSimpleMessageQuery(t *testing.T) {
|
||||
@ -97,7 +97,7 @@ func TestSendSimpleMessageQuery(t *testing.T) {
|
||||
|
||||
msg1Get := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, "/api/messages/"+fmt.Sprintf("%v", msg1["scn_msg_id"]))
|
||||
tt.AssertStrRepEqual(t, "msg.title", "Hello World 2134", msg1Get["title"])
|
||||
tt.AssertStrRepEqual(t, "msg.channel_name", "main", msg1Get["channel_name"])
|
||||
tt.AssertStrRepEqual(t, "msg.channel_internal_name", "main", msg1Get["channel_internal_name"])
|
||||
}
|
||||
|
||||
func TestSendSimpleMessageForm(t *testing.T) {
|
||||
@ -137,7 +137,7 @@ func TestSendSimpleMessageForm(t *testing.T) {
|
||||
|
||||
msg1Get := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, "/api/messages/"+fmt.Sprintf("%v", msg1["scn_msg_id"]))
|
||||
tt.AssertStrRepEqual(t, "msg.title", "Hello World 9999 [$$$]", msg1Get["title"])
|
||||
tt.AssertStrRepEqual(t, "msg.channel_name", "main", msg1Get["channel_name"])
|
||||
tt.AssertStrRepEqual(t, "msg.channel_internal_name", "main", msg1Get["channel_internal_name"])
|
||||
}
|
||||
|
||||
func TestSendSimpleMessageFormAndQuery(t *testing.T) {
|
||||
@ -238,7 +238,7 @@ func TestSendSimpleMessageAlt1(t *testing.T) {
|
||||
|
||||
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_name", "main", msg1Get["channel_name"])
|
||||
tt.AssertStrRepEqual(t, "msg.channel_internal_name", "main", msg1Get["channel_internal_name"])
|
||||
}
|
||||
|
||||
func TestSendContentMessage(t *testing.T) {
|
||||
@ -278,12 +278,12 @@ func TestSendContentMessage(t *testing.T) {
|
||||
tt.AssertEqual(t, "len(messages)", 1, len(msgList1.Messages))
|
||||
tt.AssertStrRepEqual(t, "msg.title", "HelloWorld_042", msgList1.Messages[0]["title"])
|
||||
tt.AssertStrRepEqual(t, "msg.content", "I am Content\nasdf", msgList1.Messages[0]["content"])
|
||||
tt.AssertStrRepEqual(t, "msg.channel_name", "main", msgList1.Messages[0]["channel_name"])
|
||||
tt.AssertStrRepEqual(t, "msg.channel_internal_name", "main", msgList1.Messages[0]["channel_internal_name"])
|
||||
|
||||
msg1Get := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, "/api/messages/"+fmt.Sprintf("%v", msg1["scn_msg_id"]))
|
||||
tt.AssertStrRepEqual(t, "msg.title", "HelloWorld_042", msg1Get["title"])
|
||||
tt.AssertStrRepEqual(t, "msg.content", "I am Content\nasdf", msg1Get["content"])
|
||||
tt.AssertStrRepEqual(t, "msg.channel_name", "main", msg1Get["channel_name"])
|
||||
tt.AssertStrRepEqual(t, "msg.channel_internal_name", "main", msg1Get["channel_internal_name"])
|
||||
}
|
||||
|
||||
func TestSendWithSendername(t *testing.T) {
|
||||
@ -326,13 +326,13 @@ func TestSendWithSendername(t *testing.T) {
|
||||
tt.AssertStrRepEqual(t, "msg.title", "HelloWorld_xyz", msgList1.Messages[0]["title"])
|
||||
tt.AssertStrRepEqual(t, "msg.content", "Unicode: 日本 - yäy\000\n\t\x00...", msgList1.Messages[0]["content"])
|
||||
tt.AssertStrRepEqual(t, "msg.sender_name", "localhorst", msgList1.Messages[0]["sender_name"])
|
||||
tt.AssertStrRepEqual(t, "msg.channel_name", "main", msgList1.Messages[0]["channel_name"])
|
||||
tt.AssertStrRepEqual(t, "msg.channel_internal_name", "main", msgList1.Messages[0]["channel_internal_name"])
|
||||
|
||||
msg1Get := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, "/api/messages/"+fmt.Sprintf("%v", msg1["scn_msg_id"]))
|
||||
tt.AssertStrRepEqual(t, "msg.title", "HelloWorld_xyz", msg1Get["title"])
|
||||
tt.AssertStrRepEqual(t, "msg.content", "Unicode: 日本 - yäy\000\n\t\x00...", msg1Get["content"])
|
||||
tt.AssertStrRepEqual(t, "msg.sender_name", "localhorst", msg1Get["sender_name"])
|
||||
tt.AssertStrRepEqual(t, "msg.channel_name", "main", msg1Get["channel_name"])
|
||||
tt.AssertStrRepEqual(t, "msg.channel_internal_name", "main", msg1Get["channel_internal_name"])
|
||||
}
|
||||
|
||||
func TestSendLongContent(t *testing.T) {
|
||||
@ -377,20 +377,20 @@ func TestSendLongContent(t *testing.T) {
|
||||
tt.AssertEqual(t, "len(messages)", 1, len(msgList1.Messages))
|
||||
tt.AssertStrRepEqual(t, "msg.title", "HelloWorld_042", msgList1.Messages[0]["title"])
|
||||
tt.AssertNotStrRepEqual(t, "msg.content", longContent, msgList1.Messages[0]["content"])
|
||||
tt.AssertStrRepEqual(t, "msg.channel_name", "main", msgList1.Messages[0]["channel_name"])
|
||||
tt.AssertStrRepEqual(t, "msg.channel_internal_name", "main", msgList1.Messages[0]["channel_internal_name"])
|
||||
tt.AssertStrRepEqual(t, "msg.trimmmed", true, msgList1.Messages[0]["trimmed"])
|
||||
|
||||
msgList2 := tt.RequestAuthGet[mglist](t, admintok, baseUrl, "/api/messages?trimmed=false")
|
||||
tt.AssertEqual(t, "len(messages)", 1, len(msgList2.Messages))
|
||||
tt.AssertStrRepEqual(t, "msg.title", "HelloWorld_042", msgList2.Messages[0]["title"])
|
||||
tt.AssertStrRepEqual(t, "msg.content", longContent, msgList2.Messages[0]["content"])
|
||||
tt.AssertStrRepEqual(t, "msg.channel_name", "main", msgList2.Messages[0]["channel_name"])
|
||||
tt.AssertStrRepEqual(t, "msg.channel_internal_name", "main", msgList2.Messages[0]["channel_internal_name"])
|
||||
tt.AssertStrRepEqual(t, "msg.trimmmed", false, msgList2.Messages[0]["trimmed"])
|
||||
|
||||
msg1Get := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, "/api/messages/"+fmt.Sprintf("%v", msg1["scn_msg_id"]))
|
||||
tt.AssertStrRepEqual(t, "msg.title", "HelloWorld_042", msg1Get["title"])
|
||||
tt.AssertStrRepEqual(t, "msg.titcontentle", longContent, msg1Get["content"])
|
||||
tt.AssertStrRepEqual(t, "msg.channel_name", "main", msg1Get["channel_name"])
|
||||
tt.AssertStrRepEqual(t, "msg.channel_internal_name", "main", msg1Get["channel_internal_name"])
|
||||
tt.AssertStrRepEqual(t, "msg.trimmmed", false, msg1Get["trimmed"])
|
||||
}
|
||||
|
||||
@ -859,7 +859,7 @@ func TestSendWithTimestamp(t *testing.T) {
|
||||
tt.AssertEqual(t, "len(messages)", 1, len(msgList1.Messages))
|
||||
tt.AssertStrRepEqual(t, "msg.title", "TTT", msgList1.Messages[0]["title"])
|
||||
tt.AssertStrRepEqual(t, "msg.content", nil, msgList1.Messages[0]["sender_name"])
|
||||
tt.AssertStrRepEqual(t, "msg.channel_name", "main", msgList1.Messages[0]["channel_name"])
|
||||
tt.AssertStrRepEqual(t, "msg.channel_internal_name", "main", msgList1.Messages[0]["channel_internal_name"])
|
||||
|
||||
tm1, err := time.Parse(time.RFC3339Nano, msgList1.Messages[0]["timestamp"].(string))
|
||||
tt.TestFailIfErr(t, err)
|
||||
@ -868,7 +868,7 @@ func TestSendWithTimestamp(t *testing.T) {
|
||||
msg1Get := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, "/api/messages/"+fmt.Sprintf("%v", msg1["scn_msg_id"]))
|
||||
tt.AssertStrRepEqual(t, "msg.title", "TTT", msg1Get["title"])
|
||||
tt.AssertStrRepEqual(t, "msg.content", nil, msg1Get["sender_name"])
|
||||
tt.AssertStrRepEqual(t, "msg.channel_name", "main", msg1Get["channel_name"])
|
||||
tt.AssertStrRepEqual(t, "msg.channel_internal_name", "main", msg1Get["channel_internal_name"])
|
||||
|
||||
tmg1, err := time.Parse(time.RFC3339Nano, msg1Get["timestamp"].(string))
|
||||
tt.TestFailIfErr(t, err)
|
||||
@ -1022,7 +1022,7 @@ func TestSendCompat(t *testing.T) {
|
||||
|
||||
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_name", "main", msg1Get["channel_name"])
|
||||
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)
|
||||
|
||||
@ -1098,8 +1098,8 @@ func TestSendToNewChannel(t *testing.T) {
|
||||
|
||||
{
|
||||
clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid))
|
||||
tt.AssertEqual(t, "chan.len", 1, len(clist.Channels))
|
||||
tt.AssertEqual(t, "chan.name", "main", clist.Channels[0]["name"])
|
||||
tt.AssertMappedSet(t, "channels", []string{"main"}, clist.Channels, "display_name")
|
||||
tt.AssertMappedSet(t, "channels", []string{"main"}, clist.Channels, "internal_name")
|
||||
}
|
||||
|
||||
tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
||||
@ -1112,8 +1112,8 @@ func TestSendToNewChannel(t *testing.T) {
|
||||
|
||||
{
|
||||
clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid))
|
||||
tt.AssertEqual(t, "chan.len", 1, len(clist.Channels))
|
||||
tt.AssertEqual(t, "chan.name", "main", clist.Channels[0]["name"])
|
||||
tt.AssertMappedSet(t, "channels", []string{"main"}, clist.Channels, "display_name")
|
||||
tt.AssertMappedSet(t, "channels", []string{"main"}, clist.Channels, "internal_name")
|
||||
}
|
||||
|
||||
tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
||||
@ -1126,9 +1126,8 @@ func TestSendToNewChannel(t *testing.T) {
|
||||
|
||||
{
|
||||
clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid))
|
||||
tt.AssertEqual(t, "chan-count", 2, len(clist.Channels))
|
||||
tt.AssertArrAny(t, "chan.has('main')", clist.Channels, func(msg gin.H) bool { return msg["name"].(string) == "main" })
|
||||
tt.AssertArrAny(t, "chan.has('test')", clist.Channels, func(msg gin.H) bool { return msg["name"].(string) == "test" })
|
||||
tt.AssertMappedSet(t, "channels", []string{"main", "test"}, clist.Channels, "display_name")
|
||||
tt.AssertMappedSet(t, "channels", []string{"main", "test"}, clist.Channels, "internal_name")
|
||||
}
|
||||
|
||||
tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
||||
@ -1140,9 +1139,8 @@ func TestSendToNewChannel(t *testing.T) {
|
||||
|
||||
{
|
||||
clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid))
|
||||
tt.AssertEqual(t, "chan-count", 2, len(clist.Channels))
|
||||
tt.AssertArrAny(t, "chan.has('main')", clist.Channels, func(msg gin.H) bool { return msg["name"].(string) == "main" })
|
||||
tt.AssertArrAny(t, "chan.has('test')", clist.Channels, func(msg gin.H) bool { return msg["name"].(string) == "test" })
|
||||
tt.AssertMappedSet(t, "channels", []string{"main", "test"}, clist.Channels, "display_name")
|
||||
tt.AssertMappedSet(t, "channels", []string{"main", "test"}, clist.Channels, "internal_name")
|
||||
}
|
||||
}
|
||||
|
||||
@ -1166,8 +1164,9 @@ func TestSendToManualChannel(t *testing.T) {
|
||||
}
|
||||
|
||||
{
|
||||
chan0 := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid))
|
||||
tt.AssertEqual(t, "chan-count", 0, len(chan0.Channels))
|
||||
clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid))
|
||||
tt.AssertMappedSet(t, "channels", []string{}, clist.Channels, "display_name")
|
||||
tt.AssertMappedSet(t, "channels", []string{}, clist.Channels, "internal_name")
|
||||
}
|
||||
|
||||
tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
||||
@ -1178,8 +1177,8 @@ func TestSendToManualChannel(t *testing.T) {
|
||||
|
||||
{
|
||||
clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid))
|
||||
tt.AssertEqual(t, "chan.len", 1, len(clist.Channels))
|
||||
tt.AssertEqual(t, "chan.name", "main", clist.Channels[0]["name"])
|
||||
tt.AssertMappedSet(t, "channels", []string{"main"}, clist.Channels, "display_name")
|
||||
tt.AssertMappedSet(t, "channels", []string{"main"}, clist.Channels, "internal_name")
|
||||
}
|
||||
|
||||
tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
||||
@ -1193,7 +1192,8 @@ func TestSendToManualChannel(t *testing.T) {
|
||||
{
|
||||
clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid))
|
||||
tt.AssertEqual(t, "chan.len", 1, len(clist.Channels))
|
||||
tt.AssertEqual(t, "chan.name", "main", clist.Channels[0]["name"])
|
||||
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{
|
||||
@ -1202,9 +1202,8 @@ func TestSendToManualChannel(t *testing.T) {
|
||||
|
||||
{
|
||||
clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid))
|
||||
tt.AssertEqual(t, "chan-count", 2, len(clist.Channels))
|
||||
tt.AssertArrAny(t, "chan.has('main')", clist.Channels, func(msg gin.H) bool { return msg["name"].(string) == "main" })
|
||||
tt.AssertArrAny(t, "chan.has('test')", clist.Channels, func(msg gin.H) bool { return msg["name"].(string) == "test" })
|
||||
tt.AssertMappedSet(t, "channels", []string{"main", "test"}, clist.Channels, "display_name")
|
||||
tt.AssertMappedSet(t, "channels", []string{"main", "test"}, clist.Channels, "internal_name")
|
||||
}
|
||||
|
||||
tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
||||
@ -1217,9 +1216,8 @@ func TestSendToManualChannel(t *testing.T) {
|
||||
|
||||
{
|
||||
clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid))
|
||||
tt.AssertEqual(t, "chan-count", 2, len(clist.Channels))
|
||||
tt.AssertArrAny(t, "chan.has('main')", clist.Channels, func(msg gin.H) bool { return msg["name"].(string) == "main" })
|
||||
tt.AssertArrAny(t, "chan.has('test')", clist.Channels, func(msg gin.H) bool { return msg["name"].(string) == "test" })
|
||||
tt.AssertMappedSet(t, "channels", []string{"main", "test"}, clist.Channels, "display_name")
|
||||
tt.AssertMappedSet(t, "channels", []string{"main", "test"}, clist.Channels, "internal_name")
|
||||
}
|
||||
|
||||
tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
||||
@ -1231,9 +1229,8 @@ func TestSendToManualChannel(t *testing.T) {
|
||||
|
||||
{
|
||||
clist := tt.RequestAuthGet[chanlist](t, admintok, baseUrl, fmt.Sprintf("/api/users/%d/channels", uid))
|
||||
tt.AssertEqual(t, "chan-count", 2, len(clist.Channels))
|
||||
tt.AssertArrAny(t, "chan.has('main')", clist.Channels, func(msg gin.H) bool { return msg["name"].(string) == "main" })
|
||||
tt.AssertArrAny(t, "chan.has('test')", clist.Channels, func(msg gin.H) bool { return msg["name"].(string) == "test" })
|
||||
tt.AssertMappedSet(t, "channels", []string{"main", "test"}, clist.Channels, "display_name")
|
||||
tt.AssertMappedSet(t, "channels", []string{"main", "test"}, clist.Channels, "internal_name")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -203,7 +203,14 @@ func AssertMultiNonEmpty(t *testing.T, key string, args ...any) {
|
||||
|
||||
func AssertMappedSet[T langext.OrderedConstraint](t *testing.T, key string, expected []T, values []gin.H, objkey string) {
|
||||
|
||||
actual := langext.ArrMap(values, func(v gin.H) T { return v[objkey].(T) })
|
||||
actual := make([]T, 0)
|
||||
for idx, vv := range values {
|
||||
if tv, ok := vv[objkey].(T); ok {
|
||||
actual = append(actual, tv)
|
||||
} else {
|
||||
TestFailFmt(t, "[%s]->[%d] is wrong type (expected: %T, actual: %T)", key, idx, *new(T), vv)
|
||||
}
|
||||
}
|
||||
|
||||
langext.Sort(actual)
|
||||
langext.Sort(expected)
|
||||
|
@ -86,8 +86,10 @@ var userExamples = []userex{
|
||||
{9, true, "UniqueUnicorn", "Galaxy Quest", "2023.1", "ANDROID", "FCM_TOK_EX_010", ""},
|
||||
{10, false, "", "", "", "", "", ""},
|
||||
{11, false, "", "", "", "", "", "ANDROID|v2|PURCHASED:PRO_TOK_002"},
|
||||
{12, true, "ChanTester1", "StarfireXX", "1.x", "IOS", "FCM_TOK_EX_012", ""},
|
||||
{13, true, "ChanTester2", "StarfireXX", "1.x", "IOS", "FCM_TOK_EX_013", ""},
|
||||
{12, true, "NoMessageUser", "Ocean Explorer", "737edc01", "IOS", "FCM_TOK_EX_014", ""},
|
||||
{13, false, "EmptyUser", "", "", "", "", ""},
|
||||
{14, true, "ChanTester1", "StarfireXX", "1.x", "IOS", "FCM_TOK_EX_012", ""},
|
||||
{15, true, "ChanTester2", "StarfireXX", "1.x", "IOS", "FCM_TOK_EX_013", ""},
|
||||
}
|
||||
|
||||
var clientExamples = []clientex{
|
||||
@ -267,14 +269,14 @@ var messageExamples = []msgex{
|
||||
{11, "Promotions", "", PX, AKEY, "Join Our VIP Club and Enjoy Exclusive Benefits", "Sign up for our VIP club and enjoy exclusive benefits like early access to sales, special offers, and personalized service. Don't miss out on this exclusive opportunity.", timeext.FromHours(2.32)},
|
||||
{11, "Promotions", "", P2, SKEY, "Summer Clearance: Save Up to 75% on Your Favorite Products", "It's time for our annual summer clearance sale! Save up to 75% on your favorite products, from clothing and accessories to home decor and more.", timeext.FromHours(1.87)},
|
||||
|
||||
{12, "", "", P0, SKEY, "New Product Launch", "We are excited to announce the launch of our new product, the XYZ widget", 0},
|
||||
{12, "chan_self_subscribed", "", P0, SKEY, "Important Update", "We have released a critical update", 0},
|
||||
{12, "chan_self_unsub", "", P0, SKEY, "Reminder: Upcoming Maintenance", "", 0},
|
||||
{14, "", "", P0, SKEY, "New Product Launch", "We are excited to announce the launch of our new product, the XYZ widget", 0},
|
||||
{14, "chan_self_subscribed", "", P0, SKEY, "Important Update", "We have released a critical update", 0},
|
||||
{14, "chan_self_unsub", "", P0, SKEY, "Reminder: Upcoming Maintenance", "", 0},
|
||||
|
||||
{13, "", "", P0, SKEY, "New Feature Available", "ability to schedule appointments", 0},
|
||||
{13, "chan_other_nosub", "", P0, SKEY, "Account Suspended", "Please contact us", 0},
|
||||
{13, "chan_other_request", "", P0, SKEY, "Invitation to Beta Test", "", 0},
|
||||
{13, "chan_other_accepted", "", P0, SKEY, "New Blog Post", "Congratulations on your promotion! We are proud", 0},
|
||||
{15, "", "", P0, SKEY, "New Feature Available", "ability to schedule appointments", 0},
|
||||
{15, "chan_other_nosub", "", P0, SKEY, "Account Suspended", "Please contact us", 0},
|
||||
{15, "chan_other_request", "", P0, SKEY, "Invitation to Beta Test", "", 0},
|
||||
{15, "chan_other_accepted", "", P0, SKEY, "New Blog Post", "Congratulations on your promotion! We are proud", 0},
|
||||
}
|
||||
|
||||
type DefData struct {
|
||||
@ -373,6 +375,12 @@ func InitDefaultData(t *testing.T, ws *logic.Application) DefData {
|
||||
RequestPost[gin.H](t, baseUrl, "/", body)
|
||||
}
|
||||
|
||||
// create manual channels
|
||||
|
||||
{
|
||||
RequestAuthPost[Void](t, users[9].AdminKey, baseUrl, fmt.Sprintf("/api/users/%d/channels", users[9].UID), gin.H{"name": "manual@chan"})
|
||||
}
|
||||
|
||||
// Sub/Unsub for Users 12+13
|
||||
|
||||
{
|
||||
|
@ -163,10 +163,10 @@ func RequestAny[TResult any](t *testing.T, akey string, method string, baseURL s
|
||||
return data
|
||||
}
|
||||
|
||||
func RequestAuthAnyShouldFail(t *testing.T, akey string, method string, baseURL string, urlSuffix string, body any, statusCode int, errcode apierr.APIError) {
|
||||
func RequestAuthAnyShouldFail(t *testing.T, akey string, method string, baseURL string, urlSuffix string, body any, expectedStatusCode int, errcode apierr.APIError) {
|
||||
client := http.Client{}
|
||||
|
||||
TPrintf("[-> REQUEST] (%s) %s%s [%s] (should-fail with %d/%d)\n", method, baseURL, urlSuffix, langext.Conditional(akey == "", "NO AUTH", "AUTH"), statusCode, errcode)
|
||||
TPrintf("[-> REQUEST] (%s) %s%s [%s] (should-fail with %d/%d)\n", method, baseURL, urlSuffix, langext.Conditional(akey == "", "NO AUTH", "AUTH"), expectedStatusCode, errcode)
|
||||
|
||||
bytesbody := make([]byte, 0)
|
||||
contentType := ""
|
||||
@ -224,17 +224,17 @@ func RequestAuthAnyShouldFail(t *testing.T, akey string, method string, baseURL
|
||||
TPrintln("")
|
||||
TPrintf("---------------- RESPONSE (%d) ----------------\n", resp.StatusCode)
|
||||
TPrintln(langext.TryPrettyPrintJson(string(respBodyBin)))
|
||||
if (statusCode != 0 && resp.StatusCode != statusCode) || (statusCode == 0 && resp.StatusCode == 200) {
|
||||
if (expectedStatusCode != 0 && resp.StatusCode != expectedStatusCode) || (expectedStatusCode == 0 && resp.StatusCode == 200) {
|
||||
TryPrintTraceObj("---------------- -------- ----------------", respBodyBin, "")
|
||||
}
|
||||
TPrintln("---------------- -------- ----------------")
|
||||
TPrintln("")
|
||||
|
||||
if statusCode != 0 && resp.StatusCode != statusCode {
|
||||
TestFailFmt(t, "Statuscode != %d (expected failure)", statusCode)
|
||||
if expectedStatusCode != 0 && resp.StatusCode != expectedStatusCode {
|
||||
TestFailFmt(t, "Statuscode != %d (expected failure, but got %d)", expectedStatusCode, resp.StatusCode)
|
||||
}
|
||||
if statusCode == 0 && resp.StatusCode == 200 {
|
||||
TestFailFmt(t, "Statuscode == %d (expected failure)", resp.StatusCode)
|
||||
if expectedStatusCode == 0 && resp.StatusCode == 200 {
|
||||
TestFailFmt(t, "Statuscode == %d (expected any failure, but got %d)", resp.StatusCode, resp.StatusCode)
|
||||
}
|
||||
|
||||
var data gin.H
|
||||
|
Loading…
Reference in New Issue
Block a user