From 1c05180251ad239d47676fa82a5d74e97c09f7c5 Mon Sep 17 00:00:00 2001 From: Roland Osborne Date: Sun, 3 Jul 2022 00:18:57 -0700 Subject: [PATCH] adding sequence marker to get topics to support staggard loading --- doc/api.oa3 | 33 ++++++- net/server/internal/api_getChannelTopics.go | 104 ++++++++++++++++++-- 2 files changed, 128 insertions(+), 9 deletions(-) diff --git a/doc/api.oa3 b/doc/api.oa3 index 2cda523b..8099491d 100644 --- a/doc/api.oa3 +++ b/doc/api.oa3 @@ -2485,7 +2485,7 @@ paths: get: tags: - content - description: Get channel topic slots. If revision set, detail fields omitted in response + description: Get channel topic slots. If revision set, detail fields omitted in response. Staggered loading supported through sequnce marker. Initially count can be used to limit the topics returned. The sequence marker returned in the header must be used in subsequent queries for the same topic window. operationId: get-channel-topics security: - bearerAuth: [] @@ -2501,16 +2501,43 @@ paths: description: return updated topics since revision required: false schema: - type: string + type: integer + format: int64 + - name: count + in: query + description: limit number of topics from latest when revision not set + required: false + schema: + type: integer + format: int64 + - name: begin + in: query + description: return topics after and including sequence marker + required: false + schema: + type: integer + format: int64 + - name: end + in: query + description: return topics before and not including sequence marker + required: false + schema: + type: integer + format: int64 responses: '200': description: successful operation headers: - X-Topic-Revision: + Topic-Revision: schema: type: integer format: int64 description: current topic revision + Topic-Marker: + schema: + type: integer + format: int64 + description: sequnce marker of first topic when count set content: application/json: schema: diff --git a/net/server/internal/api_getChannelTopics.go b/net/server/internal/api_getChannelTopics.go index ad515978..871abed1 100644 --- a/net/server/internal/api_getChannelTopics.go +++ b/net/server/internal/api_getChannelTopics.go @@ -6,9 +6,23 @@ import ( "databag/internal/store" ) +func reverse(input []store.TopicSlot) []store.TopicSlot { + var output []store.TopicSlot + for i := len(input) - 1; i >= 0; i-- { + output = append(output, input[i]) + } + return output +} + func GetChannelTopics(w http.ResponseWriter, r *http.Request) { var revisionSet bool var revision int64 + var beginSet bool + var begin int64 + var endSet bool + var end int64 + var countSet bool + var count int channelSlot, _, err, code := getChannelSlot(r, false) if err != nil { @@ -25,24 +39,102 @@ func GetChannelTopics(w http.ResponseWriter, r *http.Request) { } } + cnt := r.FormValue("count") + if cnt != "" { + countSet = true + if count, err = strconv.Atoi(cnt); err != nil { + ErrResponse(w, http.StatusBadRequest, err) + return + } + } + + bn := r.FormValue("begin") + if bn != "" { + beginSet = true + if begin, err = strconv.ParseInt(bn, 10, 64); err != nil { + ErrResponse(w, http.StatusBadRequest, err) + return + } + } + + en := r.FormValue("end") + if en != "" { + endSet = true + if end, err = strconv.ParseInt(en, 10, 64); err != nil { + ErrResponse(w, http.StatusBadRequest, err) + return + } + } + response := []*Topic{} if revisionSet { var slots []store.TopicSlot - if err := store.DB.Preload("Topic").Where("channel_id = ? AND revision > ?", channelSlot.Channel.ID, revision).Find(&slots).Error; err != nil { - ErrResponse(w, http.StatusInternalServerError, err) - return + if beginSet && !endSet { + if err := store.DB.Preload("Topic").Where("channel_id = ? AND revision > ? AND id >= ?", channelSlot.Channel.ID, revision, begin).Find(&slots).Error; err != nil { + ErrResponse(w, http.StatusInternalServerError, err) + return + } + } else if !beginSet && endSet { + if err := store.DB.Preload("Topic").Where("channel_id = ? AND revision > ? AND id < ?", channelSlot.Channel.ID, revision, end).Find(&slots).Error; err != nil { + ErrResponse(w, http.StatusInternalServerError, err) + return + } + } else if beginSet && endSet { + if err := store.DB.Preload("Topic").Where("channel_id = ? AND revision > ? AND id >= ? AND id < ?", channelSlot.Channel.ID, revision, begin, end).Find(&slots).Error; err != nil { + ErrResponse(w, http.StatusInternalServerError, err) + return + } + } else { + if err := store.DB.Preload("Topic").Where("channel_id = ? AND revision > ?", channelSlot.Channel.ID, revision).Find(&slots).Error; err != nil { + ErrResponse(w, http.StatusInternalServerError, err) + return + } } for _, slot := range slots { response = append(response, getTopicRevisionModel(&slot)) } } else { var slots []store.TopicSlot - if err := store.DB.Preload("Topic.Assets").Where("channel_id = ?", channelSlot.Channel.ID).Find(&slots).Error; err != nil { - ErrResponse(w, http.StatusInternalServerError, err) - return + if countSet { + if !endSet { + if err := store.DB.Preload("Topic.Assets").Where("channel_id = ?", channelSlot.Channel.ID).Order("id desc").Limit(count).Find(&slots).Error; err != nil { + ErrResponse(w, http.StatusInternalServerError, err) + return + } + } else { + if err := store.DB.Preload("Topic.Assets").Where("channel_id = ? AND id < ?", channelSlot.Channel.ID, end).Order("id desc").Limit(count).Find(&slots).Error; err != nil { + ErrResponse(w, http.StatusInternalServerError, err) + return + } + } + slots = reverse(slots) + } else if beginSet && !endSet { + if err := store.DB.Preload("Topic.Assets").Where("channel_id = ? AND id >= ?", channelSlot.Channel.ID, begin).Find(&slots).Error; err != nil { + ErrResponse(w, http.StatusInternalServerError, err) + return + } + } else if !beginSet && endSet { + if err := store.DB.Preload("Topic.Assets").Where("channel_id = ? AND id < ?", channelSlot.Channel.ID, end).Find(&slots).Error; err != nil { + ErrResponse(w, http.StatusInternalServerError, err) + return + } + } else if beginSet && endSet { + if err := store.DB.Preload("Topic.Assets").Where("channel_id = ? AND id >= ? AND id < ?", channelSlot.Channel.ID, begin, end).Find(&slots).Error; err != nil { + ErrResponse(w, http.StatusInternalServerError, err) + return + } + } else { + if err := store.DB.Preload("Topic.Assets").Where("channel_id = ?", channelSlot.Channel.ID).Find(&slots).Error; err != nil { + ErrResponse(w, http.StatusInternalServerError, err) + return + } } for _, slot := range slots { if slot.Topic != nil { + if countSet { + w.Header().Set("Topic-Index", strconv.FormatUint(uint64(slot.ID), 10)) + countSet = false + } response = append(response, getTopicModel(&slot)) } }