diff --git a/misc.go b/misc.go index df779c1a..509ec7f7 100644 --- a/misc.go +++ b/misc.go @@ -26,6 +26,12 @@ type SlackResponse struct { ResponseMetadata ResponseMetadata `json:"response_metadata"` } +type ResponsePointer struct { + Ok *bool `json:"ok,omitempty"` + Error *string `json:"error,omitempty"` + ResponseMetadata ResponseMetadata `json:"response_metadata,omitempty"` +} + func (t SlackResponse) Err() error { if t.Ok { return nil diff --git a/slack.go b/slack.go index 756106fe..505ab98d 100644 --- a/slack.go +++ b/slack.go @@ -2,6 +2,7 @@ package slack import ( "context" + "encoding/json" "fmt" "log" "net/http" @@ -168,6 +169,16 @@ func (api *Client) postMethod(ctx context.Context, path string, values url.Value return postForm(ctx, api.httpclient, api.endpoint+path, values, intf, api) } +// post to a slack web method with json. +func (api *Client) postJSON(ctx context.Context, path string, request interface{}, response interface{}) error { + jsonPayload, err := json.Marshal(request) + if err != nil { + return err + } + + return postJSON(ctx, api.httpclient, api.endpoint+path, api.token, jsonPayload, response, api) +} + // get a slack web method. func (api *Client) getMethod(ctx context.Context, path string, token string, values url.Values, intf interface{}) error { return getResource(ctx, api.httpclient, api.endpoint+path, token, values, intf, api) diff --git a/workflows_triggers.go b/workflows_triggers.go new file mode 100644 index 00000000..71fdf078 --- /dev/null +++ b/workflows_triggers.go @@ -0,0 +1,154 @@ +package slack + +import ( + "context" + "errors" + "fmt" +) + +type ( + WorkflowsTriggersPermissionsAddInput struct { + TriggerId string `json:"trigger_id"` + ChannelIds []string `json:"channel_ids,omitempty"` + OrgIds []string `json:"org_ids,omitempty"` + TeamIds []string `json:"team_ids,omitempty"` + UserIds []string `json:"user_ids,omitempty"` + } + + WorkflowsTriggersPermissionsAddOutput struct { + PermissionType string `json:"permission_type"` + ChannelIds []string `json:"channel_ids,omitempty"` + OrgIds []string `json:"org_ids,omitempty"` + TeamIds []string `json:"team_ids,omitempty"` + UserIds []string `json:"user_ids,omitempty"` + } + + WorkflowsTriggersPermissionsListInput struct { + TriggerId string `json:"trigger_id"` + } + + WorkflowsTriggersPermissionsListOutput struct { + PermissionType string `json:"permission_type"` + ChannelIds []string `json:"channel_ids,omitempty"` + OrgIds []string `json:"org_ids,omitempty"` + TeamIds []string `json:"team_ids,omitempty"` + UserIds []string `json:"user_ids,omitempty"` + } + + WorkflowsTriggersPermissionsRemoveInput struct { + TriggerId string `json:"trigger_id"` + ChannelIds []string `json:"channel_ids,omitempty"` + OrgIds []string `json:"org_ids,omitempty"` + TeamIds []string `json:"team_ids,omitempty"` + UserIds []string `json:"user_ids,omitempty"` + } + + WorkflowsTriggersPermissionsRemoveOutput struct { + PermissionType string `json:"permission_type"` + ChannelIds []string `json:"channel_ids,omitempty"` + OrgIds []string `json:"org_ids,omitempty"` + TeamIds []string `json:"team_ids,omitempty"` + UserIds []string `json:"user_ids,omitempty"` + } + + WorkflowsTriggersPermissionsSetInput struct { + PermissionType string `json:"permission_type"` + TriggerId string `json:"trigger_id"` + ChannelIds []string `json:"channel_ids,omitempty"` + OrgIds []string `json:"org_ids,omitempty"` + TeamIds []string `json:"team_ids,omitempty"` + UserIds []string `json:"user_ids,omitempty"` + } + + WorkflowsTriggersPermissionsSetOutput struct { + PermissionType string `json:"permission_type"` + ChannelIds []string `json:"channel_ids,omitempty"` + OrgIds []string `json:"org_ids,omitempty"` + TeamIds []string `json:"team_ids,omitempty"` + UserIds []string `json:"user_ids,omitempty"` + } +) + +// WorkflowsTriggersPermissionsAdd allows users to run a trigger that has its permission type set to named_entities. +// +// Slack API Docs:https://api.dev.slack.com/methods/workflows.triggers.permissions.add +func (api *Client) WorkflowsTriggersPermissionsAdd(ctx context.Context, input *WorkflowsTriggersPermissionsAddInput) (*WorkflowsTriggersPermissionsAddOutput, error) { + response := struct { + *ResponsePointer + *WorkflowsTriggersPermissionsAddOutput + }{} + + err := api.postJSON(ctx, "workflows.triggers.permissions.add", input, &response) + if err != nil { + return nil, err + } + + if response.Error != nil { + return nil, errors.New(fmt.Sprintf("error: %s", *response.Error)) + } + + return response.WorkflowsTriggersPermissionsAddOutput, nil +} + +// WorkflowsTriggersPermissionsList returns the permission type of a trigger and if applicable, includes the entities that have been granted access. +// +// Slack API Docs:https://api.dev.slack.com/methods/workflows.triggers.permissions.list +func (api *Client) WorkflowsTriggersPermissionsList(ctx context.Context, input *WorkflowsTriggersPermissionsListInput) (*WorkflowsTriggersPermissionsListOutput, error) { + response := struct { + *ResponsePointer + *WorkflowsTriggersPermissionsListOutput + }{} + + err := api.postJSON(ctx, "workflows.triggers.permissions.list", input, &response) + if err != nil { + return nil, err + } + + if response.Error != nil { + return nil, errors.New(fmt.Sprintf("error: %s", *response.Error)) + } + + return response.WorkflowsTriggersPermissionsListOutput, nil +} + +// WorkflowsTriggersPermissionsRemove revoke an entity's access to a trigger that has its permission type set to named_entities. +// +// Slack API Docs:https://api.dev.slack.com/methods/workflows.triggers.permissions.remove +func (api *Client) WorkflowsTriggersPermissionsRemove(ctx context.Context, input *WorkflowsTriggersPermissionsRemoveInput) (*WorkflowsTriggersPermissionsRemoveOutput, error) { + response := struct { + *ResponsePointer + *WorkflowsTriggersPermissionsRemoveOutput + }{} + + err := api.postJSON(ctx, "workflows.triggers.permissions.remove", input, &response) + if err != nil { + return nil, err + } + + if response.Error != nil { + return nil, errors.New(fmt.Sprintf("error: %s", *response.Error)) + } + + return response.WorkflowsTriggersPermissionsRemoveOutput, nil +} + +// WorkflowsTriggersPermissionsSet sets the permission type for who can run a trigger. +// +// Slack API Docs:https://api.dev.slack.com/methods/workflows.triggers.permissions.set +func (api *Client) WorkflowsTriggersPermissionsSet(ctx context.Context, input *WorkflowsTriggersPermissionsSetInput) (*WorkflowsTriggersPermissionsSetOutput, error) { + response := struct { + *ResponsePointer + *WorkflowsTriggersPermissionsSetOutput + }{} + + err := api.postJSON(ctx, "workflows.triggers.permissions.set", input, &response) + if err != nil { + return nil, err + } + + if response.Error != nil { + return nil, errors.New(fmt.Sprintf("error: %s", *response.Error)) + } + + return response.WorkflowsTriggersPermissionsSetOutput, nil +} diff --git a/workflows_triggers_test.go b/workflows_triggers_test.go new file mode 100644 index 00000000..695ec487 --- /dev/null +++ b/workflows_triggers_test.go @@ -0,0 +1,274 @@ +package slack + +import ( + "context" + "net/http" + "reflect" + "testing" +) + +type workflowsHandler struct { + rawResponse string +} + +func (h *workflowsHandler) handler(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(h.rawResponse)) +} + +func TestSlack_WorkflowsTriggersPermissionsAdd(t *testing.T) { + once.Do(startServer) + api := New("testing-token", OptionAPIURL("http://"+serverAddr+"/")) + + cases := []struct { + caseName string + input *WorkflowsTriggersPermissionsAddInput + rawResp string + expectedResp *WorkflowsTriggersPermissionsAddOutput + expectedErr error + }{ + { + caseName: "success", + input: &WorkflowsTriggersPermissionsAddInput{ + TriggerId: "Ft0000000001", + ChannelIds: []string{"C0000000001"}, + OrgIds: []string{"E00000001"}, + TeamIds: []string{"T0000000001"}, + UserIds: []string{"U0000000001", "U0000000002"}, + }, + rawResp: `{ + "ok": true, + "permission_type": "named_entities", + "user_ids": ["U0000000001", "U0000000002"], + "channel_ids": ["C0000000001"], + "org_ids": ["E00000001"], + "team_ids": ["T0000000001"] + }`, + expectedResp: &WorkflowsTriggersPermissionsAddOutput{ + PermissionType: "named_entities", + UserIds: []string{"U0000000001", "U0000000002"}, + ChannelIds: []string{"C0000000001"}, + OrgIds: []string{"E00000001"}, + TeamIds: []string{"T0000000001"}, + }, + expectedErr: nil, + }, + } + + h := &workflowsHandler{} + http.HandleFunc("/workflows.triggers.permissions.add", h.handler) + for _, c := range cases { + t.Run(c.caseName, func(t *testing.T) { + h.rawResponse = c.rawResp + + resp, err := api.WorkflowsTriggersPermissionsAdd(context.Background(), c.input) + if c.expectedErr == nil && err != nil { + t.Fatalf("unexpected error: %s\n", err) + } + if c.expectedErr != nil && err == nil { + t.Fatalf("expected %s, but did not raise an error", c.expectedErr) + } + if c.expectedErr != nil && err != nil && c.expectedErr.Error() != err.Error() { + t.Fatalf("expected %s as error but got %s\n", c.expectedErr, err) + } + if resp == nil || c.expectedResp == nil { + return + } + if !reflect.DeepEqual(resp, c.expectedResp) { + t.Fatalf("expected:\n\t%v\n but got:\n\t%v\n", c.expectedResp, resp) + } + }) + } +} + +func TestSlack_WorkflowsTriggersPermissionsList(t *testing.T) { + once.Do(startServer) + api := New("testing-token", OptionAPIURL("http://"+serverAddr+"/")) + + cases := []struct { + caseName string + input *WorkflowsTriggersPermissionsListInput + rawResp string + expectedResp *WorkflowsTriggersPermissionsListOutput + expectedErr error + }{ + { + caseName: "success", + input: &WorkflowsTriggersPermissionsListInput{ + TriggerId: "Ft0000000001", + }, + rawResp: `{ + "ok": true, + "permission_type": "named_entities", + "user_ids": ["U0000000001", "U0000000002"], + "channel_ids": ["C0000000001"], + "org_ids": ["E00000001"], + "team_ids": ["T0000000001"] + }`, + expectedResp: &WorkflowsTriggersPermissionsListOutput{ + PermissionType: "named_entities", + UserIds: []string{"U0000000001", "U0000000002"}, + ChannelIds: []string{"C0000000001"}, + OrgIds: []string{"E00000001"}, + TeamIds: []string{"T0000000001"}, + }, + expectedErr: nil, + }, + } + + h := &workflowsHandler{} + http.HandleFunc("/workflows.triggers.permissions.list", h.handler) + for _, c := range cases { + t.Run(c.caseName, func(t *testing.T) { + h.rawResponse = c.rawResp + + resp, err := api.WorkflowsTriggersPermissionsList(context.Background(), c.input) + if c.expectedErr == nil && err != nil { + t.Fatalf("unexpected error: %s\n", err) + } + if c.expectedErr != nil && err == nil { + t.Fatalf("expected %s, but did not raise an error", c.expectedErr) + } + if c.expectedErr != nil && err != nil && c.expectedErr.Error() != err.Error() { + t.Fatalf("expected %s as error but got %s\n", c.expectedErr, err) + } + if resp == nil || c.expectedResp == nil { + return + } + if !reflect.DeepEqual(resp, c.expectedResp) { + t.Fatalf("expected:\n\t%v\n but got:\n\t%v\n", c.expectedResp, resp) + } + }) + } +} + +func TestSlack_WorkflowsTriggersPermissionsRemove(t *testing.T) { + once.Do(startServer) + api := New("testing-token", OptionAPIURL("http://"+serverAddr+"/")) + + cases := []struct { + caseName string + input *WorkflowsTriggersPermissionsRemoveInput + rawResp string + expectedResp *WorkflowsTriggersPermissionsRemoveOutput + expectedErr error + }{ + { + caseName: "success", + input: &WorkflowsTriggersPermissionsRemoveInput{ + TriggerId: "Ft0000000001", + ChannelIds: []string{"C0000000001"}, + OrgIds: []string{"E00000001"}, + TeamIds: []string{"T0000000001"}, + UserIds: []string{"U0000000001", "U0000000002"}, + }, + rawResp: `{ + "ok": true, + "permission_type": "named_entities", + "user_ids": ["U0000000001", "U0000000002"], + "channel_ids": ["C0000000001"], + "org_ids": ["E00000001"], + "team_ids": ["T0000000001"] + }`, + expectedResp: &WorkflowsTriggersPermissionsRemoveOutput{ + PermissionType: "named_entities", + UserIds: []string{"U0000000001", "U0000000002"}, + ChannelIds: []string{"C0000000001"}, + OrgIds: []string{"E00000001"}, + TeamIds: []string{"T0000000001"}, + }, + expectedErr: nil, + }, + } + + h := &workflowsHandler{} + http.HandleFunc("/workflows.triggers.permissions.remove", h.handler) + for _, c := range cases { + t.Run(c.caseName, func(t *testing.T) { + h.rawResponse = c.rawResp + + resp, err := api.WorkflowsTriggersPermissionsRemove(context.Background(), c.input) + if c.expectedErr == nil && err != nil { + t.Fatalf("unexpected error: %s\n", err) + } + if c.expectedErr != nil && err == nil { + t.Fatalf("expected %s, but did not raise an error", c.expectedErr) + } + if c.expectedErr != nil && err != nil && c.expectedErr.Error() != err.Error() { + t.Fatalf("expected %s as error but got %s\n", c.expectedErr, err) + } + if resp == nil || c.expectedResp == nil { + return + } + if !reflect.DeepEqual(resp, c.expectedResp) { + t.Fatalf("expected:\n\t%v\n but got:\n\t%v\n", c.expectedResp, resp) + } + }) + } +} + +func TestSlack_WorkflowsTriggersPermissionsSet(t *testing.T) { + once.Do(startServer) + api := New("testing-token", OptionAPIURL("http://"+serverAddr+"/")) + + cases := []struct { + caseName string + input *WorkflowsTriggersPermissionsSetInput + rawResp string + expectedResp *WorkflowsTriggersPermissionsSetOutput + expectedErr error + }{ + { + caseName: "success", + input: &WorkflowsTriggersPermissionsSetInput{ + PermissionType: "named_entities", + TriggerId: "Ft0000000001", + ChannelIds: []string{"C0000000001"}, + OrgIds: []string{"E00000001"}, + TeamIds: []string{"T0000000001"}, + UserIds: []string{"U0000000001", "U0000000002"}, + }, + rawResp: `{ + "ok": true, + "permission_type": "named_entities", + "user_ids": ["U0000000001", "U0000000002"], + "channel_ids": ["C0000000001"], + "org_ids": ["E00000001"], + "team_ids": ["T0000000001"] + }`, + expectedResp: &WorkflowsTriggersPermissionsSetOutput{ + PermissionType: "named_entities", + UserIds: []string{"U0000000001", "U0000000002"}, + ChannelIds: []string{"C0000000001"}, + OrgIds: []string{"E00000001"}, + TeamIds: []string{"T0000000001"}, + }, + expectedErr: nil, + }, + } + + h := &workflowsHandler{} + http.HandleFunc("/workflows.triggers.permissions.set", h.handler) + for _, c := range cases { + t.Run(c.caseName, func(t *testing.T) { + h.rawResponse = c.rawResp + + resp, err := api.WorkflowsTriggersPermissionsSet(context.Background(), c.input) + if c.expectedErr == nil && err != nil { + t.Fatalf("unexpected error: %s\n", err) + } + if c.expectedErr != nil && err == nil { + t.Fatalf("expected %s, but did not raise an error", c.expectedErr) + } + if c.expectedErr != nil && err != nil && c.expectedErr.Error() != err.Error() { + t.Fatalf("expected %s as error but got %s\n", c.expectedErr, err) + } + if resp == nil || c.expectedResp == nil { + return + } + if !reflect.DeepEqual(resp, c.expectedResp) { + t.Fatalf("expected:\n\t%v\n but got:\n\t%v\n", c.expectedResp, resp) + } + }) + } +}