From a2c0fe5670919a5e1a54a5e8f0746fa21fd5ec9b Mon Sep 17 00:00:00 2001 From: Fil Maj Date: Wed, 12 Jun 2024 14:13:27 -0400 Subject: [PATCH] web-api: public canvas APIs (#1813) --- packages/web-api/src/methods.ts | 58 ++++++ packages/web-api/src/types/request/auth.ts | 2 +- packages/web-api/src/types/request/canvas.ts | 91 +++++++++ packages/web-api/src/types/request/index.ts | 1 + .../response/CanvasesAccessDeleteResponse.ts | 21 +++ .../response/CanvasesAccessSetResponse.ts | 21 +++ .../types/response/CanvasesCreateResponse.ts | 22 +++ .../types/response/CanvasesDeleteResponse.ts | 19 ++ .../types/response/CanvasesEditResponse.ts | 20 ++ .../CanvasesSectionsLookupResponse.ts | 24 +++ .../ConversationsCanvasesCreateResponse.ts | 22 +++ packages/web-api/src/types/response/index.ts | 7 + .../test/types/methods/canvas.test-d.ts | 172 ++++++++++++++++++ 13 files changed, 479 insertions(+), 1 deletion(-) create mode 100644 packages/web-api/src/types/request/canvas.ts create mode 100644 packages/web-api/src/types/response/CanvasesAccessDeleteResponse.ts create mode 100644 packages/web-api/src/types/response/CanvasesAccessSetResponse.ts create mode 100644 packages/web-api/src/types/response/CanvasesCreateResponse.ts create mode 100644 packages/web-api/src/types/response/CanvasesDeleteResponse.ts create mode 100644 packages/web-api/src/types/response/CanvasesEditResponse.ts create mode 100644 packages/web-api/src/types/response/CanvasesSectionsLookupResponse.ts create mode 100644 packages/web-api/src/types/response/ConversationsCanvasesCreateResponse.ts create mode 100644 packages/web-api/test/types/methods/canvas.test-d.ts diff --git a/packages/web-api/src/methods.ts b/packages/web-api/src/methods.ts index b6f176142..2f6362574 100644 --- a/packages/web-api/src/methods.ts +++ b/packages/web-api/src/methods.ts @@ -99,6 +99,12 @@ import type { CallsUpdateResponse, CallsParticipantsAddResponse, CallsParticipantsRemoveResponse, + CanvasesAccessDeleteResponse, + CanvasesAccessSetResponse, + CanvasesCreateResponse, + CanvasesDeleteResponse, + CanvasesEditResponse, + CanvasesSectionsLookupResponse, ChatDeleteResponse, ChatDeleteScheduledMessageResponse, ChatGetPermalinkResponse, @@ -111,6 +117,7 @@ import type { ChatUpdateResponse, ConversationsAcceptSharedInviteResponse, ConversationsApproveSharedInviteResponse, + ConversationsCanvasesCreateResponse, ConversationsDeclineSharedInviteResponse, ConversationsInviteSharedResponse, ConversationsListConnectInvitesResponse, @@ -325,6 +332,7 @@ import type { ConversationsAcceptSharedInviteArguments, ConversationsApproveSharedInviteArguments, ConversationsArchiveArguments, + ConversationsCanvasesCreateArguments, ConversationsCloseArguments, ConversationsCreateArguments, ConversationsDeclineSharedInviteArguments, @@ -355,6 +363,12 @@ import type { ChatScheduledMessagesListArguments, ChatUnfurlArguments, ChatUpdateArguments, + CanvasesAccessDeleteArguments, + CanvasesAccessSetArguments, + CanvasesCreateArguments, + CanvasesDeleteArguments, + CanvasesEditArguments, + CanvasesSectionsLookupArguments, CallsAddArguments, CallsEndArguments, CallsInfoArguments, @@ -1337,6 +1351,43 @@ export abstract class Methods extends EventEmitter { }, }; + public readonly canvases = { + access: { + /** + * @description Remove access to a canvas for specified entities. + * @see {@link https://api.slack.com/methods/canvases.access.delete `canvases.access.delete` API reference}. + */ + delete: bindApiCall(this, 'canvases.access.delete'), + /** + * @description Sets the access level to a canvas for specified entities. + * @see {@link https://api.slack.com/methods/canvases.access.set `canvases.access.set` API reference}. + */ + set: bindApiCall(this, 'canvases.access.set'), + }, + /** + * @description Create Canvas for a user. + * @see {@link https://api.slack.com/methods/canvases.create `canvases.create` API reference}. + */ + create: bindApiCall(this, 'canvases.create'), + /** + * @description Deletes a canvas. + * @see {@link https://api.slack.com/methods/canvases.delete `canvases.delete` API reference}. + */ + delete: bindApiCall(this, 'canvases.delete'), + /** + * @description Update an existing canvas. + * @see {@link https://api.slack.com/methods/canvases.edit `canvases.edit` API reference}. + */ + edit: bindApiCall(this, 'canvases.edit'), + sections: { + /** + * @description Find sections matching the provided criteria. + * @see {@link https://api.slack.com/methods/canvases.sections.lookup `canvases.sections.lookup` API reference}. + */ + lookup: bindApiCall(this, 'canvases.sections.lookup'), + }, + }; + public readonly chat = { /** * @description Deletes a message. @@ -1423,6 +1474,13 @@ export abstract class Methods extends EventEmitter { * @see {@link https://api.slack.com/methods/conversations.archive `conversations.archive` API reference}. */ archive: bindApiCall(this, 'conversations.archive'), + canvases: { + /** + * @description Create a Channel Canvas for a channel. + * @see {@link https://api.slack.com/methods/conversations.canvases.create `conversations.canvases.create` API reference}. + */ + create: bindApiCall(this, 'conversations.canvases.create'), + }, /** * @description Closes a direct message or multi-person direct message. * @see {@link https://api.slack.com/methods/conversations.close `conversations.close` API reference}. diff --git a/packages/web-api/src/types/request/auth.ts b/packages/web-api/src/types/request/auth.ts index 04be8b86c..3da910fe3 100644 --- a/packages/web-api/src/types/request/auth.ts +++ b/packages/web-api/src/types/request/auth.ts @@ -1,5 +1,5 @@ import type { CursorPaginationEnabled, TokenOverridable } from './common'; -import { OptionalArgument } from '../helpers'; +import type { OptionalArgument } from '../helpers'; // https://api.slack.com/methods/auth.revoke export type AuthRevokeArguments = OptionalArgument) | (Partial & ContainsText); +type Operation = 'insert_after' | 'insert_before' | 'insert_at_start' | 'insert_at_end' | 'replace' | 'delete'; +interface BaseChange { + /** @description The operation to perform on the canvas. */ + operation?: Operation; + /** @description The section of the canvas to target the operation on. */ + section_id?: string; + /** @description Structure describing the type and contents. */ + document_content?: DocumentContent; +} +type ChangeWithSectionAndContent = Required & { + /** @description The operation to perform on the canvas. */ + operation: 'insert_after' | 'insert_before' +}; +type ChangeWithContent = Required> & { + /** @description The operation to perform on the canvas. */ + operation: 'insert_at_start' | 'insert_at_end'; +}; +type ChangeWithContentAndOptionalSection = BaseChange & Required> & { + /** @description The operation to perform on the canvas. */ + operation: 'replace'; +}; +type ChangeWithSection = Required> & { + /** @description The operation to perform on the canvas. */ + operation: 'delete'; +}; +type Change = ChangeWithSection | ChangeWithContent | ChangeWithSectionAndContent | ChangeWithContentAndOptionalSection; + +// https://api.slack.com/methods/canvases.access.delete +export interface CanvasesAccessDeleteArguments extends CanvasID, Partial, TokenOverridable, + Partial {} + +// https://api.slack.com/methods/canvases.access.set +export interface CanvasesAccessSetArguments extends CanvasID, Partial, TokenOverridable, Partial { + /** @description Desired level of access. */ + access_level: 'read' | 'write'; +} + +// https://api.slack.com/methods/canvases.create +export type CanvasesCreateArguments = OptionalArgument; + +// https://api.slack.com/methods/canvases.sections.lookup +export interface CanvasesSectionsLookupArguments extends CanvasID, TokenOverridable { + /** @description Filtering criteria. */ + criteria: Criteria; +} + +// https://api.slack.com/methods/canvases.delete +export interface CanvasesDeleteArguments extends CanvasID, TokenOverridable {} + +// https://api.slack.com/methods/canvases.edit +export interface CanvasesEditArguments extends CanvasID, TokenOverridable { + /** @description List of changes to apply to the canvas. */ + changes: [Change, ...Change[]]; +} + +// https://api.slack.com/methods/conversations.canvases.create +export interface ConversationsCanvasesCreateArguments extends TokenOverridable { + /** @description Channel ID of the channel to create a canvas in. */ + channel_id: string; + /** @description Structure describing the type and contents of the Canvas being created. */ + document_content?: DocumentContent; +} diff --git a/packages/web-api/src/types/request/index.ts b/packages/web-api/src/types/request/index.ts index 50e06cbf2..166e61177 100644 --- a/packages/web-api/src/types/request/index.ts +++ b/packages/web-api/src/types/request/index.ts @@ -19,6 +19,7 @@ export type { DndEndDndArguments, DndEndSnoozeArguments, DndInfoArguments, DndSe export type { DialogOpenArguments } from './dialog'; export type { ConversationsAcceptSharedInviteArguments, ConversationsApproveSharedInviteArguments, ConversationsArchiveArguments, ConversationsCloseArguments, ConversationsCreateArguments, ConversationsDeclineSharedInviteArguments, ConversationsHistoryArguments, ConversationsInfoArguments, ConversationsInviteArguments, ConversationsInviteSharedArguments, ConversationsJoinArguments, ConversationsKickArguments, ConversationsLeaveArguments, ConversationsListArguments, ConversationsListConnectInvitesArguments, ConversationsMarkArguments, ConversationsMembersArguments, ConversationsOpenArguments, ConversationsRenameArguments, ConversationsRepliesArguments, ConversationsSetPurposeArguments, ConversationsSetTopicArguments, ConversationsUnarchiveArguments } from './conversations'; export type { ChatDeleteArguments, ChatDeleteScheduledMessageArguments, ChatGetPermalinkArguments, ChatMeMessageArguments, ChatPostEphemeralArguments, ChatPostMessageArguments, ChatScheduleMessageArguments, ChatScheduledMessagesListArguments, ChatUnfurlArguments, ChatUpdateArguments } from './chat'; +export type { CanvasesAccessDeleteArguments, CanvasesAccessSetArguments, CanvasesCreateArguments, CanvasesDeleteArguments, CanvasesEditArguments, CanvasesSectionsLookupArguments, ConversationsCanvasesCreateArguments } from './canvas'; export type { CallsAddArguments, CallsEndArguments, CallsInfoArguments, CallsUpdateArguments, CallsParticipantsAddArguments, CallsParticipantsRemoveArguments } from './calls'; export type { BotsInfoArguments } from './bots'; export type { BookmarksAddArguments, BookmarksEditArguments, BookmarksListArguments, BookmarksRemoveArguments } from './bookmarks'; diff --git a/packages/web-api/src/types/response/CanvasesAccessDeleteResponse.ts b/packages/web-api/src/types/response/CanvasesAccessDeleteResponse.ts new file mode 100644 index 000000000..ef460b2bb --- /dev/null +++ b/packages/web-api/src/types/response/CanvasesAccessDeleteResponse.ts @@ -0,0 +1,21 @@ +/* eslint-disable */ +///////////////////////////////////////////////////////////////////////////////////////// +// // +// !!! 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 { WebAPICallResult } from '../../WebClient'; +export type CanvasesAccessDeleteResponse = WebAPICallResult & { + error?: string; + failed_to_update_channel_ids?: string[]; + failed_to_update_user_ids?: string[]; + ok?: boolean; + response_metadata?: ResponseMetadata; +}; + +export interface ResponseMetadata { +} diff --git a/packages/web-api/src/types/response/CanvasesAccessSetResponse.ts b/packages/web-api/src/types/response/CanvasesAccessSetResponse.ts new file mode 100644 index 000000000..2078da900 --- /dev/null +++ b/packages/web-api/src/types/response/CanvasesAccessSetResponse.ts @@ -0,0 +1,21 @@ +/* eslint-disable */ +///////////////////////////////////////////////////////////////////////////////////////// +// // +// !!! 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 { WebAPICallResult } from '../../WebClient'; +export type CanvasesAccessSetResponse = WebAPICallResult & { + error?: string; + failed_to_update_channel_ids?: string[]; + failed_to_update_user_ids?: string[]; + ok?: boolean; + response_metadata?: ResponseMetadata; +}; + +export interface ResponseMetadata { +} diff --git a/packages/web-api/src/types/response/CanvasesCreateResponse.ts b/packages/web-api/src/types/response/CanvasesCreateResponse.ts new file mode 100644 index 000000000..3cd420c56 --- /dev/null +++ b/packages/web-api/src/types/response/CanvasesCreateResponse.ts @@ -0,0 +1,22 @@ +/* eslint-disable */ +///////////////////////////////////////////////////////////////////////////////////////// +// // +// !!! 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 { WebAPICallResult } from '../../WebClient'; +export type CanvasesCreateResponse = WebAPICallResult & { + canvas_id?: string; + detail?: string; + error?: string; + ok?: boolean; + response_metadata?: ResponseMetadata; +}; + +export interface ResponseMetadata { + messages?: string[]; +} diff --git a/packages/web-api/src/types/response/CanvasesDeleteResponse.ts b/packages/web-api/src/types/response/CanvasesDeleteResponse.ts new file mode 100644 index 000000000..3274c978a --- /dev/null +++ b/packages/web-api/src/types/response/CanvasesDeleteResponse.ts @@ -0,0 +1,19 @@ +/* eslint-disable */ +///////////////////////////////////////////////////////////////////////////////////////// +// // +// !!! 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 { WebAPICallResult } from '../../WebClient'; +export type CanvasesDeleteResponse = WebAPICallResult & { + error?: string; + ok?: boolean; + response_metadata?: ResponseMetadata; +}; + +export interface ResponseMetadata { +} diff --git a/packages/web-api/src/types/response/CanvasesEditResponse.ts b/packages/web-api/src/types/response/CanvasesEditResponse.ts new file mode 100644 index 000000000..c046831ee --- /dev/null +++ b/packages/web-api/src/types/response/CanvasesEditResponse.ts @@ -0,0 +1,20 @@ +/* eslint-disable */ +///////////////////////////////////////////////////////////////////////////////////////// +// // +// !!! 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 { WebAPICallResult } from '../../WebClient'; +export type CanvasesEditResponse = WebAPICallResult & { + detail?: string; + error?: string; + ok?: boolean; + response_metadata?: ResponseMetadata; +}; + +export interface ResponseMetadata { +} diff --git a/packages/web-api/src/types/response/CanvasesSectionsLookupResponse.ts b/packages/web-api/src/types/response/CanvasesSectionsLookupResponse.ts new file mode 100644 index 000000000..6712e3b27 --- /dev/null +++ b/packages/web-api/src/types/response/CanvasesSectionsLookupResponse.ts @@ -0,0 +1,24 @@ +/* eslint-disable */ +///////////////////////////////////////////////////////////////////////////////////////// +// // +// !!! 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 { WebAPICallResult } from '../../WebClient'; +export type CanvasesSectionsLookupResponse = WebAPICallResult & { + error?: string; + ok?: boolean; + response_metadata?: ResponseMetadata; + sections?: Section[]; +}; + +export interface ResponseMetadata { +} + +export interface Section { + id?: string; +} diff --git a/packages/web-api/src/types/response/ConversationsCanvasesCreateResponse.ts b/packages/web-api/src/types/response/ConversationsCanvasesCreateResponse.ts new file mode 100644 index 000000000..64ba1111c --- /dev/null +++ b/packages/web-api/src/types/response/ConversationsCanvasesCreateResponse.ts @@ -0,0 +1,22 @@ +/* eslint-disable */ +///////////////////////////////////////////////////////////////////////////////////////// +// // +// !!! 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 { WebAPICallResult } from '../../WebClient'; +export type ConversationsCanvasesCreateResponse = WebAPICallResult & { + canvas_id?: string; + detail?: string; + error?: string; + ok?: boolean; + response_metadata?: ResponseMetadata; +}; + +export interface ResponseMetadata { + messages?: string[]; +} diff --git a/packages/web-api/src/types/response/index.ts b/packages/web-api/src/types/response/index.ts index 5035d08a9..2d3ced087 100644 --- a/packages/web-api/src/types/response/index.ts +++ b/packages/web-api/src/types/response/index.ts @@ -125,6 +125,12 @@ export { CallsInfoResponse } from './CallsInfoResponse'; export { CallsParticipantsAddResponse } from './CallsParticipantsAddResponse'; export { CallsParticipantsRemoveResponse } from './CallsParticipantsRemoveResponse'; export { CallsUpdateResponse } from './CallsUpdateResponse'; +export { CanvasesAccessDeleteResponse } from './CanvasesAccessDeleteResponse'; +export { CanvasesAccessSetResponse } from './CanvasesAccessSetResponse'; +export { CanvasesCreateResponse } from './CanvasesCreateResponse'; +export { CanvasesDeleteResponse } from './CanvasesDeleteResponse'; +export { CanvasesEditResponse } from './CanvasesEditResponse'; +export { CanvasesSectionsLookupResponse } from './CanvasesSectionsLookupResponse'; export { ChannelsArchiveResponse } from './ChannelsArchiveResponse'; export { ChannelsCreateResponse } from './ChannelsCreateResponse'; export { ChannelsHistoryResponse } from './ChannelsHistoryResponse'; @@ -153,6 +159,7 @@ export { ChatUpdateResponse } from './ChatUpdateResponse'; export { ConversationsAcceptSharedInviteResponse } from './ConversationsAcceptSharedInviteResponse'; export { ConversationsApproveSharedInviteResponse } from './ConversationsApproveSharedInviteResponse'; export { ConversationsArchiveResponse } from './ConversationsArchiveResponse'; +export { ConversationsCanvasesCreateResponse } from './ConversationsCanvasesCreateResponse'; export { ConversationsCloseResponse } from './ConversationsCloseResponse'; export { ConversationsCreateResponse } from './ConversationsCreateResponse'; export { ConversationsDeclineSharedInviteResponse } from './ConversationsDeclineSharedInviteResponse'; diff --git a/packages/web-api/test/types/methods/canvas.test-d.ts b/packages/web-api/test/types/methods/canvas.test-d.ts new file mode 100644 index 000000000..19c6740d5 --- /dev/null +++ b/packages/web-api/test/types/methods/canvas.test-d.ts @@ -0,0 +1,172 @@ +import { expectAssignable, expectError } from 'tsd'; +import { WebClient } from '../../../src/WebClient'; + +const web = new WebClient('TOKEN'); + +// canvases.access.delete +// -- sad path +expectError(web.canvases.access.delete()); // lacking argument +expectError(web.canvases.access.delete({})); // empty argument +// -- happy path +expectAssignable>([{ + canvas_id: 'F1234', +}]); + +// canvases.access.set +// -- sad path +expectError(web.canvases.access.set()); // lacking argument +expectError(web.canvases.access.set({})); // empty argument +expectError(web.canvases.access.set({ + canvas_id: 'F1234', // missing access_level +})); +expectError(web.canvases.access.set({ + access_level: 'read', // missing canvas_id +})); +// -- happy path +expectAssignable>([{ + canvas_id: 'F1234', + access_level: 'write', +}]); + +// canvases.create +// -- sad path +expectError(web.canvases.create({ + document_content: 'heyo', // invalid document_content +})); +expectError(web.canvases.create({ + document_content: { type: 'mrkdwn', markdown: 'hihi' }, // invalid document_content +})); +// -- happy path +expectAssignable>([]); // no arg ok +expectAssignable>([{}]); // all optional args + +// canvases.sections.lookup +// -- sad path +expectError(web.canvases.sections.lookup()); // lacking argument +expectError(web.canvases.sections.lookup({})); // empty argument +expectError(web.canvases.sections.lookup({ + canvas_id: 'F1234', // missing criteria +})); +expectError(web.canvases.sections.lookup({ + criteria: { contains_text: 'hi' }, // missing canvas_id +})); +expectError(web.canvases.sections.lookup({ + canvas_id: 'F1234', + criteria: {}, // empty criteria object +})); +expectError(web.canvases.sections.lookup({ + canvas_id: 'F1234', + criteria: { + section_types: [], // need at least one section type + }, +})); +// -- happy path +expectAssignable>([{ + canvas_id: 'F1234', + criteria: { contains_text: 'hi' }, +}]); +expectAssignable>([{ + canvas_id: 'F1234', + criteria: { section_types: ['any_header'] }, +}]); +expectAssignable>([{ + canvas_id: 'F1234', + criteria: { + section_types: ['any_header'], + contains_text: 'list', + }, +}]); + +// canvases.delete +// -- sad path +expectError(web.canvases.delete()); // lacking argument +expectError(web.canvases.delete({})); // empty argument +// -- happy path +expectAssignable>([{ + canvas_id: 'F1234', +}]); + +// canvases.edit +// -- sad path +expectError(web.canvases.edit()); // lacking argument +expectError(web.canvases.edit({})); // empty argument +expectError(web.canvases.edit({ + canvas_id: 'F1234', // missing changes +})); +expectError(web.canvases.edit({ + changes: [{ + operation: 'delete', + section_id: 'S1234', + }], // missing canvas_id +})); +expectError(web.canvases.edit({ + changes: [], // minimum 1 change + canvas_id: 'F1234', +})); +expectError(web.canvases.edit({ + changes: [{ + document_content: { type: 'markdown', markdown: 'hihi' }, // missing operation + }], + canvas_id: 'F1234', +})); +expectError(web.canvases.edit({ + changes: [{ + operation: 'insert_after', + document_content: { type: 'markdown', markdown: 'hihi' }, // missing section_id for op: insert_after + }], + canvas_id: 'F1234', +})); +expectError(web.canvases.edit({ + changes: [{ + operation: 'insert_before', + section_id: '1234', // missing document_content for op: insert_before + }], + canvas_id: 'F1234', +})); +expectError(web.canvases.edit({ + changes: [{ + operation: 'insert_at_end', // missing document_content for op: insert_at_end + }], + canvas_id: 'F1234', +})); +expectError(web.canvases.edit({ + changes: [{ + operation: 'replace', // missing document_content for op: replace + section_id: '1234', + }], + canvas_id: 'F1234', +})); +expectError(web.canvases.edit({ + changes: [{ + operation: 'delete', // missing section_id for op: delete + }], + canvas_id: 'F1234', +})); +// -- happy path +expectAssignable>([{ + canvas_id: 'F1234', + changes: [{ + operation: 'insert_after', + document_content: { type: 'markdown', markdown: 'hihi' }, + section_id: '1234', + }, { + operation: 'insert_at_start', + document_content: { type: 'markdown', markdown: 'hihi' }, + }, { + operation: 'replace', + document_content: { type: 'markdown', markdown: 'hihi' }, + section_id: '1234', + }, { + operation: 'delete', + section_id: '1234', + }], +}]); + +// conversations.canvases.create +// -- sad path +expectError(web.conversations.canvases.create()); // lacking argument +expectError(web.conversations.canvases.create({})); // empty argument +// -- happy path +expectAssignable>([{ + channel_id: 'C1234', +}]);