Skip to content

Commit

Permalink
Merge branch 'master' into rdp-remove-experiemental
Browse files Browse the repository at this point in the history
* master:
  requested update timeline event (#1324)
  Filter playbooks for LHS (#1325)
  MM-45352: Run Details Page - RHS E2E (#1326)
  [MM-44755] Favoriting Playbooks (#1312)
  • Loading branch information
trilopin committed Jul 14, 2022
2 parents 16b0e0b + ce9b80f commit c1c83e2
Show file tree
Hide file tree
Showing 33 changed files with 1,009 additions and 98 deletions.
19 changes: 13 additions & 6 deletions client/playbook_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,19 @@ type Metadata struct {
type TimelineEventType string

const (
PlaybookRunCreated TimelineEventType = "incident_created"
TaskStateModified TimelineEventType = "task_state_modified"
StatusUpdated TimelineEventType = "status_updated"
OwnerChanged TimelineEventType = "owner_changed"
AssigneeChanged TimelineEventType = "assignee_changed"
RanSlashCommand TimelineEventType = "ran_slash_command"
PlaybookRunCreated TimelineEventType = "incident_created"
TaskStateModified TimelineEventType = "task_state_modified"
StatusUpdated TimelineEventType = "status_updated"
StatusUpdateRequested TimelineEventType = "status_update_requested"
OwnerChanged TimelineEventType = "owner_changed"
AssigneeChanged TimelineEventType = "assignee_changed"
RanSlashCommand TimelineEventType = "ran_slash_command"
EventFromPost TimelineEventType = "event_from_post"
UserJoinedLeft TimelineEventType = "user_joined_left"
PublishedRetrospective TimelineEventType = "published_retrospective"
CanceledRetrospective TimelineEventType = "canceled_retrospective"
RunFinished TimelineEventType = "run_finished"
RunRestored TimelineEventType = "run_restored"
)

// TimelineEvent represents an event recorded to a playbook run's timeline.
Expand Down
76 changes: 70 additions & 6 deletions server/api/categories.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ func NewCategoryHandler(router *mux.Router, api *pluginapi.Client, logger bot.Lo
categoriesRouter := router.PathPrefix("/my_categories").Subrouter()
categoriesRouter.HandleFunc("", handler.getMyCategories).Methods(http.MethodGet)
categoriesRouter.HandleFunc("", handler.createMyCategory).Methods(http.MethodPost)
categoriesRouter.HandleFunc("/favorites", handler.addFavorite).Methods(http.MethodPost)
categoriesRouter.HandleFunc("/favorites", handler.deleteFavorite).Methods(http.MethodDelete)
categoriesRouter.HandleFunc("/favorites", handler.isFavorite).Methods(http.MethodGet)

categoryRouter := categoriesRouter.PathPrefix("/{id:[A-Za-z0-9]+}").Subrouter()
categoryRouter.HandleFunc("", handler.updateMyCategory).Methods(http.MethodPut)
Expand All @@ -52,13 +55,14 @@ func (h *CategoryHandler) getMyCategories(w http.ResponseWriter, r *http.Request
h.HandleError(w, err)
return
}
filteredCustomCategories := filterEmptyCategories(customCategories)

runsCategory, err := h.getRunsCategory(teamID, userID)
if err != nil {
h.HandleError(w, err)
return
}
filteredRuns := filterDuplicatesFromCategory(runsCategory, customCategories)
filteredRuns := filterDuplicatesFromCategory(runsCategory, filteredCustomCategories)
allCategories := append([]app.Category{}, customCategories...)
allCategories = append(allCategories, filteredRuns)

Expand All @@ -67,11 +71,10 @@ func (h *CategoryHandler) getMyCategories(w http.ResponseWriter, r *http.Request
h.HandleError(w, err)
return
}
filteredPlaybooks := filterDuplicatesFromCategory(playbooksCategory, customCategories)
filteredPlaybooks := filterDuplicatesFromCategory(playbooksCategory, filteredCustomCategories)
allCategories = append(allCategories, filteredPlaybooks)

filteredCategories := filterEmptyCategories(allCategories)
ReturnJSON(w, filteredCategories, http.StatusOK)
ReturnJSON(w, allCategories, http.StatusOK)
}

func (h *CategoryHandler) createMyCategory(w http.ResponseWriter, r *http.Request) {
Expand Down Expand Up @@ -144,7 +147,9 @@ func (h *CategoryHandler) updateMyCategory(w http.ResponseWriter, r *http.Reques

if err := h.categoryService.Update(category); err != nil {
h.HandleError(w, err)
return
}
w.WriteHeader(http.StatusOK)
}

func (h *CategoryHandler) deleteMyCategory(w http.ResponseWriter, r *http.Request) {
Expand Down Expand Up @@ -173,7 +178,65 @@ func (h *CategoryHandler) deleteMyCategory(w http.ResponseWriter, r *http.Reques

if err := h.categoryService.Delete(categoryID); err != nil {
h.HandleError(w, err)
return
}
w.WriteHeader(http.StatusNoContent)
}

func (h *CategoryHandler) addFavorite(w http.ResponseWriter, r *http.Request) {
userID := r.Header.Get("Mattermost-User-ID")
params := r.URL.Query()
teamID := params.Get("team_id")

var item app.CategoryItem
if err := json.NewDecoder(r.Body).Decode(&item); err != nil {
h.HandleErrorWithCode(w, http.StatusBadRequest, "unable to decode category item", err)
return
}

if err := h.categoryService.AddFavorite(item, teamID, userID); err != nil {
h.HandleError(w, err)
return
}
w.WriteHeader(http.StatusOK)
}

func (h *CategoryHandler) deleteFavorite(w http.ResponseWriter, r *http.Request) {
userID := r.Header.Get("Mattermost-User-ID")
params := r.URL.Query()
teamID := params.Get("team_id")

var item app.CategoryItem
if err := json.NewDecoder(r.Body).Decode(&item); err != nil {
h.HandleErrorWithCode(w, http.StatusBadRequest, "unable to decode category item", err)
return
}

if err := h.categoryService.DeleteFavorite(item, teamID, userID); err != nil {
h.HandleError(w, err)
return
}
w.WriteHeader(http.StatusNoContent)
}

func (h *CategoryHandler) isFavorite(w http.ResponseWriter, r *http.Request) {
userID := r.Header.Get("Mattermost-User-ID")
params := r.URL.Query()
teamID := params.Get("team_id")
itemID := params.Get("item_id")
itemType := params.Get("type")
convertedItemType, err := app.StringToItemType(itemType)
if err != nil {
h.HandleError(w, err)
return
}

isFavorite, err := h.categoryService.IsItemFavorite(app.CategoryItem{ItemID: itemID, Type: convertedItemType}, teamID, userID)
if err != nil {
h.HandleError(w, err)
return
}
ReturnJSON(w, isFavorite, http.StatusOK)
}

func (h *CategoryHandler) getRunsCategory(teamID, userID string) (app.Category, error) {
Expand Down Expand Up @@ -221,8 +284,9 @@ func (h *CategoryHandler) getPlaybooksCategory(teamID, userID string) (app.Categ
},
teamID,
app.PlaybookFilterOptions{
Page: 0,
PerPage: maxItemsInRunsAndPlaybooksCategory,
Page: 0,
PerPage: maxItemsInRunsAndPlaybooksCategory,
WithMembershipOnly: true,
},
)
if err != nil {
Expand Down
5 changes: 5 additions & 0 deletions server/api/graphql.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
type GraphQLHandler struct {
*ErrorHandler
playbookService app.PlaybookService
categoryService app.CategoryService
pluginAPI *pluginapi.Client
log bot.Logger
config config.Service
Expand All @@ -35,6 +36,7 @@ var SchemaFile string
func NewGraphQLHandler(
router *mux.Router,
playbookService app.PlaybookService,
categoryService app.CategoryService,
api *pluginapi.Client,
log bot.Logger,
configService config.Service,
Expand All @@ -45,6 +47,7 @@ func NewGraphQLHandler(
handler := &GraphQLHandler{
ErrorHandler: &ErrorHandler{log: log},
playbookService: playbookService,
categoryService: categoryService,
pluginAPI: api,
log: log,
config: configService,
Expand Down Expand Up @@ -85,6 +88,7 @@ type Context struct {
r *http.Request
playbookService app.PlaybookService
playbookStore app.PlaybookStore
categoryService app.CategoryService
pluginAPI *pluginapi.Client
log bot.Logger
config config.Service
Expand Down Expand Up @@ -117,6 +121,7 @@ func (h *GraphQLHandler) graphQL(w http.ResponseWriter, r *http.Request) {
c := &Context{
r: r,
playbookService: h.playbookService,
categoryService: h.categoryService,
pluginAPI: h.pluginAPI,
log: h.log,
config: h.config,
Expand Down
1 change: 1 addition & 0 deletions server/api/graphql_playbook.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

type PlaybookResolver struct {
app.Playbook
IsFavorite bool
}

func (r *PlaybookResolver) DeleteAt() float64 {
Expand Down
40 changes: 39 additions & 1 deletion server/api/graphql_root.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,19 @@ func (r *RootResolver) Playbook(ctx context.Context, args struct {
if err != nil {
return nil, err
}
isFavorite, err := c.categoryService.IsItemFavorite(
app.CategoryItem{
ItemID: playbookID,
Type: app.PlaybookItemType,
},
playbook.TeamID,
userID,
)
if err != nil {
return nil, errors.Wrap(err, "can't determine if item is favorite or not")
}

return &PlaybookResolver{playbook}, nil
return &PlaybookResolver{playbook, isFavorite}, nil
}

type UpdateChecklist struct {
Expand Down Expand Up @@ -88,6 +99,7 @@ func (r *RootResolver) UpdatePlaybook(ctx context.Context, args struct {
RunSummaryTemplate *string
ChannelNameTemplate *string
Checklists *[]UpdateChecklist
IsFavorite *bool
}
}) (string, error) {
c, err := getContext(ctx)
Expand Down Expand Up @@ -209,6 +221,32 @@ func (r *RootResolver) UpdatePlaybook(ctx context.Context, args struct {
}
}

if args.Updates.IsFavorite != nil {
if *args.Updates.IsFavorite {
if err := c.categoryService.AddFavorite(
app.CategoryItem{
ItemID: currentPlaybook.ID,
Type: app.PlaybookItemType,
},
currentPlaybook.TeamID,
userID,
); err != nil {
return "", err
}
} else {
if err := c.categoryService.DeleteFavorite(
app.CategoryItem{
ItemID: currentPlaybook.ID,
Type: app.PlaybookItemType,
},
currentPlaybook.TeamID,
userID,
); err != nil {
return "", err
}
}
}

return args.ID, nil
}

Expand Down
2 changes: 2 additions & 0 deletions server/api/schema.graphqls
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ input PlaybookUpdates {
runSummaryTemplate: String
channelNameTemplate: String
checklists: [ChecklistUpdates!]
isFavorite: Boolean
}

input ChecklistUpdates {
Expand Down Expand Up @@ -104,6 +105,7 @@ type Playbook {
defaultRunAdminRole: String!
defaultRunMemberRole: String!
metrics: [PlaybookMetricConfig!]!
isFavorite: Boolean!
}

type Checklist {
Expand Down
24 changes: 21 additions & 3 deletions server/api_runs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1191,21 +1191,39 @@ func TestRequestUpdate(t *testing.T) {
// Gained Viewer access
err = e.PlaybooksClient2.PlaybookRuns.RequestUpdate(context.Background(), privateRun.ID, e.RegularUser2.Id)
assert.NoError(t, err)

// Assert that timeline event is created
privateRun, err = e.PlaybooksClient.PlaybookRuns.Get(context.Background(), privateRun.ID)
assert.NoError(t, err)
assert.NotEmpty(t, privateRun.TimelineEvents)
lastEvent := privateRun.TimelineEvents[len(privateRun.TimelineEvents)-1]
assert.Equal(t, client.StatusUpdateRequested, lastEvent.EventType)
assert.Equal(t, e.RegularUser2.Id, lastEvent.SubjectUserID)
assert.Equal(t, "@playbooksuser2 requested a status update", lastEvent.Summary)
})

t.Run("public - viewer access ", func(t *testing.T) {
privateRun, err := e.PlaybooksClient.PlaybookRuns.Create(context.Background(), client.PlaybookRunCreateOptions{
publicRun, err := e.PlaybooksClient.PlaybookRuns.Create(context.Background(), client.PlaybookRunCreateOptions{
Name: "Basic create",
OwnerUserID: e.RegularUser.Id,
TeamID: e.BasicTeam.Id,
PlaybookID: e.BasicPlaybook.ID,
})
assert.NoError(t, err)

err = e.PlaybooksClient2.PlaybookRuns.RequestUpdate(context.Background(), privateRun.ID, e.RegularUser2.Id)
err = e.PlaybooksClient2.PlaybookRuns.RequestUpdate(context.Background(), publicRun.ID, e.RegularUser2.Id)
assert.NoError(t, err)

err = e.PlaybooksClientNotInTeam.PlaybookRuns.RequestUpdate(context.Background(), privateRun.ID, e.RegularUserNotInTeam.Id)
// Assert that timeline event is created
publicRun, err = e.PlaybooksClient.PlaybookRuns.Get(context.Background(), publicRun.ID)
assert.NoError(t, err)
assert.NotEmpty(t, publicRun.TimelineEvents)
lastEvent := publicRun.TimelineEvents[len(publicRun.TimelineEvents)-1]
assert.Equal(t, client.StatusUpdateRequested, lastEvent.EventType)
assert.Equal(t, e.RegularUser2.Id, lastEvent.SubjectUserID)
assert.Equal(t, "@playbooksuser2 requested a status update", lastEvent.Summary)

err = e.PlaybooksClientNotInTeam.PlaybookRuns.RequestUpdate(context.Background(), publicRun.ID, e.RegularUserNotInTeam.Id)
assert.Error(t, err)
})

Expand Down
34 changes: 34 additions & 0 deletions server/app/category.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,18 @@ const (
RunItemType CategoryItemType = "r"
)

func StringToItemType(item string) (CategoryItemType, error) {
var convertedItem CategoryItemType
if item == string(PlaybookItemType) {
convertedItem = PlaybookItemType
} else if item == string(RunItemType) {
convertedItem = RunItemType
} else {
return PlaybookItemType, errors.New("unknown item type")
}
return convertedItem, nil
}

type CategoryItem struct {
ItemID string `json:"item_id"`
Type CategoryItemType `json:"type"`
Expand Down Expand Up @@ -86,6 +98,15 @@ type CategoryService interface {

// Delete deletes a category
Delete(categoryID string) error

// AddFavorite favorites an item, which may be either run or playbook
AddFavorite(item CategoryItem, teamID, userID string) error

// DeleteFavorite unfavorites an item, which may be either run or playbook
DeleteFavorite(item CategoryItem, teamID, userID string) error

// IsItemFavorite returns whether item was favorited or not
IsItemFavorite(item CategoryItem, teamID, userID string) (bool, error)
}

type CategoryStore interface {
Expand All @@ -103,4 +124,17 @@ type CategoryStore interface {

// Delete deletes a category
Delete(categoryID string) error

// GetFavoriteCategory returns favorite category
GetFavoriteCategory(teamID, userID string) (Category, error)

// AddItemToFavoriteCategory adds an item to favorite category,
// if favorite category does not exist it creates one
AddItemToFavoriteCategory(item CategoryItem, teamID, userID string) error

// AddItemToCategory adds an item to category
AddItemToCategory(item CategoryItem, categoryID string) error

// DeleteItemFromCategory adds an item to category
DeleteItemFromCategory(item CategoryItem, categoryID string) error
}
Loading

0 comments on commit c1c83e2

Please sign in to comment.