[MM-44765] Add API method for getting post's information from permalink (#21518)
* add api for getting post's information * add information about current user state Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
0c64252c9f
Коммит
3059abdd88
22
api4/post.go
22
api4/post.go
@@ -23,6 +23,7 @@ func (api *API) InitPost() {
|
||||
api.BaseRoutes.Posts.Handle("/ids", api.APISessionRequired(getPostsByIds)).Methods("POST")
|
||||
api.BaseRoutes.Posts.Handle("/ephemeral", api.APISessionRequired(createEphemeralPost)).Methods("POST")
|
||||
api.BaseRoutes.Post.Handle("/thread", api.APISessionRequired(getPostThread)).Methods("GET")
|
||||
api.BaseRoutes.Post.Handle("/info", api.APISessionRequired(getPostInfo)).Methods("GET")
|
||||
api.BaseRoutes.Post.Handle("/files/info", api.APISessionRequired(getFileInfosForPost)).Methods("GET")
|
||||
api.BaseRoutes.PostsForChannel.Handle("", api.APISessionRequired(getPostsForChannel)).Methods("GET")
|
||||
api.BaseRoutes.PostsForUser.Handle("/flagged", api.APISessionRequired(getFlaggedPostsForUser)).Methods("GET")
|
||||
@@ -1055,3 +1056,24 @@ func getFileInfosForPost(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set(model.HeaderEtagServer, model.GetEtagForFileInfos(infos))
|
||||
w.Write(js)
|
||||
}
|
||||
|
||||
func getPostInfo(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
c.RequirePostId()
|
||||
if c.Err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
info, appErr := c.App.GetPostInfo(c.AppContext, c.Params.PostId)
|
||||
if appErr != nil {
|
||||
c.Err = appErr
|
||||
return
|
||||
}
|
||||
|
||||
js, err := json.Marshal(info)
|
||||
if err != nil {
|
||||
c.Err = model.NewAppError("getPostInfo", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
return
|
||||
}
|
||||
|
||||
w.Write(js)
|
||||
}
|
||||
|
||||
@@ -3343,6 +3343,262 @@ func TestPostReminder(t *testing.T) {
|
||||
require.Truef(t, caught, "User should have received %s event", model.WebsocketEventEphemeralMessage)
|
||||
}
|
||||
|
||||
func TestPostGetInfo(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
client := th.Client
|
||||
sysadminClient := th.SystemAdminClient
|
||||
sysadminClient.AddTeamMember(th.BasicTeam.Id, th.SystemAdminUser.Id)
|
||||
|
||||
openChannel, _, err := client.CreateChannel(&model.Channel{TeamId: th.BasicTeam.Id, Type: model.ChannelTypeOpen, Name: "open-channel", DisplayName: "Open Channel"})
|
||||
require.NoError(t, err)
|
||||
sysadminClient.AddChannelMember(openChannel.Id, th.SystemAdminUser.Id)
|
||||
openPost, _, err := client.CreatePost(&model.Post{ChannelId: openChannel.Id})
|
||||
require.NoError(t, err)
|
||||
|
||||
privateChannel, _, err := sysadminClient.CreateChannel(&model.Channel{TeamId: th.BasicTeam.Id, Type: model.ChannelTypePrivate, Name: "private-channel", DisplayName: "Private Channel"})
|
||||
require.NoError(t, err)
|
||||
privatePost, _, err := sysadminClient.CreatePost(&model.Post{ChannelId: privateChannel.Id})
|
||||
require.NoError(t, err)
|
||||
|
||||
privateChannelBasicUser, _, err := client.CreateChannel(&model.Channel{TeamId: th.BasicTeam.Id, Type: model.ChannelTypePrivate, Name: "private-channel-basic-user", DisplayName: "Private Channel - Basic User"})
|
||||
require.NoError(t, err)
|
||||
privatePostBasicUser, _, err := client.CreatePost(&model.Post{ChannelId: privateChannelBasicUser.Id})
|
||||
require.NoError(t, err)
|
||||
|
||||
user3 := th.CreateUser()
|
||||
gmChannel, _, err := client.CreateGroupChannel([]string{th.BasicUser.Id, th.BasicUser2.Id, user3.Id})
|
||||
require.NoError(t, err)
|
||||
gmPost, _, err := client.CreatePost(&model.Post{ChannelId: gmChannel.Id})
|
||||
require.NoError(t, err)
|
||||
|
||||
dmChannel, _, err := client.CreateDirectChannel(th.BasicUser.Id, th.BasicUser2.Id)
|
||||
require.NoError(t, err)
|
||||
dmPost, _, err := client.CreatePost(&model.Post{ChannelId: dmChannel.Id})
|
||||
require.NoError(t, err)
|
||||
|
||||
openTeam, _, err := sysadminClient.CreateTeam(&model.Team{Type: model.TeamOpen, Name: "open-team", DisplayName: "Open Team"})
|
||||
require.NoError(t, err)
|
||||
openTeamOpenChannel, _, err := sysadminClient.CreateChannel(&model.Channel{TeamId: openTeam.Id, Type: model.ChannelTypeOpen, Name: "open-team-open-channel", DisplayName: "Open Team - Open Channel"})
|
||||
require.NoError(t, err)
|
||||
openTeamOpenPost, _, err := sysadminClient.CreatePost(&model.Post{ChannelId: openTeamOpenChannel.Id})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Alt team is a team without the sysadmin in it.
|
||||
altOpenTeam, _, err := client.CreateTeam(&model.Team{Type: model.TeamOpen, Name: "alt-open-team", DisplayName: "Alt Open Team"})
|
||||
require.NoError(t, err)
|
||||
altOpenTeamOpenChannel, _, err := client.CreateChannel(&model.Channel{TeamId: altOpenTeam.Id, Type: model.ChannelTypeOpen, Name: "alt-open-team-open-channel", DisplayName: "Open Team - Open Channel"})
|
||||
require.NoError(t, err)
|
||||
altOpenTeamOpenPost, _, err := client.CreatePost(&model.Post{ChannelId: altOpenTeamOpenChannel.Id})
|
||||
require.NoError(t, err)
|
||||
|
||||
inviteTeam, _, err := sysadminClient.CreateTeam(&model.Team{Type: model.TeamInvite, Name: "invite-team", DisplayName: "Invite Team"})
|
||||
require.NoError(t, err)
|
||||
inviteTeamOpenChannel, _, err := sysadminClient.CreateChannel(&model.Channel{TeamId: inviteTeam.Id, Type: model.ChannelTypeOpen, Name: "invite-team-open-channel", DisplayName: "Invite Team - Open Channel"})
|
||||
require.NoError(t, err)
|
||||
inviteTeamOpenPost, _, err := sysadminClient.CreatePost(&model.Post{ChannelId: inviteTeamOpenChannel.Id})
|
||||
require.NoError(t, err)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
team *model.Team
|
||||
hasJoinedTeam bool
|
||||
channel *model.Channel
|
||||
hasJoinedChannel bool
|
||||
post *model.Post
|
||||
client *model.Client4
|
||||
hasAccess bool
|
||||
}{
|
||||
// Open channel - Current Team
|
||||
{
|
||||
name: "Open post - Current team - Basic user",
|
||||
team: th.BasicTeam,
|
||||
hasJoinedTeam: true,
|
||||
channel: openChannel,
|
||||
hasJoinedChannel: true,
|
||||
post: openPost,
|
||||
client: client,
|
||||
hasAccess: true,
|
||||
},
|
||||
{
|
||||
name: "Open post - Current team - Sysadmin user",
|
||||
team: th.BasicTeam,
|
||||
hasJoinedTeam: true,
|
||||
channel: openChannel,
|
||||
hasJoinedChannel: true,
|
||||
post: openPost,
|
||||
client: sysadminClient,
|
||||
hasAccess: true,
|
||||
},
|
||||
|
||||
// Private channel - Current Team
|
||||
{
|
||||
name: "Private post by sysadmin - Current team - Basic user",
|
||||
team: th.BasicTeam,
|
||||
channel: privateChannel,
|
||||
post: privatePost,
|
||||
client: client,
|
||||
hasAccess: false,
|
||||
},
|
||||
{
|
||||
name: "Private post by sysadmin - Current team - Sysadmin user",
|
||||
team: th.BasicTeam,
|
||||
hasJoinedTeam: true,
|
||||
channel: privateChannel,
|
||||
hasJoinedChannel: true,
|
||||
post: privatePost,
|
||||
client: sysadminClient,
|
||||
hasAccess: true,
|
||||
},
|
||||
{
|
||||
name: "Private post by basic user - Current team - Basic user",
|
||||
team: th.BasicTeam,
|
||||
hasJoinedTeam: true,
|
||||
channel: privateChannelBasicUser,
|
||||
hasJoinedChannel: true,
|
||||
post: privatePostBasicUser,
|
||||
client: client,
|
||||
hasAccess: true,
|
||||
},
|
||||
{
|
||||
name: "Private post by basic user - Current team - Sysadmin user",
|
||||
team: th.BasicTeam,
|
||||
hasJoinedTeam: true,
|
||||
channel: privateChannelBasicUser,
|
||||
hasJoinedChannel: false,
|
||||
post: privatePostBasicUser,
|
||||
client: sysadminClient,
|
||||
hasAccess: true,
|
||||
},
|
||||
|
||||
// GM channel
|
||||
{
|
||||
name: "GM post - Current team - Basic user",
|
||||
team: nil,
|
||||
channel: gmChannel,
|
||||
hasJoinedChannel: true,
|
||||
post: gmPost,
|
||||
client: client,
|
||||
hasAccess: true,
|
||||
},
|
||||
{
|
||||
name: "GM post - Current team - Sysadmin user",
|
||||
team: nil,
|
||||
channel: gmChannel,
|
||||
post: gmPost,
|
||||
client: sysadminClient,
|
||||
hasAccess: false,
|
||||
},
|
||||
|
||||
// DM channel
|
||||
{
|
||||
name: "DM post - Current team - Basic user",
|
||||
team: nil,
|
||||
channel: dmChannel,
|
||||
hasJoinedChannel: true,
|
||||
post: dmPost,
|
||||
client: client,
|
||||
hasAccess: true,
|
||||
},
|
||||
{
|
||||
name: "DM post - Current team - Sysadmin user",
|
||||
team: nil,
|
||||
channel: dmChannel,
|
||||
post: dmPost,
|
||||
client: sysadminClient,
|
||||
hasAccess: false,
|
||||
},
|
||||
|
||||
// Open channel - Open Team
|
||||
{
|
||||
name: "Open post - Open team - Basic user",
|
||||
team: openTeam,
|
||||
hasJoinedTeam: false,
|
||||
channel: openTeamOpenChannel,
|
||||
hasJoinedChannel: false,
|
||||
post: openTeamOpenPost,
|
||||
client: client,
|
||||
hasAccess: true,
|
||||
},
|
||||
{
|
||||
name: "Open post - Open team - Sysadmin user",
|
||||
team: openTeam,
|
||||
hasJoinedTeam: true,
|
||||
channel: openTeamOpenChannel,
|
||||
hasJoinedChannel: true,
|
||||
post: openTeamOpenPost,
|
||||
client: sysadminClient,
|
||||
hasAccess: true,
|
||||
},
|
||||
|
||||
// Open channel - Alt Open Team
|
||||
{
|
||||
name: "Open post - Alt open team - Basic user",
|
||||
team: altOpenTeam,
|
||||
hasJoinedTeam: true,
|
||||
channel: altOpenTeamOpenChannel,
|
||||
hasJoinedChannel: true,
|
||||
post: altOpenTeamOpenPost,
|
||||
client: client,
|
||||
hasAccess: true,
|
||||
},
|
||||
{
|
||||
name: "Open post - Alt open team - Sysadmin user",
|
||||
team: altOpenTeam,
|
||||
hasJoinedTeam: false,
|
||||
channel: altOpenTeamOpenChannel,
|
||||
hasJoinedChannel: false,
|
||||
post: altOpenTeamOpenPost,
|
||||
client: sysadminClient,
|
||||
hasAccess: true,
|
||||
},
|
||||
|
||||
// Open channel - Invite Team
|
||||
{
|
||||
name: "Open post - Invite team - Basic user",
|
||||
team: inviteTeam,
|
||||
channel: inviteTeamOpenChannel,
|
||||
post: inviteTeamOpenPost,
|
||||
client: client,
|
||||
hasAccess: false,
|
||||
},
|
||||
{
|
||||
name: "Open post - Invite team - Sysadmin user",
|
||||
team: inviteTeam,
|
||||
hasJoinedTeam: true,
|
||||
channel: inviteTeamOpenChannel,
|
||||
hasJoinedChannel: true,
|
||||
post: inviteTeamOpenPost,
|
||||
client: sysadminClient,
|
||||
hasAccess: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
info, resp, err := tc.client.GetPostInfo(tc.post.Id)
|
||||
if !tc.hasAccess {
|
||||
require.Error(t, err)
|
||||
CheckNotFoundStatus(t, resp)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
CheckOKStatus(t, resp)
|
||||
require.Equal(t, tc.channel.Id, info.ChannelId)
|
||||
require.Equal(t, tc.channel.Type, info.ChannelType)
|
||||
require.Equal(t, tc.channel.DisplayName, info.ChannelDisplayName)
|
||||
require.Equal(t, tc.hasJoinedChannel, info.HasJoinedChannel)
|
||||
if tc.team != nil {
|
||||
require.Equal(t, tc.team.Id, info.TeamId)
|
||||
require.Equal(t, tc.team.Type, info.TeamType)
|
||||
require.Equal(t, tc.team.DisplayName, info.TeamDisplayName)
|
||||
require.Equal(t, tc.hasJoinedTeam, info.HasJoinedTeam)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAcknowledgePost(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
@@ -712,6 +712,7 @@ type AppIface interface {
|
||||
GetPostIdAfterTime(channelID string, time int64, collapsedThreads bool) (string, *model.AppError)
|
||||
GetPostIdBeforeTime(channelID string, time int64, collapsedThreads bool) (string, *model.AppError)
|
||||
GetPostIfAuthorized(c request.CTX, postID string, session *model.Session, includeDeleted bool) (*model.Post, *model.AppError)
|
||||
GetPostInfo(c request.CTX, postID string) (*model.PostInfo, *model.AppError)
|
||||
GetPostThread(postID string, opts model.GetPostsOptions, userID string) (*model.PostList, *model.AppError)
|
||||
GetPosts(channelID string, offset int, limit int) (*model.PostList, *model.AppError)
|
||||
GetPostsAfterPost(options model.GetPostsOptions) (*model.PostList, *model.AppError)
|
||||
|
||||
@@ -7952,6 +7952,28 @@ func (a *OpenTracingAppLayer) GetPostIfAuthorized(c request.CTX, postID string,
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GetPostInfo(c request.CTX, postID string) (*model.PostInfo, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetPostInfo")
|
||||
|
||||
a.ctx = newCtx
|
||||
a.app.Srv().Store().SetContext(newCtx)
|
||||
defer func() {
|
||||
a.app.Srv().Store().SetContext(origCtx)
|
||||
a.ctx = origCtx
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0, resultVar1 := a.app.GetPostInfo(c, postID)
|
||||
|
||||
if resultVar1 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar1))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GetPostThread(postID string, opts model.GetPostsOptions, userID string) (*model.PostList, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetPostThread")
|
||||
|
||||
68
app/post.go
68
app/post.go
@@ -2151,6 +2151,74 @@ func (a *App) CheckPostReminders() {
|
||||
|
||||
}
|
||||
|
||||
func (a *App) GetPostInfo(c request.CTX, postID string) (*model.PostInfo, *model.AppError) {
|
||||
userID := c.Session().UserId
|
||||
post, appErr := a.GetSinglePost(postID, false)
|
||||
if appErr != nil {
|
||||
return nil, appErr
|
||||
}
|
||||
|
||||
channel, appErr := a.GetChannel(c, post.ChannelId)
|
||||
if appErr != nil {
|
||||
return nil, appErr
|
||||
}
|
||||
|
||||
notFoundError := model.NewAppError("GetPostInfo", "app.post.get.app_error", nil, "", http.StatusNotFound)
|
||||
|
||||
var team *model.Team
|
||||
hasPermissionToAccessTeam := false
|
||||
if channel.TeamId != "" {
|
||||
team, appErr = a.GetTeam(channel.TeamId)
|
||||
if appErr != nil {
|
||||
return nil, appErr
|
||||
}
|
||||
|
||||
if team.Type == model.TeamOpen {
|
||||
hasPermissionToAccessTeam = a.HasPermissionToTeam(userID, team.Id, model.PermissionJoinPublicTeams)
|
||||
} else if team.Type == model.TeamInvite {
|
||||
hasPermissionToAccessTeam = a.HasPermissionToTeam(userID, team.Id, model.PermissionJoinPrivateTeams)
|
||||
}
|
||||
} else {
|
||||
// This happens in case of DMs and GMs.
|
||||
hasPermissionToAccessTeam = true
|
||||
}
|
||||
|
||||
if !hasPermissionToAccessTeam {
|
||||
return nil, notFoundError
|
||||
}
|
||||
|
||||
hasPermissionToAccessChannel := false
|
||||
if channel.Type == model.ChannelTypeOpen {
|
||||
hasPermissionToAccessChannel = true
|
||||
} else if channel.Type == model.ChannelTypePrivate {
|
||||
hasPermissionToAccessChannel = a.HasPermissionToChannel(c, userID, channel.Id, model.PermissionManagePrivateChannelMembers)
|
||||
} else if channel.Type == model.ChannelTypeDirect || channel.Type == model.ChannelTypeGroup {
|
||||
hasPermissionToAccessChannel = a.HasPermissionToChannel(c, userID, channel.Id, model.PermissionReadChannel)
|
||||
}
|
||||
|
||||
if !hasPermissionToAccessChannel {
|
||||
return nil, notFoundError
|
||||
}
|
||||
|
||||
_, channelMemberErr := a.GetChannelMember(c, channel.Id, userID)
|
||||
|
||||
info := model.PostInfo{
|
||||
ChannelId: channel.Id,
|
||||
ChannelType: channel.Type,
|
||||
ChannelDisplayName: channel.DisplayName,
|
||||
HasJoinedChannel: channelMemberErr == nil,
|
||||
}
|
||||
if team != nil {
|
||||
_, teamMemberErr := a.GetTeamMember(team.Id, userID)
|
||||
|
||||
info.TeamId = team.Id
|
||||
info.TeamType = team.Type
|
||||
info.TeamDisplayName = team.DisplayName
|
||||
info.HasJoinedTeam = teamMemberErr == nil
|
||||
}
|
||||
return &info, nil
|
||||
}
|
||||
|
||||
func includeEmbedsAndImages(a *App, c request.CTX, topThreadList *model.TopThreadList, userID string) (*model.TopThreadList, error) {
|
||||
for _, topThread := range topThreadList.Items {
|
||||
topThread.Post = a.PreparePostForClientWithEmbedsAndImages(c, topThread.Post, false, false, true)
|
||||
|
||||
@@ -8540,6 +8540,20 @@ func (c *Client4) GetNewTeamMembersSince(teamID string, timeRange string, page i
|
||||
return newTeamMembersList, BuildResponse(r), nil
|
||||
}
|
||||
|
||||
func (c *Client4) GetPostInfo(postId string) (*PostInfo, *Response, error) {
|
||||
r, err := c.DoAPIGet(c.postRoute(postId)+"/info", "")
|
||||
if err != nil {
|
||||
return nil, BuildResponse(r), err
|
||||
}
|
||||
defer closeBody(r)
|
||||
|
||||
var info *PostInfo
|
||||
if err = json.NewDecoder(r.Body).Decode(&info); err != nil {
|
||||
return nil, nil, NewAppError("GetPostInfo", "api.unmarshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
return info, BuildResponse(r), nil
|
||||
}
|
||||
|
||||
func (c *Client4) AcknowledgePost(postId, userId string) (*PostAcknowledgement, *Response, error) {
|
||||
r, err := c.DoAPIPost(c.userRoute(userId)+c.postRoute(postId)+"/ack", "")
|
||||
if err != nil {
|
||||
|
||||
15
model/post_info.go
Обычный файл
15
model/post_info.go
Обычный файл
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
type PostInfo struct {
|
||||
ChannelId string `json:"channel_id"`
|
||||
ChannelType ChannelType `json:"channel_type"`
|
||||
ChannelDisplayName string `json:"channel_display_name"`
|
||||
HasJoinedChannel bool `json:"has_joined_channel"`
|
||||
TeamId string `json:"team_id"`
|
||||
TeamType string `json:"team_type"`
|
||||
TeamDisplayName string `json:"team_display_name"`
|
||||
HasJoinedTeam bool `json:"has_joined_team"`
|
||||
}
|
||||
Ссылка в новой задаче
Block a user