diff --git a/auth.go b/auth.go index f4f7f003a..bf6e80d36 100644 --- a/auth.go +++ b/auth.go @@ -38,3 +38,37 @@ func (api *Client) SendAuthRevokeContext(ctx context.Context, token string) (*Au return api.authRequest(ctx, "auth.revoke", values) } + +type listTeamsResponse struct { + Teams []Team `json:"teams"` + SlackResponse +} + +type ListTeamsParameters struct { + Limit int + Cursor string +} + +// ListTeams returns all workspaces a token can access. +// More info: https://api.slack.com/methods/admin.teams.list +func (api *Client) ListTeams(params ListTeamsParameters) ([]Team, string, error) { + return api.ListTeamsContext(context.Background(), params) +} + +// ListTeams returns all workspaces a token can access with a custom context. +func (api *Client) ListTeamsContext(ctx context.Context, params ListTeamsParameters) ([]Team, string, error) { + values := url.Values{ + "token": {api.token}, + } + if params.Cursor != "" { + values.Add("cursor", params.Cursor) + } + + response := &listTeamsResponse{} + err := api.postMethod(ctx, "auth.teams.list", values, response) + if err != nil { + return nil, "", err + } + + return response.Teams, response.ResponseMetadata.Cursor, response.Err() +} diff --git a/auth_test.go b/auth_test.go new file mode 100644 index 000000000..5fe9900eb --- /dev/null +++ b/auth_test.go @@ -0,0 +1,51 @@ +package slack + +import ( + "net/http" + "testing" + + "github.com/stretchr/testify/assert" +) + +func getTeamList(rw http.ResponseWriter, r *http.Request) { + rw.Header().Set("Content-Type", "application/json") + response := []byte(`{ + "ok": true, + "teams": [ + { + "name": "Shinichi's workspace", + "id": "T12345678" + }, + { + "name": "Migi's workspace", + "id": "T12345679" + } + ], + "response_metadata": { + "next_cursor": "dXNlcl9pZDo5MTQyOTI5Mzkz" + } +}`) + rw.Write(response) +} + +func TestListTeams(t *testing.T) { + http.HandleFunc("/auth.teams.list", getTeamList) + + once.Do(startServer) + api := New("testing-token", OptionAPIURL("http://"+serverAddr+"/")) + + teams, cursor, err := api.ListTeams(ListTeamsParameters{}) + if err != nil { + t.Errorf("Unexpected error: %s", err) + return + } + + assert.Len(t, teams, 2) + assert.Equal(t, "T12345678", teams[0].ID) + assert.Equal(t, "Shinichi's workspace", teams[0].Name) + + assert.Equal(t, "T12345679", teams[1].ID) + assert.Equal(t, "Migi's workspace", teams[1].Name) + + assert.Equal(t, "dXNlcl9pZDo5MTQyOTI5Mzkz", cursor) +} diff --git a/conversation.go b/conversation.go index d6a57073c..44f06ea93 100644 --- a/conversation.go +++ b/conversation.go @@ -343,17 +343,26 @@ func (api *Client) CloseConversationContext(ctx context.Context, channelID strin return response.NoOp, response.AlreadyClosed, response.Err() } +type CreateConversationParams struct { + ChannelName string + IsPrivate bool + TeamID string +} + // CreateConversation initiates a public or private channel-based conversation -func (api *Client) CreateConversation(channelName string, isPrivate bool) (*Channel, error) { - return api.CreateConversationContext(context.Background(), channelName, isPrivate) +func (api *Client) CreateConversation(params CreateConversationParams) (*Channel, error) { + return api.CreateConversationContext(context.Background(), params) } // CreateConversationContext initiates a public or private channel-based conversation with a custom context -func (api *Client) CreateConversationContext(ctx context.Context, channelName string, isPrivate bool) (*Channel, error) { +func (api *Client) CreateConversationContext(ctx context.Context, params CreateConversationParams) (*Channel, error) { values := url.Values{ "token": {api.token}, - "name": {channelName}, - "is_private": {strconv.FormatBool(isPrivate)}, + "name": {params.ChannelName}, + "is_private": {strconv.FormatBool(params.IsPrivate)}, + } + if params.TeamID != "" { + values.Set("team_id", params.TeamID) } response, err := api.channelRequest(ctx, "conversations.create", values) if err != nil { diff --git a/conversation_test.go b/conversation_test.go index 29f4b7742..85011d5a6 100644 --- a/conversation_test.go +++ b/conversation_test.go @@ -385,7 +385,7 @@ func TestCreateConversation(t *testing.T) { http.HandleFunc("/conversations.create", okChannelJsonHandler) once.Do(startServer) api := New("testing-token", OptionAPIURL("http://"+serverAddr+"/")) - channel, err := api.CreateConversation("CXXXXXXXX", false) + channel, err := api.CreateConversation(CreateConversationParams{ChannelName: "CXXXXXXXX"}) if err != nil { t.Errorf("Unexpected error: %s", err) return diff --git a/examples/pins/pins.go b/examples/pins/pins.go index d13d2d2c8..5c4f67c22 100644 --- a/examples/pins/pins.go +++ b/examples/pins/pins.go @@ -43,7 +43,7 @@ func main() { postAsUserID = authTest.UserID // Create a temporary channel - channel, err := api.CreateConversation(channelName, false) + channel, err := api.CreateConversation(slack.CreateConversationParams{ChannelName: channelName}) if err != nil { // If the channel exists, that means we just need to unarchive it diff --git a/slackevents/inner_events.go b/slackevents/inner_events.go index eb1e8df65..e88b6cefe 100644 --- a/slackevents/inner_events.go +++ b/slackevents/inner_events.go @@ -524,6 +524,18 @@ func (e MessageEvent) IsEdited() bool { e.Message.Edited != nil } +// TeamAccessGrantedEvent is sent if access to teams was granted for your org-wide app. +type TeamAccessGrantedEvent struct { + Type string `json:"type"` + TeamIDs []string `json:"team_ids"` +} + +// TeamAccessRevokedEvent is sent if access to teams was revoked for your org-wide app. +type TeamAccessRevokedEvent struct { + Type string `json:"type"` + TeamIDs []string `json:"team_ids"` +} + type EventsAPIType string const ( @@ -599,6 +611,10 @@ const ( MessageMetadataUpdated = EventsAPIType("message_metadata_updated") // MessageMetadataPosted A message with metadata was deleted MessageMetadataDeleted = EventsAPIType("message_metadata_deleted") + // TeamAccessGranted is sent if access to teams was granted for your org-wide app. + TeamAccessGranted = EventsAPIType("team_access_granted") + // TeamAccessrevoked is sent if access to teams was revoked for your org-wide app. + TeamAccessrevoked = EventsAPIType("team_access_revoked") ) // EventsAPIInnerEventMapping maps INNER Event API events to their corresponding struct @@ -641,4 +657,6 @@ var EventsAPIInnerEventMapping = map[EventsAPIType]interface{}{ MessageMetadataPosted: MessageMetadataPostedEvent{}, MessageMetadataUpdated: MessageMetadataUpdatedEvent{}, MessageMetadataDeleted: MessageMetadataDeletedEvent{}, + TeamAccessGranted: TeamAccessGrantedEvent{}, + TeamAccessrevoked: TeamAccessRevokedEvent{}, } diff --git a/slackevents/outer_events.go b/slackevents/outer_events.go index 6277248dc..bcc85f331 100644 --- a/slackevents/outer_events.go +++ b/slackevents/outer_events.go @@ -35,6 +35,7 @@ type EventsAPICallbackEvent struct { Token string `json:"token"` TeamID string `json:"team_id"` APIAppID string `json:"api_app_id"` + EnterpriseID string `json:"enterprise_id"` InnerEvent *json.RawMessage `json:"event"` AuthedUsers []string `json:"authed_users"` AuthedTeams []string `json:"authed_teams"` diff --git a/slackevents/parsers.go b/slackevents/parsers.go index 23ba2b85b..96ba5681b 100644 --- a/slackevents/parsers.go +++ b/slackevents/parsers.go @@ -102,7 +102,7 @@ func parseInnerEvent(e *EventsAPICallbackEvent) (EventsAPIEvent, error) { e.TeamID, "unmarshalling_error", e.APIAppID, - "", + e.EnterpriseID, &slack.UnmarshallingErrorEvent{ErrorObj: err}, EventsAPIInnerEvent{}, }, err @@ -114,7 +114,7 @@ func parseInnerEvent(e *EventsAPICallbackEvent) (EventsAPIEvent, error) { e.TeamID, iE.Type, e.APIAppID, - "", + e.EnterpriseID, nil, EventsAPIInnerEvent{}, }, fmt.Errorf("Inner Event does not exist! %s", iE.Type) @@ -128,7 +128,7 @@ func parseInnerEvent(e *EventsAPICallbackEvent) (EventsAPIEvent, error) { e.TeamID, "unmarshalling_error", e.APIAppID, - "", + e.EnterpriseID, &slack.UnmarshallingErrorEvent{ErrorObj: err}, EventsAPIInnerEvent{}, }, err @@ -138,7 +138,7 @@ func parseInnerEvent(e *EventsAPICallbackEvent) (EventsAPIEvent, error) { e.TeamID, e.Type, e.APIAppID, - "", + e.EnterpriseID, e, EventsAPIInnerEvent{iE.Type, recvEvent}, }, nil diff --git a/slacktest/handlers_test.go b/slacktest/handlers_test.go index 6678e7684..dec4c99be 100644 --- a/slacktest/handlers_test.go +++ b/slacktest/handlers_test.go @@ -37,7 +37,7 @@ func TestServerCreateConversationHandler(t *testing.T) { go s.Start() client := slack.New("ABCDEFG", slack.OptionAPIURL(s.GetAPIURL())) - conversation, err := client.CreateConversation("test", false) + conversation, err := client.CreateConversation(slack.CreateConversationParams{ChannelName: "test"}) assert.NoError(t, err) assert.Equal(t, "C0EAQDV4Z", conversation.ID) assert.Equal(t, "U023BECGF", conversation.Creator)