Skip to content

Commit

Permalink
Merge branch 'master' into weblate-playbooks-webapp
Browse files Browse the repository at this point in the history
  • Loading branch information
mattermost-build authored Dec 13, 2024
2 parents 225ca3a + 2ac07ec commit 9a44db6
Show file tree
Hide file tree
Showing 13 changed files with 195 additions and 63 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ server/e2etest.config.json

# Notice
.notice-work
.aider*
.env
8 changes: 4 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,12 @@ require (
github.com/gorilla/mux v1.8.1
github.com/graph-gophers/dataloader/v7 v7.1.0
github.com/graph-gophers/graphql-go v1.4.0
github.com/hashicorp/go-multierror v1.1.1
github.com/jmoiron/sqlx v1.4.0
github.com/lib/pq v1.10.9
github.com/mattermost/mattermost-plugin-playbooks/client v0.7.0
github.com/mattermost/mattermost/server/public v0.1.8
github.com/mattermost/mattermost/server/v8 v8.0.0-20241112090719-5eef415a39e1
github.com/mattermost/mattermost/server/public v0.1.9
github.com/mattermost/mattermost/server/v8 v8.0.0-20241113102039-053d0b5f0ad5
github.com/mattermost/morph v1.1.0
github.com/mitchellh/mapstructure v1.5.0
github.com/pkg/errors v0.9.1
Expand All @@ -36,6 +37,7 @@ require (
github.com/stretchr/testify v1.9.0
github.com/writeas/go-strip-markdown v2.0.1+incompatible
gopkg.in/guregu/null.v4 v4.0.0
gopkg.in/yaml.v3 v3.0.1
)

require (
Expand Down Expand Up @@ -107,7 +109,6 @@ require (
github.com/h2non/go-is-svg v0.0.0-20160927212452-35e8c4b0612c // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-hclog v1.6.3 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-plugin v1.6.1 // indirect
github.com/hashicorp/golang-lru v1.0.2 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
Expand Down Expand Up @@ -203,7 +204,6 @@ require (
gopkg.in/mail.v2 v2.3.1 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
modernc.org/gc/v3 v3.0.0-20240722195230-4a140ff9c08e // indirect
modernc.org/libc v1.55.3 // indirect
modernc.org/mathutil v1.6.0 // indirect
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -295,10 +295,10 @@ github.com/mattermost/ldap v0.0.0-20231116144001-0f480c025956 h1:Y1Tu/swM31pVwwb
github.com/mattermost/ldap v0.0.0-20231116144001-0f480c025956/go.mod h1:SRl30Lb7/QoYyohYeVBuqYvvmXSZJxZgiV3Zf6VbxjI=
github.com/mattermost/logr/v2 v2.0.21 h1:CMHsP+nrbRlEC4g7BwOk1GAnMtHkniFhlSQPXy52be4=
github.com/mattermost/logr/v2 v2.0.21/go.mod h1:kZkB/zqKL9e+RY5gB3vGpsyenC+TpuiOenjMkvJJbzc=
github.com/mattermost/mattermost/server/public v0.1.8 h1:Z2PUXR4YGquuSo3ojNUl0aazOMSRqALjyMaf20jNIy4=
github.com/mattermost/mattermost/server/public v0.1.8/go.mod h1:SkTKbMul91Rq0v2dIxe8mqzUOY+3KwlwwLmAlxDfGCk=
github.com/mattermost/mattermost/server/v8 v8.0.0-20241112090719-5eef415a39e1 h1:qNW9//+lx3kwCETTCvqjrFgQ3SH0DyHusE3PnGJfHVE=
github.com/mattermost/mattermost/server/v8 v8.0.0-20241112090719-5eef415a39e1/go.mod h1:B4pQsrbZs6yO4GpWY6nCJPNG7myB0r3gvlFWWlGABmc=
github.com/mattermost/mattermost/server/public v0.1.9 h1:l/OKPRVuFeqL0yqRVC/JpveG5sLNKcT9llxqMkO9e+s=
github.com/mattermost/mattermost/server/public v0.1.9/go.mod h1:SkTKbMul91Rq0v2dIxe8mqzUOY+3KwlwwLmAlxDfGCk=
github.com/mattermost/mattermost/server/v8 v8.0.0-20241113102039-053d0b5f0ad5 h1:XVePV2EZhapQ6JIGFycfTd1QgxICvDs8rRUy3pqxagQ=
github.com/mattermost/mattermost/server/v8 v8.0.0-20241113102039-053d0b5f0ad5/go.mod h1:B4pQsrbZs6yO4GpWY6nCJPNG7myB0r3gvlFWWlGABmc=
github.com/mattermost/morph v1.1.0 h1:Q9vrJbeM3s2jfweGheq12EFIzdNp9a/6IovcbvOQ6Cw=
github.com/mattermost/morph v1.1.0/go.mod h1:gD+EaqX2UMyyuzmF4PFh4r33XneQ8Nzi+0E8nXjMa3A=
github.com/mattermost/rsc v0.0.0-20160330161541-bbaefb05eaa0 h1:G9tL6JXRBMzjuD1kkBtcnd42kUiT6QDwxfFYu7adM6o=
Expand Down
2 changes: 1 addition & 1 deletion server/api/api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1249,7 +1249,7 @@ paths:
500:
$ref: "#/components/responses/500"

/plugins/playbooks/api/v0/runs/{id}/timeline/{event_id}/:
/plugins/playbooks/api/v0/runs/{id}/timeline/{event_id}:
delete:
summary: Remove a timeline event from the playbook run
operationId: removeTimelineEvent
Expand Down
10 changes: 5 additions & 5 deletions server/api_runs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1304,7 +1304,7 @@ func TestIgnoreKeywords(t *testing.T) {
},
},
}
botPost, err := e.Srv.Store().Post().Save(botPost)
botPost, err := e.Srv.Store().Post().Save(e.Context, botPost)
require.NoError(t, err)

// Create post action request
Expand All @@ -1321,14 +1321,14 @@ func TestIgnoreKeywords(t *testing.T) {
require.NoError(t, err)

// Make the request
result, err := e.ServerClient.DoAPIRequestBytes("POST", e.ServerClient.URL+"/plugins/"+manifest.Id+"/api/v0/signal/keywords/ignore-thread", reqBytes, "")
result, err := e.ServerClient.DoAPIRequestBytes(context.Background(), "POST", e.ServerClient.URL+"/plugins/"+manifest.Id+"/api/v0/signal/keywords/ignore-thread", reqBytes, "")
require.Error(t, err)
require.Equal(t, http.StatusForbidden, result.StatusCode)
})

t.Run("has permission to channel", func(t *testing.T) {
// Add user to private channel
_, _, err := e.ServerAdminClient.AddChannelMember(e.BasicPrivateChannel.Id, e.RegularUser.Id)
_, _, err := e.ServerAdminClient.AddChannelMember(context.Background(), e.BasicPrivateChannel.Id, e.RegularUser.Id)
require.NoError(t, err)

// Create a bot post in the private channel
Expand All @@ -1348,7 +1348,7 @@ func TestIgnoreKeywords(t *testing.T) {
},
},
}
botPost, err = e.Srv.Store().Post().Save(botPost)
botPost, err = e.Srv.Store().Post().Save(e.Context, botPost)
require.NoError(t, err)

// Create post action request
Expand All @@ -1365,7 +1365,7 @@ func TestIgnoreKeywords(t *testing.T) {
require.NoError(t, err)

// Make the request
result, err := e.ServerClient.DoAPIRequestBytes("POST", e.ServerClient.URL+"/plugins/"+manifest.Id+"/api/v0/signal/keywords/ignore-thread", reqBytes, "")
result, err := e.ServerClient.DoAPIRequestBytes(context.Background(), "POST", e.ServerClient.URL+"/plugins/"+manifest.Id+"/api/v0/signal/keywords/ignore-thread", reqBytes, "")
require.NoError(t, err)
require.Equal(t, http.StatusOK, result.StatusCode)
})
Expand Down
6 changes: 6 additions & 0 deletions server/app/playbook.go
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,9 @@ type PlaybookService interface {
// GetPlaybooks retrieves all playbooks
GetPlaybooks() ([]Playbook, error)

// GetActivePlaybooks retrieves all active playbooks
GetActivePlaybooks() ([]Playbook, error)

// GetPlaybooksForTeam retrieves all playbooks on the specified team given the provided options
GetPlaybooksForTeam(requesterInfo RequesterInfo, teamID string, opts PlaybookFilterOptions) (GetPlaybooksResults, error)

Expand Down Expand Up @@ -388,6 +391,9 @@ type PlaybookStore interface {
// GetPlaybooks retrieves all playbooks
GetPlaybooks() ([]Playbook, error)

// GetActivePlaybooks retrieves all active playbooks
GetActivePlaybooks() ([]Playbook, error)

// GetPlaybooksForTeam retrieves all playbooks on the specified team
GetPlaybooksForTeam(requesterInfo RequesterInfo, teamID string, opts PlaybookFilterOptions) (GetPlaybooksResults, error)

Expand Down
4 changes: 4 additions & 0 deletions server/app/playbook_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ func (s *playbookService) GetPlaybooks() ([]Playbook, error) {
return s.store.GetPlaybooks()
}

func (s *playbookService) GetActivePlaybooks() ([]Playbook, error) {
return s.store.GetActivePlaybooks()
}

func (s *playbookService) GetPlaybooksForTeam(requesterInfo RequesterInfo, teamID string, opts PlaybookFilterOptions) (GetPlaybooksResults, error) {
return s.store.GetPlaybooksForTeam(requesterInfo, teamID, opts)
}
Expand Down
2 changes: 1 addition & 1 deletion server/command/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -1155,7 +1155,7 @@ And... yes, of course, we have emojis
return
}

gotPlaybooks, err := r.playbookService.GetPlaybooks()
gotPlaybooks, err := r.playbookService.GetActivePlaybooks()
if err != nil {
r.postCommandResponse("There was an error while retrieving all playbooks. Err: " + err.Error())
return
Expand Down
108 changes: 66 additions & 42 deletions server/sqlstore/playbook.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,58 +336,82 @@ func (p *playbookStore) Get(id string) (app.Playbook, error) {
return playbook, nil
}

func selectAllPlaybooks(builder sq.StatementBuilderType) sq.SelectBuilder {
return builder.Select(
"p.ID",
"p.Title",
"p.Description",
"p.TeamID",
"p.Public",
"p.CreatePublicIncident AS CreatePublicPlaybookRun",
"p.CreateAt",
"p.DeleteAt",
"p.NumStages",
"p.NumSteps",
"COUNT(i.ID) AS NumRuns",
"COALESCE(MAX(i.CreateAt), 0) AS LastRunAt",
`(
1 + -- Channel creation is hard-coded
CASE WHEN p.InviteUsersEnabled THEN 1 ELSE 0 END +
CASE WHEN p.DefaultCommanderEnabled THEN 1 ELSE 0 END +
CASE WHEN p.BroadcastEnabled THEN 1 ELSE 0 END +
CASE WHEN p.WebhookOnCreationEnabled THEN 1 ELSE 0 END +
CASE WHEN p.MessageOnJoinEnabled THEN 1 ELSE 0 END +
CASE WHEN p.WebhookOnStatusUpdateEnabled THEN 1 ELSE 0 END +
CASE WHEN p.SignalAnyKeywordsEnabled THEN 1 ELSE 0 END +
CASE WHEN p.CategorizeChannelEnabled THEN 1 ELSE 0 END +
CASE WHEN p.CreateChannelMemberOnNewParticipant THEN 1 ELSE 0 END +
CASE WHEN p.RemoveChannelMemberOnRemovedParticipant THEN 1 ELSE 0 END
) AS NumActions`,
"COALESCE(ChannelNameTemplate, '') ChannelNameTemplate",
"COALESCE(s.DefaultPlaybookAdminRole, 'playbook_admin') DefaultPlaybookAdminRole",
"COALESCE(s.DefaultPlaybookMemberRole, 'playbook_member') DefaultPlaybookMemberRole",
"COALESCE(s.DefaultRunAdminRole, 'run_admin') DefaultRunAdminRole",
"COALESCE(s.DefaultRunMemberRole, 'run_member') DefaultRunMemberRole",
).
From("IR_Playbook AS p").
LeftJoin("IR_Incident AS i ON p.ID = i.PlaybookID").
LeftJoin("Teams t ON t.Id = p.TeamID").
LeftJoin("Schemes s ON t.SchemeId = s.Id").
GroupBy("p.ID").
GroupBy("s.Id")
}

// GetPlaybooks retrieves all playbooks that are not deleted.
// Members are not retrieved for this as the query would be large and we don't need it for this for now.
// This is only used for the keywords feature
func (p *playbookStore) GetPlaybooks() ([]app.Playbook, error) {
func (p *playbookStore) GetActivePlaybooks() ([]app.Playbook, error) {
tx, err := p.store.db.Beginx()
if err != nil {
return nil, errors.Wrap(err, "could not begin transaction")
}
defer p.store.finalizeTransaction(tx)

var playbooks []app.Playbook
err = p.store.selectBuilder(tx, &playbooks, p.store.builder.
Select(
"p.ID",
"p.Title",
"p.Description",
"p.TeamID",
"p.Public",
"p.CreatePublicIncident AS CreatePublicPlaybookRun",
"p.CreateAt",
"p.DeleteAt",
"p.NumStages",
"p.NumSteps",
"COUNT(i.ID) AS NumRuns",
"COALESCE(MAX(i.CreateAt), 0) AS LastRunAt",
`(
1 + -- Channel creation is hard-coded
CASE WHEN p.InviteUsersEnabled THEN 1 ELSE 0 END +
CASE WHEN p.DefaultCommanderEnabled THEN 1 ELSE 0 END +
CASE WHEN p.BroadcastEnabled THEN 1 ELSE 0 END +
CASE WHEN p.WebhookOnCreationEnabled THEN 1 ELSE 0 END +
CASE WHEN p.MessageOnJoinEnabled THEN 1 ELSE 0 END +
CASE WHEN p.WebhookOnStatusUpdateEnabled THEN 1 ELSE 0 END +
CASE WHEN p.SignalAnyKeywordsEnabled THEN 1 ELSE 0 END +
CASE WHEN p.CategorizeChannelEnabled THEN 1 ELSE 0 END +
CASE WHEN p.CreateChannelMemberOnNewParticipant THEN 1 ELSE 0 END +
CASE WHEN p.RemoveChannelMemberOnRemovedParticipant THEN 1 ELSE 0 END
) AS NumActions`,
"COALESCE(ChannelNameTemplate, '') ChannelNameTemplate",
"COALESCE(s.DefaultPlaybookAdminRole, 'playbook_admin') DefaultPlaybookAdminRole",
"COALESCE(s.DefaultPlaybookMemberRole, 'playbook_member') DefaultPlaybookMemberRole",
"COALESCE(s.DefaultRunAdminRole, 'run_admin') DefaultRunAdminRole",
"COALESCE(s.DefaultRunMemberRole, 'run_member') DefaultRunMemberRole",
).
From("IR_Playbook AS p").
LeftJoin("IR_Incident AS i ON p.ID = i.PlaybookID").
LeftJoin("Teams t ON t.Id = p.TeamID").
LeftJoin("Schemes s ON t.SchemeId = s.Id").
Where(sq.Eq{"p.DeleteAt": 0}).
GroupBy("p.ID").
GroupBy("s.Id"))
err = p.store.selectBuilder(tx, &playbooks,
selectAllPlaybooks(p.store.builder).Where(sq.Eq{"p.DeleteAt": 0}),
)
if err == sql.ErrNoRows {
return nil, errors.Wrap(app.ErrNotFound, "no playbooks found")
} else if err != nil {
return nil, errors.Wrap(err, "failed to get playbooks")
}

return playbooks, nil
}

// GetPlaybooks retrieves all playbooks, even deleted ones.
// Members are not retrieved for this as the query would be large and we don't need it for this for now.
func (p *playbookStore) GetPlaybooks() ([]app.Playbook, error) {
tx, err := p.store.db.Beginx()
if err != nil {
return nil, errors.Wrap(err, "could not begin transaction")
}
defer p.store.finalizeTransaction(tx)

var playbooks []app.Playbook
err = p.store.selectBuilder(tx, &playbooks,
selectAllPlaybooks(p.store.builder),
)
if err == sql.ErrNoRows {
return nil, errors.Wrap(app.ErrNotFound, "no playbooks found")
} else if err != nil {
Expand Down
8 changes: 4 additions & 4 deletions server/sqlstore/playbook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -880,7 +880,7 @@ func TestGetPlaybooksForTeam(t *testing.T) {
playbookStore := setupPlaybookStore(t, db)

t.Run(driverName+" - zero playbooks", func(t *testing.T) {
result, err := playbookStore.GetPlaybooks()
result, err := playbookStore.GetActivePlaybooks()
require.NoError(t, err)
require.ElementsMatch(t, []app.Playbook{}, result)
})
Expand Down Expand Up @@ -1344,7 +1344,7 @@ func TestGetPlaybooksForKeywords(t *testing.T) {
playbookStore := setupPlaybookStore(t, db)

t.Run("zero playbooks", func(t *testing.T) {
result, err := playbookStore.GetPlaybooks()
result, err := playbookStore.GetActivePlaybooks()
require.NoError(t, err)
require.ElementsMatch(t, []app.Playbook{}, result)
})
Expand Down Expand Up @@ -1421,7 +1421,7 @@ func TestGetTimeLastUpdated(t *testing.T) {
playbookStore := setupPlaybookStore(t, db)

t.Run("zero playbooks", func(t *testing.T) {
result, err := playbookStore.GetPlaybooks()
result, err := playbookStore.GetActivePlaybooks()
require.NoError(t, err)
require.ElementsMatch(t, []app.Playbook{}, result)

Expand Down Expand Up @@ -1540,7 +1540,7 @@ func TestGetPlaybookIDsForUser(t *testing.T) {
playbookStore := setupPlaybookStore(t, db)

t.Run("zero playbooks", func(t *testing.T) {
result, err := playbookStore.GetPlaybooks()
result, err := playbookStore.GetActivePlaybooks()
require.NoError(t, err)
require.ElementsMatch(t, []app.Playbook{}, result)
})
Expand Down
58 changes: 58 additions & 0 deletions server/support_packet.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package main

import (
"path/filepath"

"github.com/hashicorp/go-multierror"
"github.com/pkg/errors"
"gopkg.in/yaml.v3"

"github.com/mattermost/mattermost-plugin-playbooks/server/app"
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/public/plugin"
)

type SupportPacket struct {
Version string `yaml:"version"`
// The total number of playbooks.
TotalPlaybooks int64 `yaml:"total_playbooks"`
// The number of active playbooks.
ActivePlaybooks int64 `yaml:"active_playbooks"`
// The total number of playbook runs.
TotalPlaybookRuns int64 `yaml:"total_playbook_runs"`
}

func (p *Plugin) GenerateSupportData(_ *plugin.Context) ([]*model.FileData, error) {
var result *multierror.Error

playbooks, err := p.playbookService.GetPlaybooks()
if err != nil {
result = multierror.Append(result, errors.Wrap(err, "Failed to get total number of playbooks for Support Packet"))
}

activePlaybooks, err := p.playbookService.GetActivePlaybooks()
if err != nil {
result = multierror.Append(result, errors.Wrap(err, "Failed to get number of active playbooks for Support Packet"))
}

playbookRuns, err := p.playbookRunService.GetPlaybookRuns(app.RequesterInfo{IsAdmin: true}, app.PlaybookRunFilterOptions{SkipExtras: true})
if err != nil {
result = multierror.Append(result, errors.Wrap(err, "Failed to get total number of playbook runs for Support Packet"))
}

diagnostics := SupportPacket{
Version: manifest.Version,
TotalPlaybooks: int64(len(playbooks)),
ActivePlaybooks: int64(len(activePlaybooks)),
TotalPlaybookRuns: int64(playbookRuns.TotalCount),
}
body, err := yaml.Marshal(diagnostics)
if err != nil {
return nil, errors.Wrap(err, "Failed to marshal diagnostics")
}

return []*model.FileData{{
Filename: filepath.Join(manifest.Id, "diagnostics.yaml"),
Body: body,
}}, result.ErrorOrNil()
}
Loading

0 comments on commit 9a44db6

Please sign in to comment.