From 643e1c53ee33179ba69037cb6cf3b162b3be657a Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Mon, 21 Jul 2025 13:50:30 -0700 Subject: [PATCH 1/4] feat(web-api): add workflows.featured.{add|list|remove|set} methods --- packages/web-api/src/methods.ts | 33 +++++++++++++ packages/web-api/src/types/request/index.ts | 4 ++ .../web-api/src/types/request/workflows.ts | 48 +++++++++++++++++++ .../response/WorkflowsFeaturedAddResponse.ts | 3 ++ .../response/WorkflowsFeaturedListResponse.ts | 11 +++++ .../WorkflowsFeaturedRemoveResponse.ts | 3 ++ .../response/WorkflowsFeaturedSetResponse.ts | 3 ++ packages/web-api/src/types/response/index.ts | 4 ++ 8 files changed, 109 insertions(+) create mode 100644 packages/web-api/src/types/response/WorkflowsFeaturedAddResponse.ts create mode 100644 packages/web-api/src/types/response/WorkflowsFeaturedListResponse.ts create mode 100644 packages/web-api/src/types/response/WorkflowsFeaturedRemoveResponse.ts create mode 100644 packages/web-api/src/types/response/WorkflowsFeaturedSetResponse.ts diff --git a/packages/web-api/src/methods.ts b/packages/web-api/src/methods.ts index 3ebf5dbff..e3e6e06bf 100644 --- a/packages/web-api/src/methods.ts +++ b/packages/web-api/src/methods.ts @@ -250,6 +250,10 @@ import type { ViewsPublishArguments, ViewsPushArguments, ViewsUpdateArguments, + WorkflowsFeaturedAddArguments, + WorkflowsFeaturedListArguments, + WorkflowsFeaturedRemoveArguments, + WorkflowsFeaturedSetArguments, WorkflowsStepCompletedArguments, WorkflowsStepFailedArguments, WorkflowsUpdateStepArguments, @@ -503,6 +507,10 @@ import type { ViewsPublishResponse, ViewsPushResponse, ViewsUpdateResponse, + WorkflowsFeaturedAddResponse, + WorkflowsFeaturedListResponse, + WorkflowsFeaturedRemoveResponse, + WorkflowsFeaturedSetResponse, WorkflowsStepCompletedResponse, WorkflowsStepFailedResponse, WorkflowsUpdateStepResponse, @@ -2380,6 +2388,31 @@ export abstract class Methods extends EventEmitter { }; public readonly workflows = { + featured: { + /** + * @description Add featured workflows to a channel. + * @see {@link https://docs.slack.dev/reference/methods/workflows.featured.add `workflows.featured.add` API reference}. + */ + add: bindApiCall(this, 'workflows.featured.add'), + /** + * @description List the featured workflows for specified channels. + * @see {@link https://docs.slack.dev/reference/methods/workflows.featured.list `workflows.featured.list` API reference}. + */ + list: bindApiCall(this, 'workflows.featured.list'), + /** + * @description Remove featured workflows from a channel. + * @see {@link https://docs.slack.dev/reference/methods/workflows.featured.remove `workflows.featured.remove` API reference}. + */ + remove: bindApiCall( + this, + 'workflows.featured.remove', + ), + /** + * @description Set featured workflows for a channel. + * @see {@link https://docs.slack.dev/reference/methods/workflows.featured.set `workflows.featured.set` API reference}. + */ + set: bindApiCall(this, 'workflows.featured.set'), + }, /** * @description Indicate that an app's step in a workflow completed execution. * @deprecated Steps from Apps is deprecated. diff --git a/packages/web-api/src/types/request/index.ts b/packages/web-api/src/types/request/index.ts index 305055de8..0391c1607 100644 --- a/packages/web-api/src/types/request/index.ts +++ b/packages/web-api/src/types/request/index.ts @@ -317,6 +317,10 @@ export type { ViewsUpdateArguments, } from './views'; export type { + WorkflowsFeaturedAddArguments, + WorkflowsFeaturedListArguments, + WorkflowsFeaturedRemoveArguments, + WorkflowsFeaturedSetArguments, WorkflowsStepCompletedArguments, WorkflowsStepFailedArguments, WorkflowsUpdateStepArguments, diff --git a/packages/web-api/src/types/request/workflows.ts b/packages/web-api/src/types/request/workflows.ts index 64006935f..4173caf94 100644 --- a/packages/web-api/src/types/request/workflows.ts +++ b/packages/web-api/src/types/request/workflows.ts @@ -1,5 +1,53 @@ import type { TokenOverridable } from './common'; +// https://docs.slack.dev/reference/methods/workflows.featured.add +export interface WorkflowsFeaturedAddArguments extends TokenOverridable { + /** + * @description Channel to add featured workflow in. + */ + channel_id: string; + /** + * @description Comma-separated array of trigger IDs to add; max 15 + * @example ["Ft012345", "Ft012346"] + */ + trigger_ids: string[]; +} + +// https://docs.slack.dev/reference/methods/workflows.featured.list +export interface WorkflowsFeaturedListArguments extends TokenOverridable { + /** + * @description Comma-separated array of channel IDs to list featured workflows for. + * @example ["C012345678", "C987654321"] + */ + channel_ids: string[]; +} + +// https://docs.slack.dev/reference/methods/workflows.featured.remove +export interface WorkflowsFeaturedRemoveArguments extends TokenOverridable { + /** + * @description Channel to remove featured workflow from. + */ + channel_id: string; + /** + * @description Comma-separated array of trigger IDs to remove; max 15 + * @example ["Ft012345", "Ft012346"] + */ + trigger_ids: string[]; +} + +// https://docs.slack.dev/reference/methods/workflows.featured.set +export interface WorkflowsFeaturedSetArguments extends TokenOverridable { + /** + * @description Channel to set featured workflow in. + */ + channel_id: string; + /** + * @description Comma-separated array of trigger IDs that will replace any existing featured workflows in the channel; max 15 + * @example ["Ft012345", "Ft012346"] + */ + trigger_ids: string[]; +} + // TODO: breaking change: to be removed after Sep 12 2024 // https://docs.slack.dev/changelog/2023-08-workflow-steps-from-apps-step-back diff --git a/packages/web-api/src/types/response/WorkflowsFeaturedAddResponse.ts b/packages/web-api/src/types/response/WorkflowsFeaturedAddResponse.ts new file mode 100644 index 000000000..aa71dc15b --- /dev/null +++ b/packages/web-api/src/types/response/WorkflowsFeaturedAddResponse.ts @@ -0,0 +1,3 @@ +import type { WebAPICallResult } from '../../WebClient'; + +export type WorkflowsFeaturedAddResponse = WebAPICallResult & {}; diff --git a/packages/web-api/src/types/response/WorkflowsFeaturedListResponse.ts b/packages/web-api/src/types/response/WorkflowsFeaturedListResponse.ts new file mode 100644 index 000000000..e5de2cc91 --- /dev/null +++ b/packages/web-api/src/types/response/WorkflowsFeaturedListResponse.ts @@ -0,0 +1,11 @@ +import type { WebAPICallResult } from '../../WebClient'; + +export type WorkflowsFeaturedListResponse = WebAPICallResult & { + featured_workflows: { + channel_id: string; + triggers: { + id: string; + title: string; + }[]; + }[]; +}; diff --git a/packages/web-api/src/types/response/WorkflowsFeaturedRemoveResponse.ts b/packages/web-api/src/types/response/WorkflowsFeaturedRemoveResponse.ts new file mode 100644 index 000000000..45004b950 --- /dev/null +++ b/packages/web-api/src/types/response/WorkflowsFeaturedRemoveResponse.ts @@ -0,0 +1,3 @@ +import type { WebAPICallResult } from '../../WebClient'; + +export type WorkflowsFeaturedRemoveResponse = WebAPICallResult & {}; diff --git a/packages/web-api/src/types/response/WorkflowsFeaturedSetResponse.ts b/packages/web-api/src/types/response/WorkflowsFeaturedSetResponse.ts new file mode 100644 index 000000000..04903a4cc --- /dev/null +++ b/packages/web-api/src/types/response/WorkflowsFeaturedSetResponse.ts @@ -0,0 +1,3 @@ +import type { WebAPICallResult } from '../../WebClient'; + +export type WorkflowsFeaturedSetResponse = WebAPICallResult & {}; diff --git a/packages/web-api/src/types/response/index.ts b/packages/web-api/src/types/response/index.ts index 1f78f13fe..c9186bd75 100644 --- a/packages/web-api/src/types/response/index.ts +++ b/packages/web-api/src/types/response/index.ts @@ -308,6 +308,10 @@ export { ViewsOpenResponse } from './ViewsOpenResponse'; export { ViewsPublishResponse } from './ViewsPublishResponse'; export { ViewsPushResponse } from './ViewsPushResponse'; export { ViewsUpdateResponse } from './ViewsUpdateResponse'; +export { WorkflowsFeaturedAddResponse } from './WorkflowsFeaturedAddResponse'; +export { WorkflowsFeaturedListResponse } from './WorkflowsFeaturedListResponse'; +export { WorkflowsFeaturedRemoveResponse } from './WorkflowsFeaturedRemoveResponse'; +export { WorkflowsFeaturedSetResponse } from './WorkflowsFeaturedSetResponse'; export { WorkflowsStepCompletedResponse } from './WorkflowsStepCompletedResponse'; export { WorkflowsStepFailedResponse } from './WorkflowsStepFailedResponse'; export { WorkflowsUpdateStepResponse } from './WorkflowsUpdateStepResponse'; From 5ade4c45dc2f2b285244438cb2166201db3c5475 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Mon, 21 Jul 2025 13:51:02 -0700 Subject: [PATCH 2/4] fix: avoid warnings of deprecated methods for new workflow methods --- packages/web-api/src/WebClient.spec.ts | 18 ++++++++++++++++++ packages/web-api/src/WebClient.ts | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/web-api/src/WebClient.spec.ts b/packages/web-api/src/WebClient.spec.ts index e4d4575dc..eeb46e9a1 100644 --- a/packages/web-api/src/WebClient.spec.ts +++ b/packages/web-api/src/WebClient.spec.ts @@ -326,6 +326,24 @@ describe('WebClient', () => { assert.fail('Should have logged a warning and info when files.upload is used'); } }); + + it('warns when user is accessing a deprecated method', async () => { + const client = new WebClient(token, { logLevel: LogLevel.INFO, logger }); + await client.apiCall('workflows.stepCompleted', {}); + + let warnedAboutDeprecatedMethod = false; + for (const call of (logger.warn as sinon.SinonStub).getCalls()) { + if ( + call.args[0] === + 'workflows.stepCompleted is deprecated. Please check on https://docs.slack.dev/reference/methods for an alternative.' + ) { + warnedAboutDeprecatedMethod = true; + } + } + if (!warnedAboutDeprecatedMethod) { + assert.fail('Should have logged a warning when a deprecated method is used'); + } + }); }); describe('with OAuth scopes in the response headers', () => { diff --git a/packages/web-api/src/WebClient.ts b/packages/web-api/src/WebClient.ts index c5f83b9dc..36eecc0ac 100644 --- a/packages/web-api/src/WebClient.ts +++ b/packages/web-api/src/WebClient.ts @@ -964,7 +964,7 @@ function parseRetryHeaders(response: AxiosResponse): number | undefined { * @param logger instance of web clients logger */ function warnDeprecations(method: string, logger: Logger): void { - const deprecatedMethods = ['workflows.']; + const deprecatedMethods = ['workflows.stepCompleted', 'workflows.stepFailed', 'workflows.updateStep']; const isDeprecated = deprecatedMethods.some((depMethod) => { const re = new RegExp(`^${depMethod}`); From efbd881e9da360e3e1cd941da7f41cd29c27e696 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Mon, 21 Jul 2025 20:43:42 -0700 Subject: [PATCH 3/4] test: confirm unexpected arguments cause type errors Co-authored-by: Elaine Vegeris --- .../test/types/methods/workflows.test-d.ts | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 packages/web-api/test/types/methods/workflows.test-d.ts diff --git a/packages/web-api/test/types/methods/workflows.test-d.ts b/packages/web-api/test/types/methods/workflows.test-d.ts new file mode 100644 index 000000000..b96e7f4cc --- /dev/null +++ b/packages/web-api/test/types/methods/workflows.test-d.ts @@ -0,0 +1,108 @@ +import { expectAssignable, expectError } from 'tsd'; + +import { WebClient } from '../../../src/WebClient'; + +const web = new WebClient('TOKEN'); + +// workflows.featured.add +// -- sad path +expectError(web.workflows.featured.add()); // lacking argument +expectError(web.workflows.featured.add({})); // empty argument +expectError( + web.workflows.featured.add({ + channel_id: 'C1234', // missing trigger_ids + }), +); +expectError( + web.workflows.featured.add({ + trigger_ids: [], // missing channel_id + }), +); +expectError( + web.workflows.featured.add({ + channel_id: 'C1234', + trigger_ids: 'Ft1234', // not an array + }), +); +// -- happy path +expectAssignable>([ + { + channel_id: 'C1234', + trigger_ids: [], + }, +]); +expectAssignable>([ + { + channel_id: 'C1234', + trigger_ids: ['Ft1234', 'Ft0001'], + }, +]); + +// workflows.featured.list +// -- sad path +expectError(web.workflows.featured.list()); // lacking argument +expectError(web.workflows.featured.list({})); // empty argument +expectError( + web.workflows.featured.list({ + channel_ids: 'C1234', // not an array + }), +); +// -- happy path +expectAssignable>([ + { + channel_ids: [], + }, +]); +expectAssignable>([ + { + channel_ids: ['C1234', 'C0001'], + }, +]); + +// workflows.featured.remove +// -- sad path +expectError(web.workflows.featured.remove()); // lacking argument +expectError(web.workflows.featured.remove({})); // empty argument +expectError( + web.workflows.featured.remove({ + channel_id: 'C1234', // missing trigger_ids + }), +); +expectError( + web.workflows.featured.remove({ + trigger_ids: [], // missing channel_id + }), +); +// -- happy path +expectAssignable>([ + { + channel_id: 'C1234', + trigger_ids: [], + }, +]); +expectAssignable>([ + { + channel_id: 'C1234', + trigger_ids: ['Ft1234', 'Ft0001'], + }, +]); + +// workflows.featured.set +// -- sad path +expectError(web.workflows.featured.set()); // lacking argument +expectError(web.workflows.featured.set({})); // empty argument +expectError(web.workflows.featured.set({ + channel_id: 'C1234', // missing trigger_ids +})); +expectError(web.workflows.featured.set({ + trigger_ids: [], // missing channel_id +})); +// -- happy path +expectAssignable>([{ + channel_id: 'C1234', + trigger_ids: [], +}]); +expectAssignable>([{ + channel_id: 'C1234', + trigger_ids: ['Ft1234', 'Ft0001'], +}]); From c8138cac677e27ecd88704b3259d0b39eab50199 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Mon, 21 Jul 2025 20:46:51 -0700 Subject: [PATCH 4/4] style: run the linter before making a commit --- .../test/types/methods/workflows.test-d.ts | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/packages/web-api/test/types/methods/workflows.test-d.ts b/packages/web-api/test/types/methods/workflows.test-d.ts index b96e7f4cc..b897c8b23 100644 --- a/packages/web-api/test/types/methods/workflows.test-d.ts +++ b/packages/web-api/test/types/methods/workflows.test-d.ts @@ -91,18 +91,26 @@ expectAssignable>([ // -- sad path expectError(web.workflows.featured.set()); // lacking argument expectError(web.workflows.featured.set({})); // empty argument -expectError(web.workflows.featured.set({ - channel_id: 'C1234', // missing trigger_ids -})); -expectError(web.workflows.featured.set({ - trigger_ids: [], // missing channel_id -})); +expectError( + web.workflows.featured.set({ + channel_id: 'C1234', // missing trigger_ids + }), +); +expectError( + web.workflows.featured.set({ + trigger_ids: [], // missing channel_id + }), +); // -- happy path -expectAssignable>([{ - channel_id: 'C1234', - trigger_ids: [], -}]); -expectAssignable>([{ - channel_id: 'C1234', - trigger_ids: ['Ft1234', 'Ft0001'], -}]); +expectAssignable>([ + { + channel_id: 'C1234', + trigger_ids: [], + }, +]); +expectAssignable>([ + { + channel_id: 'C1234', + trigger_ids: ['Ft1234', 'Ft0001'], + }, +]);