Accept numeric favorite sequence
This commit is contained in:
+10
-9
@@ -408,7 +408,7 @@ type imMessageRevisionRequest struct {
|
|||||||
type imFavoritesBatchCreateRequest struct {
|
type imFavoritesBatchCreateRequest struct {
|
||||||
Messages []struct {
|
Messages []struct {
|
||||||
Topic string `json:"topic"`
|
Topic string `json:"topic"`
|
||||||
Seq int64 `json:"seq,string"`
|
Seq imForwardSeq `json:"seq"`
|
||||||
} `json:"messages"`
|
} `json:"messages"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3557,27 +3557,28 @@ func serveIMFavoriteBatchCreate(wrt http.ResponseWriter, req *http.Request) {
|
|||||||
failed := make([]map[string]any, 0)
|
failed := make([]map[string]any, 0)
|
||||||
for _, src := range body.Messages {
|
for _, src := range body.Messages {
|
||||||
sourceTopic, sourceOK := normalizeIMFavoriteSourceTopic(principal.Uid, src.Topic)
|
sourceTopic, sourceOK := normalizeIMFavoriteSourceTopic(principal.Uid, src.Topic)
|
||||||
if !sourceOK || src.Seq <= 0 {
|
seq := int64(src.Seq)
|
||||||
failed = append(failed, map[string]any{"topic": src.Topic, "seq": src.Seq, "reason": imReasonMalformedRequest})
|
if !sourceOK || seq <= 0 {
|
||||||
|
failed = append(failed, map[string]any{"topic": src.Topic, "seq": seq, "reason": imReasonMalformedRequest})
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err := requireTopicReader(principal, sourceTopic); err != nil {
|
if err := requireTopicReader(principal, sourceTopic); err != nil {
|
||||||
failed = append(failed, map[string]any{"topic": sourceTopic, "seq": src.Seq, "reason": relationErrorReason(err)})
|
failed = append(failed, map[string]any{"topic": sourceTopic, "seq": seq, "reason": relationErrorReason(err)})
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
msg, err := store.Messages.GetOne(principal.TenantID, sourceTopic, src.Seq)
|
msg, err := store.Messages.GetOne(principal.TenantID, sourceTopic, seq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
failed = append(failed, map[string]any{"topic": sourceTopic, "seq": src.Seq, "reason": relationErrorReason(err)})
|
failed = append(failed, map[string]any{"topic": sourceTopic, "seq": seq, "reason": relationErrorReason(err)})
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
idempotencyKey := favoriteItemIdempotencyKey(req.Header.Get("Idempotency-Key"), sourceTopic, src.Seq)
|
idempotencyKey := favoriteItemIdempotencyKey(req.Header.Get("Idempotency-Key"), sourceTopic, seq)
|
||||||
fav := &types.FavoriteItem{
|
fav := &types.FavoriteItem{
|
||||||
TenantID: principal.TenantID,
|
TenantID: principal.TenantID,
|
||||||
FavoriteID: "fav_" + store.Store.GetUidString(),
|
FavoriteID: "fav_" + store.Store.GetUidString(),
|
||||||
UserID: principal.Uid,
|
UserID: principal.Uid,
|
||||||
ItemType: imMessageType(msg),
|
ItemType: imMessageType(msg),
|
||||||
SourceTopic: sourceTopic,
|
SourceTopic: sourceTopic,
|
||||||
SourceSeq: src.Seq,
|
SourceSeq: seq,
|
||||||
Snapshot: favoriteSnapshot(msg),
|
Snapshot: favoriteSnapshot(msg),
|
||||||
AttachmentRefs: favoriteAttachmentRefs(msg),
|
AttachmentRefs: favoriteAttachmentRefs(msg),
|
||||||
Availability: messageAvailability(*msg),
|
Availability: messageAvailability(*msg),
|
||||||
@@ -3588,7 +3589,7 @@ func serveIMFavoriteBatchCreate(wrt http.ResponseWriter, req *http.Request) {
|
|||||||
}
|
}
|
||||||
item, err := store.Favorites.Create(principal.TenantID, fav)
|
item, err := store.Favorites.Create(principal.TenantID, fav)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
failed = append(failed, map[string]any{"topic": src.Topic, "seq": src.Seq, "reason": relationErrorReason(err)})
|
failed = append(failed, map[string]any{"topic": src.Topic, "seq": seq, "reason": relationErrorReason(err)})
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if item.FavoriteID == fav.FavoriteID {
|
if item.FavoriteID == fav.FavoriteID {
|
||||||
|
|||||||
@@ -4929,6 +4929,63 @@ func TestServeIMFavoriteBatchCreateCountsExisting(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestServeIMFavoriteBatchCreateAcceptsNumericSeq(t *testing.T) {
|
||||||
|
ctrl := gomock.NewController(t)
|
||||||
|
defer ctrl.Finish()
|
||||||
|
mockStore := expectIMBearer(t, ctrl, "token-1", 7, 42)
|
||||||
|
mockStore.EXPECT().GetUidString().Return("new-fav")
|
||||||
|
|
||||||
|
oldFavorites := store.Favorites
|
||||||
|
oldSubs := store.Subs
|
||||||
|
oldMessages := store.Messages
|
||||||
|
now := time.Date(2026, 8, 13, 9, 0, 0, 0, time.UTC)
|
||||||
|
favorites := &testFavoritesStore{}
|
||||||
|
subs := mock_store.NewMockSubsPersistenceInterface(ctrl)
|
||||||
|
messages := mock_store.NewMockMessagesPersistenceInterface(ctrl)
|
||||||
|
store.Favorites = favorites
|
||||||
|
store.Subs = subs
|
||||||
|
store.Messages = messages
|
||||||
|
t.Cleanup(func() {
|
||||||
|
store.Favorites = oldFavorites
|
||||||
|
store.Subs = oldSubs
|
||||||
|
store.Messages = oldMessages
|
||||||
|
})
|
||||||
|
|
||||||
|
subs.EXPECT().Get(types.TenantID(7), "grpAlpha", types.Uid(42), false).Return(&types.Subscription{
|
||||||
|
TenantID: 7,
|
||||||
|
User: types.Uid(42).String(),
|
||||||
|
Topic: "grpAlpha",
|
||||||
|
ModeWant: types.ModeCPublic,
|
||||||
|
ModeGiven: types.ModeCPublic,
|
||||||
|
}, nil)
|
||||||
|
messages.EXPECT().GetOne(types.TenantID(7), "grpAlpha", int64(3)).Return(&types.Message{
|
||||||
|
TenantID: 7,
|
||||||
|
Topic: "grpAlpha",
|
||||||
|
SeqId: 3,
|
||||||
|
From: types.Uid(42).String(),
|
||||||
|
Head: types.KVMap{"type": "text"},
|
||||||
|
Content: map[string]any{"text": "hello"},
|
||||||
|
ObjHeader: types.ObjHeader{
|
||||||
|
CreatedAt: now,
|
||||||
|
},
|
||||||
|
}, nil)
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodPost, "/v1/im/favorites/batch", bytes.NewBufferString(`{"messages":[{"topic":"grpAlpha","seq":3}]}`))
|
||||||
|
req.Header.Set("Authorization", "Bearer token-1")
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
serveIMFavoriteResource(rec, req)
|
||||||
|
if rec.Code != http.StatusOK {
|
||||||
|
t.Fatalf("create status: got %d body=%s", rec.Code, rec.Body.String())
|
||||||
|
}
|
||||||
|
data := decodeIMHTTPResponse(t, rec).Data.(map[string]any)
|
||||||
|
if data["created_count"].(float64) != 1 || data["existing_count"].(float64) != 0 {
|
||||||
|
t.Fatalf("unexpected favorite counts: %#v", data)
|
||||||
|
}
|
||||||
|
if favorites.created == nil || favorites.created.SourceSeq != 3 {
|
||||||
|
t.Fatalf("favorite source seq was not saved: %#v", favorites.created)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestFavoriteItemIdempotencyKeyIncludesMessageIdentity(t *testing.T) {
|
func TestFavoriteItemIdempotencyKeyIncludesMessageIdentity(t *testing.T) {
|
||||||
first := favoriteItemIdempotencyKey("same-key", "grpAlpha", 1)
|
first := favoriteItemIdempotencyKey("same-key", "grpAlpha", 1)
|
||||||
second := favoriteItemIdempotencyKey("same-key", "grpAlpha", 2)
|
second := favoriteItemIdempotencyKey("same-key", "grpAlpha", 2)
|
||||||
|
|||||||
Reference in New Issue
Block a user