Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Add search invoke #4171

Merged
merged 9 commits into from
Mar 23, 2022
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions libraries/botbuilder-core/etc/botbuilder-core.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import { O365ConnectorCard } from 'botframework-schema';
import { PasswordServiceClientCredentialFactory } from 'botframework-connector';
import { ReceiptCard } from 'botframework-schema';
import { ResourceResponse } from 'botframework-schema';
import { SearchInvokeResponse } from 'botframework-schema';
import { SearchInvokeValue } from 'botframework-schema';
import { ServiceClientCredentials } from 'botframework-connector';
import { ServiceClientCredentialsFactory } from 'botframework-connector';
import { ServiceCollection } from 'botbuilder-dialogs-adaptive-runtime-core';
Expand Down Expand Up @@ -94,6 +96,7 @@ export class ActivityHandler extends ActivityHandlerBase {
protected onReactionsAddedActivity(reactionsAdded: MessageReaction[], context: TurnContext): Promise<void>;
onReactionsRemoved(handler: BotHandler): this;
protected onReactionsRemovedActivity(reactionsRemoved: MessageReaction[], context: TurnContext): Promise<void>;
protected onSearchInvoke(context: TurnContext, invokeValue: SearchInvokeValue): Promise<SearchInvokeResponse>;
protected onSignInInvoke(context: TurnContext): Promise<void>;
onTokenResponseEvent(handler: BotHandler): this;
onTurn(handler: BotHandler): this;
Expand Down
56 changes: 56 additions & 0 deletions libraries/botbuilder-core/src/activityHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import {
Activity,
AdaptiveCardInvokeResponse,
AdaptiveCardInvokeValue,
SearchInvokeResponse,
SearchInvokeValue,
MessageReaction,
StatusCodes,
} from 'botframework-schema';
Expand Down Expand Up @@ -496,6 +498,12 @@ export class ActivityHandler extends ActivityHandlerBase {
protected async onInvokeActivity(context: TurnContext): Promise<InvokeResponse> {
try {
switch (context.activity.name) {
case 'application/search': {
const invokeValue = this.getSearchInvokeValue(context.activity);
const response = await this.onSearchInvoke(context, invokeValue);
return { status: response.statusCode, body: response };
}

case 'adaptiveCard/action': {
const invokeValue = this.getAdaptiveCardInvokeValue(context.activity);
const response = await this.onAdaptiveCardInvoke(context, invokeValue);
Expand Down Expand Up @@ -550,6 +558,19 @@ export class ActivityHandler extends ActivityHandlerBase {
return Promise.reject(new InvokeException(StatusCodes.NOT_IMPLEMENTED));
}

/**
* Invoked when the bot is sent an invoke activity with name of 'application/search'.
*
* @param context the context object for the current turn
* @param invokeValue incoming activity value
*/
protected onSearchInvoke(
context: TurnContext,
invokeValue: SearchInvokeValue
): Promise<SearchInvokeResponse> {
return Promise.reject(new InvokeException(StatusCodes.NOT_IMPLEMENTED));
}

/**
* Runs all registered _endOfConversation_ handlers and then continues the event emission process.
*
Expand Down Expand Up @@ -693,6 +714,41 @@ export class ActivityHandler extends ActivityHandlerBase {
await this.handle(context, 'UnrecognizedActivityType', this.defaultNextEvent(context));
}

private getSearchInvokeValue(activity: Activity): SearchInvokeValue {
const { value }: { value?: SearchInvokeValue } = activity;
if (!value) {
const response = this.createAdaptiveCardInvokeErrorResponse(
StatusCodes.BAD_REQUEST,
'BadRequest',
'Missing value property for search'
);

throw new InvokeException(StatusCodes.BAD_REQUEST, response);
}

if (!value.kind) {
const response = this.createAdaptiveCardInvokeErrorResponse(
StatusCodes.BAD_REQUEST,
'NotSupported',
`Missing kind property for search.`
);

throw new InvokeException(StatusCodes.BAD_REQUEST, response);
}

if (!value.queryText) {
const response = this.createAdaptiveCardInvokeErrorResponse(
StatusCodes.BAD_REQUEST,
'NotSupported',
`Missing kind queryText for search.`
);

throw new InvokeException(StatusCodes.BAD_REQUEST, response);
}

return value;
}

private getAdaptiveCardInvokeValue(activity: Activity): AdaptiveCardInvokeValue {
const { value }: { value?: AdaptiveCardInvokeValue } = activity;
if (!value) {
Expand Down
23 changes: 23 additions & 0 deletions libraries/botbuilder-core/tests/ActivityHandler.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,29 @@ describe('ActivityHandler', function () {
assert(onAdpativeCardInvokeCalled);
});

it(`should fire onSearchInvoke`, async function () {
const bot = new ActivityHandler();

let onSearchInvokeCalled = false;
bot.onSearchInvoke = async () => {
onSearchInvokeCalled = true;
return { statusCode: 200, value: 'called' };
};

await processActivity(
{
type: ActivityTypes.Invoke,
name: 'application/search',
value: {
kind: 'search',
queryText: 'test bot'
},
},
bot
);
assert(onSearchInvokeCalled);
});

it(`should fire onUnrecognizedActivityType`, async function () {
const bot = new ActivityHandler();

Expand Down
18 changes: 18 additions & 0 deletions libraries/botframework-schema/etc/botframework-schema.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1454,6 +1454,24 @@ export enum RoleTypes {
User = "user"
}

// @public
export interface SearchInvokeOptions {
skip: number;
top: number;
}

// @public
export interface SearchInvokeResponse extends AdaptiveCardInvokeResponse {
}

// @public
export interface SearchInvokeValue {
context: any;
kind: string;
queryOptions: SearchInvokeOptions;
queryText: string;
}

// @public
export interface SemanticAction {
entities: {
Expand Down
48 changes: 48 additions & 0 deletions libraries/botframework-schema/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2368,6 +2368,54 @@ export interface AdaptiveCardInvokeValue {
state: string;
}

/**
* Defines the structure that arrives in the Activity.Value for Invoke activity with
* Name of 'application/search'.
*/
export interface SearchInvokeValue {
/**
* The kind of the search invoke value.
* Must be either search, searchAnswer or typeAhead.
*/
kind: string;
/**
* The query text for the search invoke value.
*/
queryText: string;
/**
* The [SearchInvokeOptions](xref:botframework-schema.SearchInvokeOptions)
* for this search invoke value.
*/
queryOptions: SearchInvokeOptions;
/**
* The the context information about the query. Such as the UI control that issued the query.
* The type of the context field is object and is dependent on the kind field.
* For search and searchAnswers, there is no defined context value.
*/
context: any; // eslint-disable-line @typescript-eslint/no-explicit-any
}

/**
* Provides information about the options desired for a [SearchInvokeValue](xref:botframework-schema.SearchInvokeValue)
*/
export interface SearchInvokeOptions {
/**
* Indicates starting reference number from which ordered search results should be returned.
*/
skip: number;
/**
* Indicates the number of search results that should be returned.
*/
top: number;
}

/**
* Defines the structure that is returned as the result of an Invoke activity with
* Name of 'application/search'.
*/
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface SearchInvokeResponse extends AdaptiveCardInvokeResponse {}

/**
* Represents a response returned by a bot when it receives an `invoke` activity.
*
Expand Down