diff --git a/libraries/botbuilder/etc/botbuilder.api.md b/libraries/botbuilder/etc/botbuilder.api.md index b328ae7e53..973aef33d8 100644 --- a/libraries/botbuilder/etc/botbuilder.api.md +++ b/libraries/botbuilder/etc/botbuilder.api.md @@ -12,6 +12,7 @@ import { AppCredentials } from 'botframework-connector'; import { AttachmentData } from 'botbuilder-core'; import { AuthenticationConfiguration } from 'botframework-connector'; import { BotAdapter } from 'botbuilder-core'; +import { BotConfigAuth } from 'botbuilder-core'; import { BotFrameworkAuthentication } from 'botframework-connector'; import { BotFrameworkClient } from 'botbuilder-core'; import { BotFrameworkSkill } from 'botbuilder-core'; @@ -20,6 +21,8 @@ import { ChannelAccount } from 'botbuilder-core'; import { ChannelInfo } from 'botbuilder-core'; import { ClaimsIdentity } from 'botframework-connector'; import { CloudAdapterBase } from 'botbuilder-core'; +import { ConfigResponse } from 'botbuilder-core'; +import { ConfigTaskResponse } from 'botbuilder-core'; import { ConnectorClient } from 'botframework-connector'; import { ConnectorClientOptions } from 'botframework-connector'; import { ConversationAccount } from 'botbuilder-core'; @@ -367,6 +370,8 @@ export class TeamsActivityHandler extends ActivityHandler { protected handleTeamsAnonymousAppBasedLinkQuery(_context: TurnContext, _query: AppBasedLinkQuery): Promise; protected handleTeamsAppBasedLinkQuery(_context: TurnContext, _query: AppBasedLinkQuery): Promise; protected handleTeamsCardActionInvoke(_context: TurnContext): Promise; + protected handleTeamsConfigFetch(_context: TurnContext, _configData: any): Promise>; + protected handleTeamsConfigSubmit(_context: TurnContext, _configData: any): Promise>; protected handleTeamsFileConsent(context: TurnContext, fileConsentCardResponse: FileConsentCardResponse): Promise; protected handleTeamsFileConsentAccept(_context: TurnContext, _fileConsentCardResponse: FileConsentCardResponse): Promise; protected handleTeamsFileConsentDecline(_context: TurnContext, _fileConsentCardResponse: FileConsentCardResponse): Promise; @@ -389,13 +394,13 @@ export class TeamsActivityHandler extends ActivityHandler { protected handleTeamsTaskModuleSubmit(_context: TurnContext, _taskModuleRequest: TaskModuleRequest): Promise; protected onInvokeActivity(context: TurnContext): Promise; protected onSignInInvoke(context: TurnContext): Promise; - protected onTeamsChannelCreated(context: any): Promise; + protected onTeamsChannelCreated(context: TurnContext): Promise; onTeamsChannelCreatedEvent(handler: (channelInfo: ChannelInfo, teamInfo: TeamInfo, context: TurnContext, next: () => Promise) => Promise): this; - protected onTeamsChannelDeleted(context: any): Promise; + protected onTeamsChannelDeleted(context: TurnContext): Promise; onTeamsChannelDeletedEvent(handler: (channelInfo: ChannelInfo, teamInfo: TeamInfo, context: TurnContext, next: () => Promise) => Promise): this; - protected onTeamsChannelRenamed(context: any): Promise; + protected onTeamsChannelRenamed(context: TurnContext): Promise; onTeamsChannelRenamedEvent(handler: (channelInfo: ChannelInfo, teamInfo: TeamInfo, context: TurnContext, next: () => Promise) => Promise): this; - protected onTeamsChannelRestored(context: any): Promise; + protected onTeamsChannelRestored(context: TurnContext): Promise; onTeamsChannelRestoredEvent(handler: (channelInfo: ChannelInfo, teamInfo: TeamInfo, context: TurnContext, next: () => Promise) => Promise): this; protected onTeamsMeetingEnd(context: TurnContext): Promise; onTeamsMeetingEndEvent(handler: (meeting: MeetingEndEventDetails, context: TurnContext, next: () => Promise) => Promise): this; @@ -413,17 +418,17 @@ export class TeamsActivityHandler extends ActivityHandler { onTeamsMessageUndeleteEvent(handler: (context: TurnContext, next: () => Promise) => Promise): this; protected onTeamsReadReceipt(context: TurnContext): Promise; onTeamsReadReceiptEvent(handler: (receiptInfo: ReadReceiptInfo, context: TurnContext, next: () => Promise) => Promise): this; - protected onTeamsTeamArchived(context: any): Promise; + protected onTeamsTeamArchived(context: TurnContext): Promise; onTeamsTeamArchivedEvent(handler: (teamInfo: TeamInfo, context: TurnContext, next: () => Promise) => Promise): this; - protected onTeamsTeamDeleted(context: any): Promise; + protected onTeamsTeamDeleted(context: TurnContext): Promise; onTeamsTeamDeletedEvent(handler: (teamInfo: TeamInfo, context: TurnContext, next: () => Promise) => Promise): this; - protected onTeamsTeamHardDeleted(context: any): Promise; + protected onTeamsTeamHardDeleted(context: TurnContext): Promise; onTeamsTeamHardDeletedEvent(handler: (teamInfo: TeamInfo, context: TurnContext, next: () => Promise) => Promise): this; - protected onTeamsTeamRenamed(context: any): Promise; + protected onTeamsTeamRenamed(context: TurnContext): Promise; onTeamsTeamRenamedEvent(handler: (teamInfo: TeamInfo, context: TurnContext, next: () => Promise) => Promise): this; - protected onTeamsTeamRestored(context: any): Promise; + protected onTeamsTeamRestored(context: TurnContext): Promise; onTeamsTeamRestoredEvent(handler: (teamInfo: TeamInfo, context: TurnContext, next: () => Promise) => Promise): this; - protected onTeamsTeamUnarchived(context: any): Promise; + protected onTeamsTeamUnarchived(context: TurnContext): Promise; onTeamsTeamUnarchivedEvent(handler: (teamInfo: TeamInfo, context: TurnContext, next: () => Promise) => Promise): this; } diff --git a/libraries/botbuilder/src/teamsActivityHandler.ts b/libraries/botbuilder/src/teamsActivityHandler.ts index c88c6c922b..47da1d3b32 100644 --- a/libraries/botbuilder/src/teamsActivityHandler.ts +++ b/libraries/botbuilder/src/teamsActivityHandler.ts @@ -9,12 +9,15 @@ import { ActivityHandler, AppBasedLinkQuery, + BotConfigAuth, ChannelInfo, Channels, + ConfigResponse, + ConfigTaskResponse, FileConsentCardResponse, InvokeResponse, - MeetingStartEventDetails, MeetingEndEventDetails, + MeetingStartEventDetails, MessagingExtensionAction, MessagingExtensionActionResponse, MessagingExtensionQuery, @@ -26,11 +29,11 @@ import { TabSubmit, TaskModuleRequest, TaskModuleResponse, - TeamsChannelData, - TeamsChannelAccount, TeamInfo, - TurnContext, + TeamsChannelAccount, + TeamsChannelData, tokenExchangeOperationName, + TurnContext, verifyStateOperationName, } from 'botbuilder-core'; import { ReadReceiptInfo } from 'botframework-connector'; @@ -86,6 +89,14 @@ export class TeamsActivityHandler extends ActivityHandler { return await this.handleTeamsCardActionInvoke(context); } else { switch (context.activity.name) { + case 'config/fetch': + return ActivityHandler.createInvokeResponse( + await this.handleTeamsConfigFetch(context, context.activity.value) + ); + case 'config/submit': + return ActivityHandler.createInvokeResponse( + await this.handleTeamsConfigSubmit(context, context.activity.value) + ); case 'fileConsent/invoke': return ActivityHandler.createInvokeResponse( await this.handleTeamsFileConsent(context, context.activity.value) @@ -169,7 +180,7 @@ export class TeamsActivityHandler extends ActivityHandler { return super.onInvokeActivity(context); } } - } catch (err) { + } catch (err: any) { if (err.message === 'NotImplemented') { return { status: 501 }; } else if (err.message === 'BadRequest') { @@ -193,6 +204,34 @@ export class TeamsActivityHandler extends ActivityHandler { throw new Error('NotImplemented'); } + /** + * Handles a config/fetch invoke activity. + * + * @param _context A context object for this turn. + * @param _configData The object representing the configuration. + * @returns A Config Response for the activity. + */ + protected async handleTeamsConfigFetch( + _context: TurnContext, + _configData: any + ): Promise> { + throw new Error('NotImplemented'); + } + + /** + * Handles a config/submit invoke activity. + * + * @param _context A context object for this turn. + * @param _configData The object representing the configuration. + * @returns A Config Response for the activity. + */ + protected async handleTeamsConfigSubmit( + _context: TurnContext, + _configData: any + ): Promise> { + throw new Error('NotImplemented'); + } + /** * Receives invoke activities with Activity name of 'fileConsent/invoke'. Handlers registered here run before * `handleTeamsFileConsentAccept` and `handleTeamsFileConsentDecline`. @@ -424,7 +463,7 @@ export class TeamsActivityHandler extends ActivityHandler { * @remarks * Used in creating a Search-based Message Extension. * @param _context A context object for this turn. - * @param _query he object representing the query. + * @param _query The object representing the query. * @returns The Messaging Extension Response for the query. */ protected async handleTeamsMessagingExtensionSelectItem( @@ -700,6 +739,9 @@ export class TeamsActivityHandler extends ActivityHandler { */ protected async onTeamsMembersAdded(context: TurnContext): Promise { if ('TeamsMembersAdded' in this.handlers && this.handlers['TeamsMembersAdded'].length > 0) { + if (!context.activity || !context.activity.membersAdded) { + throw new Error('OnTeamsMemberAdded: context.activity is undefined'); + } for (let i = 0; i < context.activity.membersAdded.length; i++) { const channelAccount = context.activity.membersAdded[i]; @@ -717,7 +759,7 @@ export class TeamsActivityHandler extends ActivityHandler { try { context.activity.membersAdded[i] = await TeamsInfo.getMember(context, channelAccount.id); - } catch (err) { + } catch (err: any) { const errCode: string = err.body && err.body.error && err.body.error.code; if (errCode === 'ConversationNotFound') { // unable to find the member added in ConversationUpdate Activity in the response from the getMember call @@ -767,7 +809,7 @@ export class TeamsActivityHandler extends ActivityHandler { * @param context A context object for this turn. * @returns A promise that represents the work queued. */ - protected async onTeamsChannelCreated(context): Promise { + protected async onTeamsChannelCreated(context: TurnContext): Promise { await this.handle(context, 'TeamsChannelCreated', this.defaultNextEvent(context)); } @@ -779,7 +821,7 @@ export class TeamsActivityHandler extends ActivityHandler { * @param context A context object for this turn. * @returns A promise that represents the work queued. */ - protected async onTeamsChannelDeleted(context): Promise { + protected async onTeamsChannelDeleted(context: TurnContext): Promise { await this.handle(context, 'TeamsChannelDeleted', this.defaultNextEvent(context)); } @@ -791,7 +833,7 @@ export class TeamsActivityHandler extends ActivityHandler { * @param context A context object for this turn. * @returns A promise that represents the work queued. */ - protected async onTeamsChannelRenamed(context): Promise { + protected async onTeamsChannelRenamed(context: TurnContext): Promise { await this.handle(context, 'TeamsChannelRenamed', this.defaultNextEvent(context)); } @@ -803,7 +845,7 @@ export class TeamsActivityHandler extends ActivityHandler { * @param context The context for this turn. * @returns A promise that represents the work queued. */ - protected async onTeamsTeamArchived(context): Promise { + protected async onTeamsTeamArchived(context: TurnContext): Promise { await this.handle(context, 'TeamsTeamArchived', this.defaultNextEvent(context)); } @@ -815,7 +857,7 @@ export class TeamsActivityHandler extends ActivityHandler { * @param context The context for this turn. * @returns A promise that represents the work queued. */ - protected async onTeamsTeamDeleted(context): Promise { + protected async onTeamsTeamDeleted(context: TurnContext): Promise { await this.handle(context, 'TeamsTeamDeleted', this.defaultNextEvent(context)); } @@ -827,7 +869,7 @@ export class TeamsActivityHandler extends ActivityHandler { * @param context The context for this turn. * @returns A promise that represents the work queued. */ - protected async onTeamsTeamHardDeleted(context): Promise { + protected async onTeamsTeamHardDeleted(context: TurnContext): Promise { await this.handle(context, 'TeamsTeamHardDeleted', this.defaultNextEvent(context)); } @@ -840,7 +882,7 @@ export class TeamsActivityHandler extends ActivityHandler { * @param context The context for this turn. * @returns A promise that represents the work queued. */ - protected async onTeamsChannelRestored(context): Promise { + protected async onTeamsChannelRestored(context: TurnContext): Promise { await this.handle(context, 'TeamsChannelRestored', this.defaultNextEvent(context)); } @@ -852,7 +894,7 @@ export class TeamsActivityHandler extends ActivityHandler { * @param context The context for this turn. * @returns A promise that represents the work queued. */ - protected async onTeamsTeamRenamed(context): Promise { + protected async onTeamsTeamRenamed(context: TurnContext): Promise { await this.handle(context, 'TeamsTeamRenamed', this.defaultNextEvent(context)); } @@ -864,7 +906,7 @@ export class TeamsActivityHandler extends ActivityHandler { * @param context The context for this turn. * @returns A promise that represents the work queued. */ - protected async onTeamsTeamRestored(context): Promise { + protected async onTeamsTeamRestored(context: TurnContext): Promise { await this.handle(context, 'TeamsTeamRestored', this.defaultNextEvent(context)); } @@ -876,7 +918,7 @@ export class TeamsActivityHandler extends ActivityHandler { * @param context The context for this turn. * @returns A promise that represents the work queued. */ - protected async onTeamsTeamUnarchived(context): Promise { + protected async onTeamsTeamUnarchived(context: TurnContext): Promise { await this.handle(context, 'TeamsTeamUnarchived', this.defaultNextEvent(context)); } diff --git a/libraries/botbuilder/tests/teamsActivityHandler.test.js b/libraries/botbuilder/tests/teamsActivityHandler.test.js index a6ddcff165..5db46953d0 100644 --- a/libraries/botbuilder/tests/teamsActivityHandler.test.js +++ b/libraries/botbuilder/tests/teamsActivityHandler.test.js @@ -724,6 +724,42 @@ describe('TeamsActivityHandler', function () { }); describe('should send a NotImplemented status code if', function () { + it('handleTeamsBotConfigFetch is not overridden', async function () { + const bot = new TeamsActivityHandler(); + + const adapter = new TestAdapter(async (context) => { + await bot.run(context); + }); + + const taskSubmitActivity = createInvokeActivity('config/fetch'); + await adapter + .send(taskSubmitActivity) + .assertReply((activity) => { + assert.strictEqual(activity.type, 'invokeResponse'); + assert.strictEqual(activity.value.status, 501); + assert.strictEqual(activity.value.body, undefined); + }) + .startTest(); + }); + + it('handleTeamsBotConfigSubmit is not overridden', async function () { + const bot = new TeamsActivityHandler(); + + const adapter = new TestAdapter(async (context) => { + await bot.run(context); + }); + + const taskSubmitActivity = createInvokeActivity('config/submit'); + await adapter + .send(taskSubmitActivity) + .assertReply((activity) => { + assert.strictEqual(activity.type, 'invokeResponse'); + assert.strictEqual(activity.value.status, 501); + assert.strictEqual(activity.value.body, undefined); + }) + .startTest(); + }); + it('handleTeamsMessagingExtensionSubmitAction is not overridden', async function () { const bot = new TeamsActivityHandler(); diff --git a/libraries/botframework-schema/etc/botframework-schema.api.md b/libraries/botframework-schema/etc/botframework-schema.api.md index 46f65ca51f..dd4146b84c 100644 --- a/libraries/botframework-schema/etc/botframework-schema.api.md +++ b/libraries/botframework-schema/etc/botframework-schema.api.md @@ -367,6 +367,15 @@ export interface BasicCard { title: string; } +// @public +export interface BotConfigAuth { + suggestedActions?: SuggestedActions; + type: 'auth'; +} + +// @public +export type BotConfigAuthType = 'auth' | 'task'; + // @public export type BotMessagePreviewActionType = 'edit' | 'send'; @@ -484,6 +493,21 @@ export interface CommandValue { data?: T; } +// @public (undocumented) +export interface ConfigAuthResponse extends ConfigResponse { +} + +// @public +export interface ConfigResponse { + cacheInfo: CacheInfo; + config: T; + responseType: string; +} + +// @public (undocumented) +export interface ConfigTaskResponse extends ConfigResponse { +} + // @public export enum ContactRelationUpdateActionTypes { // (undocumented) diff --git a/libraries/botframework-schema/src/teams/index.ts b/libraries/botframework-schema/src/teams/index.ts index 5b105961d5..80b7cf305e 100644 --- a/libraries/botframework-schema/src/teams/index.ts +++ b/libraries/botframework-schema/src/teams/index.ts @@ -6,9 +6,29 @@ // The Teams schemas was manually added to botframework-schema. This file has been updated import from the botframework-schema and the extension folder. // The ChannelCount and MemberCount fields were manually added to the TeamDetails definition. import { MessageActionsPayloadBody, O365ConnectorCardActionBase, O365ConnectorCardInputBase } from './extension'; -import { Activity, Attachment, CardAction, ChannelAccount, ConversationAccount } from '../'; +import { Activity, Attachment, CardAction, ChannelAccount, ConversationAccount, SuggestedActions } from '../'; export * from './extension'; +/** + * @interface + * An interface the bot's authentication config for SuggestedActions + */ +export interface BotConfigAuth { + /** + * @member {SuggestedActions} [suggestedActions] SuggestedActions for the Bot Config Auth + */ + suggestedActions?: SuggestedActions; + /** + * @member {BotConfigAuthType} [type] Type of the Bot Config Auth + */ + type: 'auth'; +} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ConfigAuthResponse extends ConfigResponse {} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ConfigTaskResponse extends ConfigResponse {} /** * @interface * An interface representing ChannelInfo. @@ -29,6 +49,25 @@ export interface ChannelInfo { */ type?: string; } +/** + * @interface + * An interface container for the Config response payload + */ +export interface ConfigResponse { + /** + * @member {CacheInfo} [cacheInfo] The data of the ConfigResponse cache, including cache type and cache duration. + */ + cacheInfo: CacheInfo; + /** + * @template T + * @member {T} [config] The response to a configuration message. + */ + config: T; + /** + * @member {string} [responseType] The type of config response. Possible values are 'auth' and 'continue' + */ + responseType: string; +} /** * @interface @@ -1565,6 +1604,16 @@ export type Type = 'ViewAction' | 'OpenUri' | 'HttpPOST' | 'ActionCard'; */ export type ActivityImageType = 'avatar' | 'article'; +/** + * Defines possible values for BotConfigAuth type. + * Possible values include: "auth" + * + * @readonly + * @enum {string} + */ + +export type BotConfigAuthType = 'auth' | 'task'; + /** * Defines values for Os. * Possible values include: 'default', 'iOS', 'android', 'windows'