ListChannelMessages()
This commit is contained in:
parent
80f3b982d2
commit
b56c021356
@ -494,7 +494,7 @@ func (h APIHandler) GetChannel(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
|
|
||||||
channel, err := h.database.GetChannel(ctx, u.UserID, u.ChannelID)
|
channel, err := h.database.GetChannel(ctx, u.UserID, u.ChannelID)
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
return ginresp.InternAPIError(404, apierr.CLIENT_NOT_FOUND, "Channel not found", err)
|
return ginresp.InternAPIError(404, apierr.CHANNEL_NOT_FOUND, "Channel not found", err)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ginresp.InternAPIError(500, apierr.DATABASE_ERROR, "Failed to query channel", err)
|
return ginresp.InternAPIError(500, apierr.DATABASE_ERROR, "Failed to query channel", err)
|
||||||
@ -503,8 +503,98 @@ func (h APIHandler) GetChannel(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, channel.JSON()))
|
return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, channel.JSON()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h APIHandler) GetChannelMessages(g *gin.Context) ginresp.HTTPResponse {
|
// ListChannelMessages swaggerdoc
|
||||||
return ginresp.NotImplemented() //TODO
|
//
|
||||||
|
// @Summary List messages of a channel
|
||||||
|
// @Description The next_page_token is an opaque token, the special value "@start" (or empty-string) is the beginning and "@end" is the end
|
||||||
|
// @Description Simply start the pagination without a next_page_token and get the next page by calling this endpoint with the returned next_page_token of the last query
|
||||||
|
// @Description If there are no more entries the token "@end" will be returned
|
||||||
|
// @Description By default we return long messages with a trimmed body, if trimmed=false is supplied we return full messages (this reduces the max page_size)
|
||||||
|
// @ID api-channel-messages
|
||||||
|
//
|
||||||
|
// @Param query_data query handler.ListChannelMessages.query false " "
|
||||||
|
//
|
||||||
|
// @Success 200 {object} handler.ListChannelMessages.response
|
||||||
|
// @Failure 400 {object} ginresp.apiError
|
||||||
|
// @Failure 401 {object} ginresp.apiError
|
||||||
|
// @Failure 404 {object} ginresp.apiError
|
||||||
|
// @Failure 500 {object} ginresp.apiError
|
||||||
|
//
|
||||||
|
// @Router /api-v2/users/{uid}/channels/{cid}/messages [GET]
|
||||||
|
func (h APIHandler) ListChannelMessages(g *gin.Context) ginresp.HTTPResponse {
|
||||||
|
type uri struct {
|
||||||
|
ChannelUserID int64 `uri:"uid"`
|
||||||
|
ChannelID int64 `uri:"cid"`
|
||||||
|
}
|
||||||
|
type query struct {
|
||||||
|
PageSize *int `form:"page_size"`
|
||||||
|
NextPageToken *string `form:"next_page_token"`
|
||||||
|
Filter *string `form:"filter"`
|
||||||
|
Trimmed *bool `form:"trimmed"`
|
||||||
|
}
|
||||||
|
type response struct {
|
||||||
|
Messages []models.MessageJSON `json:"messages"`
|
||||||
|
NextPageToken string `json:"next_page_token"`
|
||||||
|
PageSize int `json:"page_size"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var u uri
|
||||||
|
var q query
|
||||||
|
ctx, errResp := h.app.StartRequest(g, &u, &q, nil)
|
||||||
|
if errResp != nil {
|
||||||
|
return *errResp
|
||||||
|
}
|
||||||
|
defer ctx.Cancel()
|
||||||
|
|
||||||
|
trimmed := langext.Coalesce(q.Trimmed, true)
|
||||||
|
|
||||||
|
maxPageSize := langext.Conditional(trimmed, 16, 256)
|
||||||
|
|
||||||
|
pageSize := mathext.Clamp(langext.Coalesce(q.PageSize, 64), 1, maxPageSize)
|
||||||
|
|
||||||
|
if permResp := ctx.CheckPermissionRead(); permResp != nil {
|
||||||
|
return *permResp
|
||||||
|
}
|
||||||
|
|
||||||
|
channel, err := h.database.GetChannel(ctx, u.ChannelUserID, u.ChannelID)
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return ginresp.InternAPIError(404, apierr.CHANNEL_NOT_FOUND, "Channel not found", err)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return ginresp.InternAPIError(500, apierr.DATABASE_ERROR, "Failed to query channel", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
userid := *ctx.GetPermissionUserID()
|
||||||
|
|
||||||
|
sub, err := h.database.GetSubscriptionBySubscriber(ctx, userid, channel.ChannelID)
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return ginresp.InternAPIError(401, apierr.USER_AUTH_FAILED, "You are not authorized for this action", nil)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return ginresp.InternAPIError(500, apierr.DATABASE_ERROR, "Failed to query subscription", err)
|
||||||
|
}
|
||||||
|
if !sub.Confirmed {
|
||||||
|
return ginresp.InternAPIError(401, apierr.USER_AUTH_FAILED, "You are not authorized for this action", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
tok, err := cursortoken.Decode(langext.Coalesce(q.NextPageToken, ""))
|
||||||
|
if err != nil {
|
||||||
|
return ginresp.InternAPIError(500, apierr.PAGETOKEN_ERROR, "Failed to decode next_page_token", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
messages, npt, err := h.database.ListChannelMessages(ctx, channel.ChannelID, pageSize, tok)
|
||||||
|
if err != nil {
|
||||||
|
return ginresp.InternAPIError(500, apierr.DATABASE_ERROR, "Failed to query messages", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var res []models.MessageJSON
|
||||||
|
if trimmed {
|
||||||
|
res = langext.ArrMap(messages, func(v models.Message) models.MessageJSON { return v.TrimmedJSON() })
|
||||||
|
} else {
|
||||||
|
res = langext.ArrMap(messages, func(v models.Message) models.MessageJSON { return v.FullJSON() })
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, response{Messages: res, NextPageToken: npt.Token(), PageSize: pageSize}))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListUserSubscriptions swaggerdoc
|
// ListUserSubscriptions swaggerdoc
|
||||||
|
@ -105,7 +105,7 @@ func (r *Router) Init(e *gin.Engine) {
|
|||||||
|
|
||||||
apiv2.GET("/users/:uid/channels", ginresp.Wrap(r.apiHandler.ListChannels))
|
apiv2.GET("/users/:uid/channels", ginresp.Wrap(r.apiHandler.ListChannels))
|
||||||
apiv2.GET("/users/:uid/channels/:cid", ginresp.Wrap(r.apiHandler.GetChannel))
|
apiv2.GET("/users/:uid/channels/:cid", ginresp.Wrap(r.apiHandler.GetChannel))
|
||||||
apiv2.GET("/users/:uid/channels/:cid/messages", ginresp.Wrap(r.apiHandler.GetChannelMessages))
|
apiv2.GET("/users/:uid/channels/:cid/messages", ginresp.Wrap(r.apiHandler.ListChannelMessages))
|
||||||
apiv2.GET("/users/:uid/channels/:cid/subscriptions", ginresp.Wrap(r.apiHandler.ListChannelSubscriptions))
|
apiv2.GET("/users/:uid/channels/:cid/subscriptions", ginresp.Wrap(r.apiHandler.ListChannelSubscriptions))
|
||||||
|
|
||||||
apiv2.GET("/users/:uid/subscriptions", ginresp.Wrap(r.apiHandler.ListUserSubscriptions))
|
apiv2.GET("/users/:uid/subscriptions", ginresp.Wrap(r.apiHandler.ListUserSubscriptions))
|
||||||
|
@ -140,3 +140,38 @@ func (db *Database) ListMessages(ctx TxContext, userid int64, pageSize int, inTo
|
|||||||
return data[0:pageSize], outToken, nil
|
return data[0:pageSize], outToken, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *Database) ListChannelMessages(ctx TxContext, channelid int64, pageSize int, inTok cursortoken.CursorToken) ([]models.Message, cursortoken.CursorToken, error) {
|
||||||
|
tx, err := ctx.GetOrCreateTransaction(db)
|
||||||
|
if err != nil {
|
||||||
|
return nil, cursortoken.CursorToken{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if inTok.Mode == cursortoken.CTMEnd {
|
||||||
|
return make([]models.Message, 0), cursortoken.End(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
pageCond := ""
|
||||||
|
if inTok.Mode == cursortoken.CTMNormal {
|
||||||
|
pageCond = fmt.Sprintf("AND ( timestamp_real < %d OR (timestamp_real = %d AND scn_message_id < %d ) )", inTok.Timestamp, inTok.Timestamp, inTok.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := tx.QueryContext(ctx, "SELECT * FROM messages WHERE channel_id = ? "+pageCond+" ORDER BY timestamp_real DESC LIMIT ?",
|
||||||
|
channelid,
|
||||||
|
pageSize+1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, cursortoken.CursorToken{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := models.DecodeMessages(rows)
|
||||||
|
if err != nil {
|
||||||
|
return nil, cursortoken.CursorToken{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(data) <= pageSize {
|
||||||
|
return data, cursortoken.End(), nil
|
||||||
|
} else {
|
||||||
|
outToken := cursortoken.Normal(data[pageSize-1].TimestampReal, data[pageSize-1].SCNMessageID, "DESC")
|
||||||
|
return data[0:pageSize], outToken, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -448,6 +448,67 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api-v2/users/{uid}/channels/{cid}/messages": {
|
||||||
|
"get": {
|
||||||
|
"description": "The next_page_token is an opaque token, the special value \"@start\" (or empty-string) is the beginning and \"@end\" is the end\nSimply start the pagination without a next_page_token and get the next page by calling this endpoint with the returned next_page_token of the last query\nIf there are no more entries the token \"@end\" will be returned\nBy default we return long messages with a trimmed body, if trimmed=false is supplied we return full messages (this reduces the max page_size)",
|
||||||
|
"summary": "List messages of a channel",
|
||||||
|
"operationId": "api-channel-messages",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "filter",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "nextPageToken",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"name": "pageSize",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "boolean",
|
||||||
|
"name": "trimmed",
|
||||||
|
"in": "query"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/handler.ListChannelMessages.response"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/ginresp.apiError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"description": "Unauthorized",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/ginresp.apiError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Not Found",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/ginresp.apiError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/ginresp.apiError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api-v2/users/{uid}/channels/{cid}/subscriptions": {
|
"/api-v2/users/{uid}/channels/{cid}/subscriptions": {
|
||||||
"get": {
|
"get": {
|
||||||
"summary": "List all subscriptions of a channel",
|
"summary": "List all subscriptions of a channel",
|
||||||
@ -1573,6 +1634,23 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"handler.ListChannelMessages.response": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"messages": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/models.MessageJSON"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"next_page_token": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"page_size": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"handler.ListChannelSubscriptions.response": {
|
"handler.ListChannelSubscriptions.response": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -102,6 +102,17 @@ definitions:
|
|||||||
user_key:
|
user_key:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
|
handler.ListChannelMessages.response:
|
||||||
|
properties:
|
||||||
|
messages:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/models.MessageJSON'
|
||||||
|
type: array
|
||||||
|
next_page_token:
|
||||||
|
type: string
|
||||||
|
page_size:
|
||||||
|
type: integer
|
||||||
|
type: object
|
||||||
handler.ListChannelSubscriptions.response:
|
handler.ListChannelSubscriptions.response:
|
||||||
properties:
|
properties:
|
||||||
subscriptions:
|
subscriptions:
|
||||||
@ -707,6 +718,49 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/ginresp.apiError'
|
$ref: '#/definitions/ginresp.apiError'
|
||||||
summary: List all channels of a user
|
summary: List all channels of a user
|
||||||
|
/api-v2/users/{uid}/channels/{cid}/messages:
|
||||||
|
get:
|
||||||
|
description: |-
|
||||||
|
The next_page_token is an opaque token, the special value "@start" (or empty-string) is the beginning and "@end" is the end
|
||||||
|
Simply start the pagination without a next_page_token and get the next page by calling this endpoint with the returned next_page_token of the last query
|
||||||
|
If there are no more entries the token "@end" will be returned
|
||||||
|
By default we return long messages with a trimmed body, if trimmed=false is supplied we return full messages (this reduces the max page_size)
|
||||||
|
operationId: api-channel-messages
|
||||||
|
parameters:
|
||||||
|
- in: query
|
||||||
|
name: filter
|
||||||
|
type: string
|
||||||
|
- in: query
|
||||||
|
name: nextPageToken
|
||||||
|
type: string
|
||||||
|
- in: query
|
||||||
|
name: pageSize
|
||||||
|
type: integer
|
||||||
|
- in: query
|
||||||
|
name: trimmed
|
||||||
|
type: boolean
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/handler.ListChannelMessages.response'
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/ginresp.apiError'
|
||||||
|
"401":
|
||||||
|
description: Unauthorized
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/ginresp.apiError'
|
||||||
|
"404":
|
||||||
|
description: Not Found
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/ginresp.apiError'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/ginresp.apiError'
|
||||||
|
summary: List messages of a channel
|
||||||
/api-v2/users/{uid}/channels/{cid}/subscriptions:
|
/api-v2/users/{uid}/channels/{cid}/subscriptions:
|
||||||
get:
|
get:
|
||||||
operationId: api-chan-subscriptions-list
|
operationId: api-chan-subscriptions-list
|
||||||
|
Loading…
Reference in New Issue
Block a user