[GH-26715] Added Pagination Support for IncomingWebHooks (#27502)
* Added Pagination Support for IncomingWebHooks * Incorporated feedback from reviews * Removed trailing spaces * Restored deleted server en.json entries, fixed order in webapp en.json --------- Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
@@ -184,6 +184,8 @@ func getIncomingHooks(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
hooks []*model.IncomingWebhook
|
||||
appErr *model.AppError
|
||||
js []byte
|
||||
err error
|
||||
)
|
||||
|
||||
if teamID != "" {
|
||||
@@ -217,7 +219,20 @@ func getIncomingHooks(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
js, err := json.Marshal(hooks)
|
||||
if c.Params.IncludeTotalCount {
|
||||
totalCount, appErr := c.App.GetIncomingWebhooksCount(teamID, userID)
|
||||
|
||||
if appErr != nil {
|
||||
c.Err = appErr
|
||||
return
|
||||
}
|
||||
|
||||
hooksWithCount := model.IncomingWebhooksWithCount{Webhooks: hooks, TotalCount: totalCount}
|
||||
js, err = json.Marshal(hooksWithCount)
|
||||
} else {
|
||||
js, err = json.Marshal(hooks)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
c.Err = model.NewAppError("getIncomingHooks", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
return
|
||||
|
||||
@@ -304,6 +304,60 @@ func TestGetIncomingWebhooksByTeam(t *testing.T) {
|
||||
assert.Equal(t, basicHook.Id, filteredHooks[0].Id)
|
||||
}
|
||||
|
||||
func TestGetIncomingWebhooksWithCount(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
BasicClient := th.Client
|
||||
th.LoginBasic()
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableIncomingWebhooks = true })
|
||||
|
||||
defaultRolePermissions := th.SaveDefaultRolePermissions()
|
||||
defer func() {
|
||||
th.RestoreDefaultRolePermissions(defaultRolePermissions)
|
||||
}()
|
||||
th.AddPermissionToRole(model.PermissionManageIncomingWebhooks.Id, model.TeamAdminRoleId)
|
||||
th.AddPermissionToRole(model.PermissionManageIncomingWebhooks.Id, model.SystemUserRoleId)
|
||||
|
||||
// Basic user webhook
|
||||
bHook := &model.IncomingWebhook{ChannelId: th.BasicChannel.Id, TeamId: th.BasicTeam.Id, UserId: th.BasicUser.Id}
|
||||
basicHook, _, err := BasicClient.CreateIncomingWebhook(context.Background(), bHook)
|
||||
require.NoError(t, err)
|
||||
|
||||
basicHooksWithCount, _, err := BasicClient.GetIncomingWebhooksWithCount(context.Background(), 0, 1000, "")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, len(basicHooksWithCount.Webhooks))
|
||||
assert.Equal(t, int64(1), basicHooksWithCount.TotalCount)
|
||||
assert.Equal(t, basicHook.Id, basicHooksWithCount.Webhooks[0].Id)
|
||||
|
||||
// Admin User webhook
|
||||
aHook := &model.IncomingWebhook{ChannelId: th.BasicChannel.Id, TeamId: th.BasicTeam.Id, UserId: th.SystemAdminUser.Id}
|
||||
adminHook, _, err := th.SystemAdminClient.CreateIncomingWebhook(context.Background(), aHook)
|
||||
require.NoError(t, err)
|
||||
|
||||
th.TestForSystemAdminAndLocal(t, func(t *testing.T, client *model.Client4) {
|
||||
adminHooksWithCount, _, err2 := client.GetIncomingWebhooksWithCount(context.Background(), 0, 1000, "")
|
||||
require.NoError(t, err2)
|
||||
assert.Equal(t, 2, len(adminHooksWithCount.Webhooks))
|
||||
assert.Equal(t, int64(2), adminHooksWithCount.TotalCount)
|
||||
|
||||
foundBasicHook := false
|
||||
foundAdminHook := false
|
||||
|
||||
for _, h := range adminHooksWithCount.Webhooks {
|
||||
if basicHook.Id == h.Id {
|
||||
foundBasicHook = true
|
||||
}
|
||||
if adminHook.Id == h.Id {
|
||||
foundAdminHook = true
|
||||
}
|
||||
}
|
||||
|
||||
require.True(t, foundBasicHook, "missing basic user hook")
|
||||
require.True(t, foundAdminHook, "missing admin user hook")
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetIncomingWebhook(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
@@ -241,7 +241,7 @@ func (a *App) getAnalytics(rctx request.CTX, name string, teamID string, forSupp
|
||||
var incomingWebhookCount int64
|
||||
g2.Go(func() error {
|
||||
var err error
|
||||
if incomingWebhookCount, err = a.Srv().Store().Webhook().AnalyticsIncomingCount(teamID); err != nil {
|
||||
if incomingWebhookCount, err = a.Srv().Store().Webhook().AnalyticsIncomingCount(teamID, ""); err != nil {
|
||||
return model.NewAppError("GetAnalytics", "app.webhooks.analytics_incoming_count.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -717,6 +717,7 @@ type AppIface interface {
|
||||
GetGroupsByUserId(userID string) ([]*model.Group, *model.AppError)
|
||||
GetHubForUserId(userID string) *platform.Hub
|
||||
GetIncomingWebhook(hookID string) (*model.IncomingWebhook, *model.AppError)
|
||||
GetIncomingWebhooksCount(teamID string, userID string) (int64, *model.AppError)
|
||||
GetIncomingWebhooksForTeamPage(teamID string, page, perPage int) ([]*model.IncomingWebhook, *model.AppError)
|
||||
GetIncomingWebhooksForTeamPageByUser(teamID string, userID string, page, perPage int) ([]*model.IncomingWebhook, *model.AppError)
|
||||
GetIncomingWebhooksPage(page, perPage int) ([]*model.IncomingWebhook, *model.AppError)
|
||||
|
||||
@@ -7220,6 +7220,28 @@ func (a *OpenTracingAppLayer) GetIncomingWebhook(hookID string) (*model.Incoming
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GetIncomingWebhooksCount(teamID string, userID string) (int64, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetIncomingWebhooksCount")
|
||||
|
||||
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.GetIncomingWebhooksCount(teamID, userID)
|
||||
|
||||
if resultVar1 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar1))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GetIncomingWebhooksForTeamPage(teamID string, page int, perPage int) ([]*model.IncomingWebhook, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetIncomingWebhooksForTeamPage")
|
||||
|
||||
@@ -507,6 +507,19 @@ func (a *App) GetIncomingWebhooksPage(page, perPage int) ([]*model.IncomingWebho
|
||||
return a.GetIncomingWebhooksPageByUser("", page, perPage)
|
||||
}
|
||||
|
||||
func (a *App) GetIncomingWebhooksCount(teamID string, userID string) (int64, *model.AppError) {
|
||||
if !*a.Config().ServiceSettings.EnableIncomingWebhooks {
|
||||
return 0, model.NewAppError("GetIncomingWebhooksCount", "api.incoming_webhook.disabled.app_error", nil, "", http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
totalCount, err := a.Srv().Store().Webhook().AnalyticsIncomingCount(teamID, userID)
|
||||
if err != nil {
|
||||
return 0, model.NewAppError("GetIncomingWebhooksCount", "app.webhooks.get_incoming_count.app_error", map[string]any{"TeamID": teamID, "UserID": userID, "Error": err.Error()}, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
|
||||
return totalCount, nil
|
||||
}
|
||||
|
||||
func (a *App) CreateOutgoingWebhook(hook *model.OutgoingWebhook) (*model.OutgoingWebhook, *model.AppError) {
|
||||
if !*a.Config().ServiceSettings.EnableOutgoingWebhooks {
|
||||
return nil, model.NewAppError("CreateOutgoingWebhook", "api.outgoing_webhook.disabled.app_error", nil, "", http.StatusNotImplemented)
|
||||
|
||||
@@ -12908,7 +12908,7 @@ func (s *OpenTracingLayerUserTermsOfServiceStore) Save(userTermsOfService *model
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (s *OpenTracingLayerWebhookStore) AnalyticsIncomingCount(teamID string) (int64, error) {
|
||||
func (s *OpenTracingLayerWebhookStore) AnalyticsIncomingCount(teamID string, userID string) (int64, error) {
|
||||
origCtx := s.Root.Store.Context()
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "WebhookStore.AnalyticsIncomingCount")
|
||||
s.Root.Store.SetContext(newCtx)
|
||||
@@ -12917,7 +12917,7 @@ func (s *OpenTracingLayerWebhookStore) AnalyticsIncomingCount(teamID string) (in
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
result, err := s.WebhookStore.AnalyticsIncomingCount(teamID)
|
||||
result, err := s.WebhookStore.AnalyticsIncomingCount(teamID, userID)
|
||||
if err != nil {
|
||||
span.LogFields(spanlog.Error(err))
|
||||
ext.Error.Set(span, true)
|
||||
|
||||
@@ -14744,11 +14744,11 @@ func (s *RetryLayerUserTermsOfServiceStore) Save(userTermsOfService *model.UserT
|
||||
|
||||
}
|
||||
|
||||
func (s *RetryLayerWebhookStore) AnalyticsIncomingCount(teamID string) (int64, error) {
|
||||
func (s *RetryLayerWebhookStore) AnalyticsIncomingCount(teamID string, userID string) (int64, error) {
|
||||
|
||||
tries := 0
|
||||
for {
|
||||
result, err := s.WebhookStore.AnalyticsIncomingCount(teamID)
|
||||
result, err := s.WebhookStore.AnalyticsIncomingCount(teamID, userID)
|
||||
if err == nil {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@@ -352,15 +352,19 @@ func (s SqlWebhookStore) UpdateOutgoing(hook *model.OutgoingWebhook) (*model.Out
|
||||
return hook, nil
|
||||
}
|
||||
|
||||
func (s SqlWebhookStore) AnalyticsIncomingCount(teamId string) (int64, error) {
|
||||
func (s SqlWebhookStore) AnalyticsIncomingCount(teamID string, userID string) (int64, error) {
|
||||
queryBuilder :=
|
||||
s.getQueryBuilder().
|
||||
Select("COUNT(*)").
|
||||
From("IncomingWebhooks").
|
||||
Where("DeleteAt = 0")
|
||||
|
||||
if teamId != "" {
|
||||
queryBuilder = queryBuilder.Where("TeamId", teamId)
|
||||
if teamID != "" {
|
||||
queryBuilder = queryBuilder.Where(sq.Eq{"TeamId": teamID})
|
||||
}
|
||||
|
||||
if userID != "" {
|
||||
queryBuilder = queryBuilder.Where(sq.Eq{"UserId": userID})
|
||||
}
|
||||
|
||||
queryString, args, err := queryBuilder.ToSql()
|
||||
|
||||
@@ -615,7 +615,7 @@ type WebhookStore interface {
|
||||
PermanentDeleteOutgoingByUser(userID string) error
|
||||
UpdateOutgoing(hook *model.OutgoingWebhook) (*model.OutgoingWebhook, error)
|
||||
|
||||
AnalyticsIncomingCount(teamID string) (int64, error)
|
||||
AnalyticsIncomingCount(teamID string, userID string) (int64, error)
|
||||
AnalyticsOutgoingCount(teamID string) (int64, error)
|
||||
InvalidateWebhookCache(webhook string)
|
||||
ClearCaches()
|
||||
|
||||
@@ -14,9 +14,9 @@ type WebhookStore struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// AnalyticsIncomingCount provides a mock function with given fields: teamID
|
||||
func (_m *WebhookStore) AnalyticsIncomingCount(teamID string) (int64, error) {
|
||||
ret := _m.Called(teamID)
|
||||
// AnalyticsIncomingCount provides a mock function with given fields: teamID, userID
|
||||
func (_m *WebhookStore) AnalyticsIncomingCount(teamID string, userID string) (int64, error) {
|
||||
ret := _m.Called(teamID, userID)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for AnalyticsIncomingCount")
|
||||
@@ -24,17 +24,17 @@ func (_m *WebhookStore) AnalyticsIncomingCount(teamID string) (int64, error) {
|
||||
|
||||
var r0 int64
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(string) (int64, error)); ok {
|
||||
return rf(teamID)
|
||||
if rf, ok := ret.Get(0).(func(string, string) (int64, error)); ok {
|
||||
return rf(teamID, userID)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(string) int64); ok {
|
||||
r0 = rf(teamID)
|
||||
if rf, ok := ret.Get(0).(func(string, string) int64); ok {
|
||||
r0 = rf(teamID, userID)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int64)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(string) error); ok {
|
||||
r1 = rf(teamID)
|
||||
if rf, ok := ret.Get(1).(func(string, string) error); ok {
|
||||
r1 = rf(teamID, userID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
@@ -577,10 +577,28 @@ func testWebhookStoreCountIncoming(t *testing.T, rctx request.CTX, ss store.Stor
|
||||
|
||||
_, _ = ss.Webhook().SaveIncoming(o1)
|
||||
|
||||
c, err := ss.Webhook().AnalyticsIncomingCount("")
|
||||
c, err := ss.Webhook().AnalyticsIncomingCount("", "")
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NotEqual(t, 0, c, "should have at least 1 incoming hook")
|
||||
|
||||
o2 := &model.IncomingWebhook{}
|
||||
o2.ChannelId = model.NewId()
|
||||
o2.UserId = model.NewId()
|
||||
o2.TeamId = model.NewId()
|
||||
|
||||
_, _ = ss.Webhook().SaveIncoming(o2)
|
||||
|
||||
c, err = ss.Webhook().AnalyticsIncomingCount("", "")
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, 1, c, "should have at least 2 incoming hooks")
|
||||
|
||||
c, err = ss.Webhook().AnalyticsIncomingCount(o1.TeamId, "")
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, 0, c, "should have at least 1 incoming hook when filtering by TeamID")
|
||||
|
||||
c, err = ss.Webhook().AnalyticsIncomingCount("", o2.UserId)
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, 0, c, "should have at least 1 incoming hook when filtering by UserID")
|
||||
}
|
||||
|
||||
func testWebhookStoreCountOutgoing(t *testing.T, rctx request.CTX, ss store.Store) {
|
||||
|
||||
@@ -11618,10 +11618,10 @@ func (s *TimerLayerUserTermsOfServiceStore) Save(userTermsOfService *model.UserT
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (s *TimerLayerWebhookStore) AnalyticsIncomingCount(teamID string) (int64, error) {
|
||||
func (s *TimerLayerWebhookStore) AnalyticsIncomingCount(teamID string, userID string) (int64, error) {
|
||||
start := time.Now()
|
||||
|
||||
result, err := s.WebhookStore.AnalyticsIncomingCount(teamID)
|
||||
result, err := s.WebhookStore.AnalyticsIncomingCount(teamID, userID)
|
||||
|
||||
elapsed := float64(time.Since(start)) / float64(time.Second)
|
||||
if s.Root.Metrics != nil {
|
||||
|
||||
@@ -7046,6 +7046,10 @@
|
||||
"id": "app.webhooks.get_incoming_by_user.app_error",
|
||||
"translation": "Unable to get the webhook."
|
||||
},
|
||||
{
|
||||
"id": "app.webhooks.get_incoming_count.app_error",
|
||||
"translation": "Unable to get the webhook for teamID={{.TeamID}}, userID={{.UserID}}, err={{.Error}}."
|
||||
},
|
||||
{
|
||||
"id": "app.webhooks.get_outgoing.app_error",
|
||||
"translation": "Unable to get the webhook."
|
||||
|
||||
@@ -351,7 +351,7 @@ func (ts *TelemetryService) trackActivity() {
|
||||
|
||||
slashCommandsCount, _ = ts.dbStore.Command().AnalyticsCommandCount("")
|
||||
|
||||
if c, err := ts.dbStore.Webhook().AnalyticsIncomingCount(""); err == nil {
|
||||
if c, err := ts.dbStore.Webhook().AnalyticsIncomingCount("", ""); err == nil {
|
||||
incomingWebhooksCount = c
|
||||
}
|
||||
|
||||
|
||||
@@ -231,7 +231,7 @@ func initializeMocks(cfg *model.Config, cloudLicense bool) (*mocks.ServerIface,
|
||||
commandStore.On("AnalyticsCommandCount", "").Return(int64(15), nil)
|
||||
|
||||
webhookStore := storeMocks.WebhookStore{}
|
||||
webhookStore.On("AnalyticsIncomingCount", "").Return(int64(16), nil)
|
||||
webhookStore.On("AnalyticsIncomingCount", "", "").Return(int64(16), nil)
|
||||
webhookStore.On("AnalyticsOutgoingCount", "").Return(int64(17), nil)
|
||||
|
||||
groupStore := storeMocks.GroupStore{}
|
||||
|
||||
@@ -4983,6 +4983,24 @@ func (c *Client4) GetIncomingWebhooks(ctx context.Context, page int, perPage int
|
||||
return iwl, BuildResponse(r), nil
|
||||
}
|
||||
|
||||
// GetIncomingWebhooksWithCount returns a page of incoming webhooks on the system including the total count. Page counting starts at 0.
|
||||
func (c *Client4) GetIncomingWebhooksWithCount(ctx context.Context, page int, perPage int, etag string) (*IncomingWebhooksWithCount, *Response, error) {
|
||||
query := fmt.Sprintf("?page=%v&per_page=%v&include_total_count="+c.boolString(true), page, perPage)
|
||||
r, err := c.DoAPIGet(ctx, c.incomingWebhooksRoute()+query, etag)
|
||||
if err != nil {
|
||||
return nil, BuildResponse(r), err
|
||||
}
|
||||
defer closeBody(r)
|
||||
var iwl *IncomingWebhooksWithCount
|
||||
if r.StatusCode == http.StatusNotModified {
|
||||
return iwl, BuildResponse(r), nil
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&iwl); err != nil {
|
||||
return nil, nil, NewAppError("GetIncomingWebhooksWithCount", "api.unmarshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
return iwl, BuildResponse(r), nil
|
||||
}
|
||||
|
||||
// GetIncomingWebhooksForTeam returns a page of incoming webhooks for a team. Page counting starts at 0.
|
||||
func (c *Client4) GetIncomingWebhooksForTeam(ctx context.Context, teamId string, page int, perPage int, etag string) ([]*IncomingWebhook, *Response, error) {
|
||||
query := fmt.Sprintf("?page=%v&per_page=%v&team_id=%v", page, perPage, teamId)
|
||||
|
||||
@@ -59,6 +59,11 @@ type IncomingWebhookRequest struct {
|
||||
Priority *PostPriority `json:"priority"`
|
||||
}
|
||||
|
||||
type IncomingWebhooksWithCount struct {
|
||||
Webhooks []*IncomingWebhook `json:"incoming_webhooks"`
|
||||
TotalCount int64 `json:"total_count"`
|
||||
}
|
||||
|
||||
func (o *IncomingWebhook) IsValid() *AppError {
|
||||
if !IsValidId(o.Id) {
|
||||
return NewAppError("IncomingWebhook.IsValid", "model.incoming_hook.id.app_error", nil, "", http.StatusBadRequest)
|
||||
|
||||
Ссылка в новой задаче
Block a user