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)
|
||||
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 {
|
||||
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()))
|
||||
}
|
||||
|
||||
func (h APIHandler) GetChannelMessages(g *gin.Context) ginresp.HTTPResponse {
|
||||
return ginresp.NotImplemented() //TODO
|
||||
// ListChannelMessages swaggerdoc
|
||||
//
|
||||
// @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
|
||||
|
@ -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/: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/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
|
||||
}
|
||||
}
|
||||
|
||||
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": {
|
||||
"get": {
|
||||
"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": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -102,6 +102,17 @@ definitions:
|
||||
user_key:
|
||||
type: string
|
||||
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:
|
||||
properties:
|
||||
subscriptions:
|
||||
@ -707,6 +718,49 @@ paths:
|
||||
schema:
|
||||
$ref: '#/definitions/ginresp.apiError'
|
||||
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:
|
||||
get:
|
||||
operationId: api-chan-subscriptions-list
|
||||
|
Loading…
Reference in New Issue
Block a user