diff --git a/.changeset/apps-user-connection-request-auth-bulk-update.md b/.changeset/apps-user-connection-request-auth-bulk-update.md new file mode 100644 index 000000000..bef0b2362 --- /dev/null +++ b/.changeset/apps-user-connection-request-auth-bulk-update.md @@ -0,0 +1,5 @@ +--- +"@slack/web-api": minor +--- + +feat: add support for apps.user.connection.requestAuth and apps.user.connection.bulkUpdate diff --git a/packages/web-api/src/methods.ts b/packages/web-api/src/methods.ts index 549ecdd81..d684ea1d4 100644 --- a/packages/web-api/src/methods.ts +++ b/packages/web-api/src/methods.ts @@ -118,6 +118,8 @@ import type { AppsManifestUpdateArguments, AppsManifestValidateArguments, AppsUninstallArguments, + AppsUserConnectionBulkUpdateArguments, + AppsUserConnectionRequestAuthArguments, AppsUserConnectionUpdateArguments, AssistantThreadsSetStatusArguments, AssistantThreadsSetSuggestedPromptsArguments, @@ -400,6 +402,8 @@ import type { AppsManifestUpdateResponse, AppsManifestValidateResponse, AppsUninstallResponse, + AppsUserConnectionBulkUpdateResponse, + AppsUserConnectionRequestAuthResponse, AppsUserConnectionUpdateResponse, AssistantThreadsSetStatusResponse, AssistantThreadsSetSuggestedPromptsResponse, @@ -1589,6 +1593,22 @@ export abstract class Methods extends EventEmitter { uninstall: bindApiCall(this, 'apps.uninstall'), user: { connection: { + /** + * @description Updates the connection status between an app and many users in one call, for backfilling an existing user base. + * @see {@link https://docs.slack.dev/reference/methods/apps.user.connection.bulkUpdate `apps.user.connection.bulkUpdate` API reference}. + */ + bulkUpdate: bindApiCall( + this, + 'apps.user.connection.bulkUpdate', + ), + /** + * @description Opens or updates a standardized authentication modal for the calling app. + * @see {@link https://docs.slack.dev/reference/methods/apps.user.connection.requestAuth `apps.user.connection.requestAuth` API reference}. + */ + requestAuth: bindApiCall( + this, + 'apps.user.connection.requestAuth', + ), /** * @description Updates the connection status between a user and an app. * @see {@link https://docs.slack.dev/reference/methods/apps.user.connection.update `apps.user.connection.update` API reference}. diff --git a/packages/web-api/src/types/request/apps.ts b/packages/web-api/src/types/request/apps.ts index 7c0e063e6..efe24eb46 100644 --- a/packages/web-api/src/types/request/apps.ts +++ b/packages/web-api/src/types/request/apps.ts @@ -1,3 +1,5 @@ +import type { Overflow, PlainTextElement } from '@slack/types'; + import type { OptionalArgument } from '../helpers'; import type { AppID, CursorPaginationEnabled, OAuthCredentials, TokenOverridable } from './common'; @@ -32,6 +34,77 @@ export interface AppsManifestValidateArguments extends Partial, TokenOver manifest: Manifest; } +interface UserConnectionConnectedUserIDs { + /** + * @description IDs of users connected to the app. + * One of `connected_user_ids` or `disconnected_user_ids` is required; combined they may not exceed 1000. + */ + connected_user_ids: string[]; + disconnected_user_ids?: string[]; +} + +interface UserConnectionDisconnectedUserIDs { + connected_user_ids?: string[]; + /** + * @description IDs of users not connected to the app. + * One of `connected_user_ids` or `disconnected_user_ids` is required; combined they may not exceed 1000. + */ + disconnected_user_ids: string[]; +} + +// https://docs.slack.dev/reference/methods/apps.user.connection.bulkUpdate +export type AppsUserConnectionBulkUpdateArguments = TokenOverridable & + (UserConnectionConnectedUserIDs | UserConnectionDisconnectedUserIDs); + +export interface UserConnectionPrimaryProvider { + /** + * @description Unique identifier for this provider within the app. Included in interaction payloads and used to + * derive the connect button `action_id` (`auth_provider_connect_`). + */ + value: string; + /** @description Human-readable name of the provider. */ + name: PlainTextElement; + /** + * @description Whether this provider is currently connected or disconnected. Slack records this state for the user, + * so an omitted value would be indistinguishable from an explicit `false` and silently mark the user disconnected. + */ + is_connected: boolean; + /** @description Brief description of what this connection enables. */ + description?: PlainTextElement; + /** @description URL for the provider icon image. Must be HTTPs format. Falls back to the app's avatar when absent. */ + icon_url?: string; + /** @description OAuth redirect URL opened when the user clicks **Connect**. Must be HTTPs format. */ + auth_url?: string; + /** @description URL opened when the user clicks **Disconnect**. Must be HTTPs format on a registered auth host. */ + disconnect_url?: string; + /** @description Display name of the connected account, shown when `is_connected` is `true`. */ + connected_identity?: PlainTextElement; + /** @description Additional context about the connection (e.g., org or workspace name). */ + connected_context?: PlainTextElement; + /** @description Overflow menu shown when connected (e.g., Disconnect, Re-authenticate). */ + accessory?: Overflow; +} + +interface UserConnectionTriggerID { + /** @description The `trigger_id` from the `user_connection` interactive callback. Opens a new modal. */ + trigger_id: string; +} + +interface UserConnectionUserID { + /** + * @description The user whose auth modal to update. Required when `trigger_id` is omitted, and ignored when it is + * present. + */ + user_id: string; +} + +// https://docs.slack.dev/reference/methods/apps.user.connection.requestAuth +export type AppsUserConnectionRequestAuthArguments = TokenOverridable & + (UserConnectionTriggerID | UserConnectionUserID) & { + /** @description The connection to render. Its `icon_url` defaults to the app's own avatar when omitted. */ + primary_provider: UserConnectionPrimaryProvider; + }; + // https://docs.slack.dev/reference/methods/apps.user.connection.update export interface AppsUserConnectionUpdateArguments extends TokenOverridable { /** @description The identifier for the user receiving the status update. */ diff --git a/packages/web-api/src/types/request/index.ts b/packages/web-api/src/types/request/index.ts index b82e85534..17f22e25e 100644 --- a/packages/web-api/src/types/request/index.ts +++ b/packages/web-api/src/types/request/index.ts @@ -141,7 +141,10 @@ export type { AppsManifestUpdateArguments, AppsManifestValidateArguments, AppsUninstallArguments, + AppsUserConnectionBulkUpdateArguments, + AppsUserConnectionRequestAuthArguments, AppsUserConnectionUpdateArguments, + UserConnectionPrimaryProvider, } from './apps'; export type { AssistantThreadsSetStatusArguments, diff --git a/packages/web-api/src/types/response/AppsUserConnectionBulkUpdateResponse.ts b/packages/web-api/src/types/response/AppsUserConnectionBulkUpdateResponse.ts new file mode 100644 index 000000000..19c44490e --- /dev/null +++ b/packages/web-api/src/types/response/AppsUserConnectionBulkUpdateResponse.ts @@ -0,0 +1,29 @@ +///////////////////////////////////////////////////////////////////////////////////////// +// // +// !!! DO NOT EDIT THIS FILE !!! // +// // +// This file is auto-generated by scripts/generate-web-api-types.sh in the repository. // +// Please refer to the script code to learn how to update the source data. // +// // +///////////////////////////////////////////////////////////////////////////////////////// + +import type { WebAPICallResult } from '../../WebClient'; +export type AppsUserConnectionBulkUpdateResponse = WebAPICallResult & { + changed_user_ids?: string[]; + error?: string; + failures?: Failure[]; + needed?: string; + ok?: boolean; + provided?: string; + response_metadata?: ResponseMetadata; + warning?: string; +}; + +export interface Failure { + error?: string; + user_id?: string; +} + +export interface ResponseMetadata { + messages?: string[]; +} diff --git a/packages/web-api/src/types/response/AppsUserConnectionRequestAuthResponse.ts b/packages/web-api/src/types/response/AppsUserConnectionRequestAuthResponse.ts new file mode 100644 index 000000000..e09e4899e --- /dev/null +++ b/packages/web-api/src/types/response/AppsUserConnectionRequestAuthResponse.ts @@ -0,0 +1,23 @@ +///////////////////////////////////////////////////////////////////////////////////////// +// // +// !!! DO NOT EDIT THIS FILE !!! // +// // +// This file is auto-generated by scripts/generate-web-api-types.sh in the repository. // +// Please refer to the script code to learn how to update the source data. // +// // +///////////////////////////////////////////////////////////////////////////////////////// + +import type { WebAPICallResult } from '../../WebClient'; +export type AppsUserConnectionRequestAuthResponse = WebAPICallResult & { + error?: string; + needed?: string; + ok?: boolean; + provided?: string; + response_metadata?: ResponseMetadata; + warning?: string; +}; + +export interface ResponseMetadata { + messages?: string[]; + warnings?: string[]; +} diff --git a/packages/web-api/src/types/response/index.ts b/packages/web-api/src/types/response/index.ts index 4413329a5..7eea1dc64 100644 --- a/packages/web-api/src/types/response/index.ts +++ b/packages/web-api/src/types/response/index.ts @@ -128,6 +128,8 @@ export { AppsPermissionsScopesListResponse } from './AppsPermissionsScopesListRe export { AppsPermissionsUsersListResponse } from './AppsPermissionsUsersListResponse'; export { AppsPermissionsUsersRequestResponse } from './AppsPermissionsUsersRequestResponse'; export { AppsUninstallResponse } from './AppsUninstallResponse'; +export { AppsUserConnectionBulkUpdateResponse } from './AppsUserConnectionBulkUpdateResponse'; +export { AppsUserConnectionRequestAuthResponse } from './AppsUserConnectionRequestAuthResponse'; export { AppsUserConnectionUpdateResponse } from './AppsUserConnectionUpdateResponse'; export { AssistantThreadsSetStatusResponse } from './AssistantThreadsSetStatusResponse'; export { AssistantThreadsSetSuggestedPromptsResponse } from './AssistantThreadsSetSuggestedPromptsResponse'; diff --git a/packages/web-api/test/types/methods/apps.test-d.ts b/packages/web-api/test/types/methods/apps.test-d.ts index 3260a3c61..b1c9b002f 100644 --- a/packages/web-api/test/types/methods/apps.test-d.ts +++ b/packages/web-api/test/types/methods/apps.test-d.ts @@ -144,6 +144,92 @@ expectAssignable>([ }, ]); +// apps.user.connection.bulkUpdate +// -- sad path +expectError(web.apps.user.connection.bulkUpdate()); // lacking argument +expectError(web.apps.user.connection.bulkUpdate({})); // missing both connected_user_ids and disconnected_user_ids +// -- happy path +expectAssignable>([ + { + connected_user_ids: ['U1234'], + }, +]); +expectAssignable>([ + { + disconnected_user_ids: ['U5678'], + }, +]); +expectAssignable>([ + { + connected_user_ids: ['U1234'], + disconnected_user_ids: ['U5678'], + }, +]); + +// apps.user.connection.requestAuth +// -- sad path +expectError(web.apps.user.connection.requestAuth()); // lacking argument +expectError(web.apps.user.connection.requestAuth({})); // empty argument +expectError( + web.apps.user.connection.requestAuth({ + primary_provider: { value: 'github', name: { type: 'plain_text', text: 'GitHub' }, is_connected: false }, // missing trigger_id or user_id + }), +); +expectError( + web.apps.user.connection.requestAuth({ + user_id: 'U1234', // missing primary_provider + }), +); +expectError( + web.apps.user.connection.requestAuth({ + user_id: 'U1234', + primary_provider: { name: { type: 'plain_text', text: 'GitHub' }, is_connected: false }, // missing value + }), +); +expectError( + web.apps.user.connection.requestAuth({ + user_id: 'U1234', + primary_provider: { value: 'github', is_connected: false }, // missing name + }), +); +expectError( + web.apps.user.connection.requestAuth({ + user_id: 'U1234', + primary_provider: { value: 'github', name: { type: 'plain_text', text: 'GitHub' } }, // missing is_connected + }), +); +// -- happy path +expectAssignable>([ + { + trigger_id: '1234567890.1234567890.abcdef0123456789', + primary_provider: { + value: 'github', + name: { type: 'plain_text', text: 'GitHub' }, + is_connected: true, + description: { type: 'plain_text', text: 'Link pull requests to conversations' }, + icon_url: 'https://example.com/icon.png', + auth_url: 'https://example.com/oauth', + disconnect_url: 'https://example.com/disconnect', + connected_identity: { type: 'plain_text', text: 'octocat' }, + connected_context: { type: 'plain_text', text: 'Acme Inc.' }, + accessory: { + type: 'overflow', + action_id: 'provider-menu', + options: [ + { text: { type: 'plain_text', text: 'Switch accounts' }, value: 'switch' }, + { text: { type: 'plain_text', text: 'Disconnect' }, value: 'disconnect' }, + ], + }, + }, + }, +]); +expectAssignable>([ + { + user_id: 'U1234', + primary_provider: { value: 'github', name: { type: 'plain_text', text: 'GitHub' }, is_connected: false }, + }, +]); + // apps.user.connection.update // -- sad path expectError(web.apps.user.connection.update()); // lacking argument