Avoid counting top channel posts for posts made by plugins and OAuth apps (#20943)
* add from_integration prop to oauth posts to: - oauth app posts - plugin posts - slash command responses - incoming webhook posts * tests * include check for bot posts * use from_plugin and from_oauth_app props * fix test * avoid counting top channel posts for posts made by plugins and oauth apps
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
3e4c44c478
Коммит
15b5b1c191
@@ -212,6 +212,53 @@ func TestCreatePost(t *testing.T) {
|
|||||||
require.Equal(t, post.CreateAt, rpost.CreateAt, "create at should match")
|
require.Equal(t, post.CreateAt, rpost.CreateAt, "create at should match")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCreatePostWithOAuthClient(t *testing.T) {
|
||||||
|
th := Setup(t).InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
originalOAuthSetting := *th.App.Config().ServiceSettings.EnableOAuthServiceProvider
|
||||||
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||||
|
*cfg.ServiceSettings.EnableOAuthServiceProvider = true
|
||||||
|
})
|
||||||
|
|
||||||
|
defer th.App.UpdateConfig(func(cfg *model.Config) {
|
||||||
|
*cfg.ServiceSettings.EnableOAuthServiceProvider = originalOAuthSetting
|
||||||
|
})
|
||||||
|
|
||||||
|
oAuthApp, appErr := th.App.CreateOAuthApp(&model.OAuthApp{
|
||||||
|
CreatorId: th.SystemAdminUser.Id,
|
||||||
|
Name: "name",
|
||||||
|
CallbackUrls: []string{"http://test.com"},
|
||||||
|
Homepage: "http://test.com",
|
||||||
|
})
|
||||||
|
require.Nil(t, appErr, "should create an OAuthApp")
|
||||||
|
|
||||||
|
session, appErr := th.App.CreateSession(&model.Session{
|
||||||
|
UserId: th.BasicUser.Id,
|
||||||
|
Token: "token",
|
||||||
|
IsOAuth: true,
|
||||||
|
Props: model.StringMap{model.SessionPropOAuthAppID: oAuthApp.Id},
|
||||||
|
})
|
||||||
|
require.Nil(t, appErr, "should create a session")
|
||||||
|
|
||||||
|
post, _, err := th.Client.CreatePost(&model.Post{
|
||||||
|
ChannelId: th.BasicPost.ChannelId,
|
||||||
|
Message: "test message",
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.NotContains(t, post.GetProps(), "from_oauth_app", "contains from_oauth_app prop when not using OAuth client")
|
||||||
|
|
||||||
|
client := th.CreateClient()
|
||||||
|
client.SetOAuthToken(session.Token)
|
||||||
|
post, _, err = client.CreatePost(&model.Post{
|
||||||
|
ChannelId: th.BasicPost.ChannelId,
|
||||||
|
Message: "test message",
|
||||||
|
})
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Contains(t, post.GetProps(), "from_oauth_app", "missing from_oauth_app prop when using OAuth client")
|
||||||
|
}
|
||||||
|
|
||||||
func TestCreatePostEphemeral(t *testing.T) {
|
func TestCreatePostEphemeral(t *testing.T) {
|
||||||
th := Setup(t).InitBasic()
|
th := Setup(t).InitBasic()
|
||||||
defer th.TearDown()
|
defer th.TearDown()
|
||||||
|
|||||||
@@ -2434,6 +2434,40 @@ func TestGetTopChannelsForTeamSince(t *testing.T) {
|
|||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// add an oauth app post to ensure it's not counted
|
||||||
|
_, err = th.Server.Store.Post().Save(&model.Post{
|
||||||
|
Message: "hello from an ouath app",
|
||||||
|
ChannelId: channel3.Id,
|
||||||
|
UserId: th.BasicUser.Id,
|
||||||
|
Props: model.StringInterface{
|
||||||
|
"from_oauth_app": true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// add a plugin post to ensure it's not counted
|
||||||
|
_, err = th.Server.Store.Post().Save(&model.Post{
|
||||||
|
Message: "hello from a plugin",
|
||||||
|
ChannelId: channel3.Id,
|
||||||
|
UserId: th.BasicUser.Id,
|
||||||
|
Props: model.StringInterface{
|
||||||
|
"from_plugin": true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// add a system post to ensure it's not counted
|
||||||
|
_, err = th.Server.Store.Post().Save(&model.Post{
|
||||||
|
Message: "system message",
|
||||||
|
Type: "system_join_channel",
|
||||||
|
ChannelId: channel3.Id,
|
||||||
|
UserId: th.BasicUser.Id,
|
||||||
|
Props: model.StringInterface{
|
||||||
|
"from_oauth_app": true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
channel4 := th.CreatePrivateChannel(th.Context, th.BasicTeam)
|
channel4 := th.CreatePrivateChannel(th.Context, th.BasicTeam)
|
||||||
channel5 := th.CreateChannel(th.Context, th.BasicTeam)
|
channel5 := th.CreateChannel(th.Context, th.BasicTeam)
|
||||||
channel6 := th.CreatePrivateChannel(th.Context, th.BasicTeam)
|
channel6 := th.CreatePrivateChannel(th.Context, th.BasicTeam)
|
||||||
|
|||||||
@@ -630,6 +630,8 @@ func (api *PluginAPI) GetGroupsForUser(userID string) ([]*model.Group, *model.Ap
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (api *PluginAPI) CreatePost(post *model.Post) (*model.Post, *model.AppError) {
|
func (api *PluginAPI) CreatePost(post *model.Post) (*model.Post, *model.AppError) {
|
||||||
|
post.AddProp("from_plugin", "true")
|
||||||
|
|
||||||
post, appErr := api.app.CreatePostMissingChannel(api.ctx, post, true)
|
post, appErr := api.app.CreatePostMissingChannel(api.ctx, post, true)
|
||||||
if post != nil {
|
if post != nil {
|
||||||
post = post.ForPlugin()
|
post = post.ForPlugin()
|
||||||
|
|||||||
@@ -1383,6 +1383,25 @@ func TestPluginCreatePostWithUploadedFile(t *testing.T) {
|
|||||||
assert.Equal(t, model.StringArray{fileInfo.Id}, actualPost.FileIds)
|
assert.Equal(t, model.StringArray{fileInfo.Id}, actualPost.FileIds)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPluginCreatePostAddsFromPluginProp(t *testing.T) {
|
||||||
|
th := Setup(t).InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
api := th.SetupPluginAPI()
|
||||||
|
|
||||||
|
channelID := th.BasicChannel.Id
|
||||||
|
userID := th.BasicUser.Id
|
||||||
|
post, err := api.CreatePost(&model.Post{
|
||||||
|
Message: "test",
|
||||||
|
ChannelId: channelID,
|
||||||
|
UserId: userID,
|
||||||
|
})
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
actualPost, err := api.GetPost(post.Id)
|
||||||
|
require.Nil(t, err)
|
||||||
|
assert.Equal(t, "true", actualPost.GetProp("from_plugin"))
|
||||||
|
}
|
||||||
|
|
||||||
func TestPluginAPIGetConfig(t *testing.T) {
|
func TestPluginAPIGetConfig(t *testing.T) {
|
||||||
th := Setup(t)
|
th := Setup(t)
|
||||||
defer th.TearDown()
|
defer th.TearDown()
|
||||||
|
|||||||
@@ -203,6 +203,10 @@ func (a *App) CreatePost(c request.CTX, post *model.Post, channel *model.Channel
|
|||||||
post.AddProp("from_bot", "true")
|
post.AddProp("from_bot", "true")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.Session().IsOAuth {
|
||||||
|
post.AddProp("from_oauth_app", "true")
|
||||||
|
}
|
||||||
|
|
||||||
var ephemeralPost *model.Post
|
var ephemeralPost *model.Post
|
||||||
if post.Type == "" && !a.HasPermissionToChannel(c, user.Id, channel.Id, model.PermissionUseChannelMentions) {
|
if post.Type == "" && !a.HasPermissionToChannel(c, user.Id, channel.Id, model.PermissionUseChannelMentions) {
|
||||||
mention := post.DisableMentionHighlights()
|
mention := post.DisableMentionHighlights()
|
||||||
|
|||||||
@@ -4159,8 +4159,8 @@ func (s SqlChannelStore) GetTeamForChannel(channelID string) (*model.Team, error
|
|||||||
func (s SqlChannelStore) GetTopChannelsForTeamSince(teamID string, userID string, since int64, offset int, limit int) (*model.TopChannelList, error) {
|
func (s SqlChannelStore) GetTopChannelsForTeamSince(teamID string, userID string, since int64, offset int, limit int) (*model.TopChannelList, error) {
|
||||||
channels := make([]*model.TopChannel, 0)
|
channels := make([]*model.TopChannel, 0)
|
||||||
var args []any
|
var args []any
|
||||||
postgresPropQuery := `AND (Posts.Props ->> 'from_bot' IS NULL OR Posts.Props ->> 'from_bot' = 'false') AND (Posts.Props ->> 'from_webhook' IS NULL OR Posts.Props ->> 'from_webhook' = 'false')`
|
postgresPropQuery := `AND (Posts.Props ->> 'from_bot' IS NULL OR Posts.Props ->> 'from_bot' = 'false') AND (Posts.Props ->> 'from_webhook' IS NULL OR Posts.Props ->> 'from_webhook' = 'false') AND (Posts.Props ->> 'from_oauth_app' IS NULL OR Posts.Props ->> 'from_oauth_app' = 'false') AND (Posts.Props ->> 'from_plugin' IS NULL OR Posts.Props ->> 'from_plugin' = 'false')`
|
||||||
mySqlPropsQuery := `AND (JSON_EXTRACT(Posts.Props, '$.from_bot') IS NULL OR JSON_EXTRACT(Posts.Props, '$.from_bot') = 'false') AND (JSON_EXTRACT(Posts.Props, '$.from_webhook') IS NULL OR JSON_EXTRACT(Posts.Props, '$.from_webhook') = 'false')`
|
mySqlPropsQuery := `AND (JSON_EXTRACT(Posts.Props, '$.from_bot') IS NULL OR JSON_EXTRACT(Posts.Props, '$.from_bot') = 'false') AND (JSON_EXTRACT(Posts.Props, '$.from_webhook') IS NULL OR JSON_EXTRACT(Posts.Props, '$.from_webhook') = 'false') AND (JSON_EXTRACT(Posts.Props, '$.from_plugin') IS NULL OR JSON_EXTRACT(Posts.Props, '$.from_plugin') = 'false') AND (JSON_EXTRACT(Posts.Props, '$.from_oauth_app') IS NULL OR JSON_EXTRACT(Posts.Props, '$.from_oauth_app') = 'false')`
|
||||||
|
|
||||||
query := `
|
query := `
|
||||||
SELECT
|
SELECT
|
||||||
@@ -4263,9 +4263,9 @@ func (s SqlChannelStore) GetTopChannelsForUserSince(userID string, teamID string
|
|||||||
|
|
||||||
var propsQuery string
|
var propsQuery string
|
||||||
if s.DriverName() == model.DatabaseDriverMysql {
|
if s.DriverName() == model.DatabaseDriverMysql {
|
||||||
propsQuery = `AND (JSON_EXTRACT(Posts.Props, '$.from_bot') IS NULL OR JSON_EXTRACT(Posts.Props, '$.from_bot') = 'false') AND (JSON_EXTRACT(Posts.Props, '$.from_webhook') IS NULL OR JSON_EXTRACT(Posts.Props, '$.from_webhook') = 'false')`
|
propsQuery = `AND (JSON_EXTRACT(Posts.Props, '$.from_bot') IS NULL OR JSON_EXTRACT(Posts.Props, '$.from_bot') = 'false') AND (JSON_EXTRACT(Posts.Props, '$.from_webhook') IS NULL OR JSON_EXTRACT(Posts.Props, '$.from_webhook') = 'false') AND (JSON_EXTRACT(Posts.Props, '$.from_plugin') IS NULL OR JSON_EXTRACT(Posts.Props, '$.from_plugin') = 'false') AND (JSON_EXTRACT(Posts.Props, '$.from_oauth_app') IS NULL OR JSON_EXTRACT(Posts.Props, '$.from_oauth_app') = 'false')`
|
||||||
} else if s.DriverName() == model.DatabaseDriverPostgres {
|
} else if s.DriverName() == model.DatabaseDriverPostgres {
|
||||||
propsQuery = `AND (Posts.Props ->> 'from_bot' IS NULL OR Posts.Props ->> 'from_bot' = 'false') AND (Posts.Props ->> 'from_webhook' IS NULL OR Posts.Props ->> 'from_webhook' = 'false')`
|
propsQuery = `AND (Posts.Props ->> 'from_bot' IS NULL OR Posts.Props ->> 'from_bot' = 'false') AND (Posts.Props ->> 'from_webhook' IS NULL OR Posts.Props ->> 'from_webhook' = 'false') AND (Posts.Props ->> 'from_oauth_app' IS NULL OR Posts.Props ->> 'from_oauth_app' = 'false') AND (Posts.Props ->> 'from_plugin' IS NULL OR Posts.Props ->> 'from_plugin' = 'false')`
|
||||||
}
|
}
|
||||||
|
|
||||||
query = `
|
query = `
|
||||||
@@ -4519,14 +4519,14 @@ func (s SqlChannelStore) PostCountsByDuration(channelIDs []string, sinceUnixMill
|
|||||||
} else {
|
} else {
|
||||||
unixSelect = `DATE_FORMAT(CONVERT_TZ(FROM_UNIXTIME(Posts.CreateAt / 1000), 'GMT', '` + loc + `'),'%Y-%m-%dT%H') AS duration`
|
unixSelect = `DATE_FORMAT(CONVERT_TZ(FROM_UNIXTIME(Posts.CreateAt / 1000), 'GMT', '` + loc + `'),'%Y-%m-%dT%H') AS duration`
|
||||||
}
|
}
|
||||||
propsQuery = `(JSON_EXTRACT(Posts.Props, '$.from_bot') IS NULL OR JSON_EXTRACT(Posts.Props, '$.from_bot') = 'false') AND (JSON_EXTRACT(Posts.Props, '$.from_webhook') IS NULL OR JSON_EXTRACT(Posts.Props, '$.from_webhook') = 'false')`
|
propsQuery = `(JSON_EXTRACT(Posts.Props, '$.from_bot') IS NULL OR JSON_EXTRACT(Posts.Props, '$.from_bot') = 'false') AND (JSON_EXTRACT(Posts.Props, '$.from_webhook') IS NULL OR JSON_EXTRACT(Posts.Props, '$.from_webhook') = 'false') AND (JSON_EXTRACT(Posts.Props, '$.from_plugin') IS NULL OR JSON_EXTRACT(Posts.Props, '$.from_plugin') = 'false') AND (JSON_EXTRACT(Posts.Props, '$.from_oauth_app') IS NULL OR JSON_EXTRACT(Posts.Props, '$.from_oauth_app') = 'false')`
|
||||||
} else if s.DriverName() == model.DatabaseDriverPostgres {
|
} else if s.DriverName() == model.DatabaseDriverPostgres {
|
||||||
if duration == model.PostsByDay {
|
if duration == model.PostsByDay {
|
||||||
unixSelect = fmt.Sprintf(`TO_CHAR(TO_TIMESTAMP(Posts.CreateAt / 1000) AT TIME ZONE '%s', 'YYYY-MM-DD') AS duration`, loc)
|
unixSelect = fmt.Sprintf(`TO_CHAR(TO_TIMESTAMP(Posts.CreateAt / 1000) AT TIME ZONE '%s', 'YYYY-MM-DD') AS duration`, loc)
|
||||||
} else {
|
} else {
|
||||||
unixSelect = fmt.Sprintf(`TO_CHAR(TO_TIMESTAMP(Posts.CreateAt / 1000) AT TIME ZONE '%s', 'YYYY-MM-DD"T"HH24') AS duration`, loc)
|
unixSelect = fmt.Sprintf(`TO_CHAR(TO_TIMESTAMP(Posts.CreateAt / 1000) AT TIME ZONE '%s', 'YYYY-MM-DD"T"HH24') AS duration`, loc)
|
||||||
}
|
}
|
||||||
propsQuery = `(Posts.Props ->> 'from_bot' IS NULL OR Posts.Props ->> 'from_bot' = 'false') AND (Posts.Props ->> 'from_webhook' IS NULL OR Posts.Props ->> 'from_webhook' = 'false')`
|
propsQuery = `(Posts.Props ->> 'from_bot' IS NULL OR Posts.Props ->> 'from_bot' = 'false') AND (Posts.Props ->> 'from_webhook' IS NULL OR Posts.Props ->> 'from_webhook' = 'false') AND (Posts.Props ->> 'from_oauth_app' IS NULL OR Posts.Props ->> 'from_oauth_app' = 'false') AND (Posts.Props ->> 'from_plugin' IS NULL OR Posts.Props ->> 'from_plugin' = 'false')`
|
||||||
}
|
}
|
||||||
query := sq.
|
query := sq.
|
||||||
Select("Posts.ChannelId AS channelid", unixSelect, "count(Posts.Id) AS postcount").
|
Select("Posts.ChannelId AS channelid", unixSelect, "count(Posts.Id) AS postcount").
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user