UNPKG

type2docfx

Version:

A tool to convert json format output from TypeDoc to universal reference model for DocFx to consume.

277 lines (258 loc) 12.4 kB
/** * @module botbuilder-prompts */ /** * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ import { Promiseable, Activity, TurnContext, Attachment, TokenResponse, BotFrameworkAdapter, CardFactory, MessageFactory, ActivityTypes, OAuthCard, SigninCard, ActionTypes } from 'botbuilder'; import { PromptValidator } from './textPrompt'; import { sendPrompt } from './internal'; /** * :package: **botbuilder-prompts** * * Defines settings for an OAuthPrompt. */ export interface OAuthPromptSettings { /** Name of the OAuth connection being used. */ connectionName: string; /** Title of the cards signin button. */ title: string; /** (Optional) additional text to include on the signin card. */ text?: string; } /** * :package: **botbuilder-prompts** * * Prompts the user to sign in using the Bot Frameworks Single Sign On (SSO) service. * * **Usage Example:** * * ```JavaScript * const { createOAuthPrompt } = require('botbuilder-prompts'); * * const loginPrompt = createOAuthPrompt({ * connectionName: 'GitConnection', * title: 'Login To GitHub' * }); * ``` * @param O (Optional) type of result returned by the [recognize()](#recognize) method. This defaults to an instance of `TokenResponse` but can be changed by the prompts custom validator. */ export interface OAuthPrompt<O = TokenResponse> { /** * Sends a formated prompt to the user. * * An `OAuthCard` will be automatically created and sent to the user requesting them to * signin. If you need to localize the card or customize the message sent to the user for any * reason you can pass in the `Activity` to send. This should just be an activity of type * `message` and contain at least one attachment that's an `OAuthCard`. * * **Usage Example:** * * ```JavaScript * await loginPrompt.prompt(context); * ``` * @param context Context for the current turn of conversation. * @param prompt (Optional) activity to send along the user. This should include an attachment containing an `OAuthCard`. If ommited, an activity will be automatically generated. */ prompt(context: TurnContext, prompt?: Partial<Activity>): Promise<void>; /** * Attempts to resolve the token after [prompt()](#prompt) has been called. There are two core * flows that need to be supported to complete a users signin: * * - The automatic signin flow where the SSO service will forward the bot the users access * token using either an `event` or `invoke` activity. * - The "magic code" flow where a user is prompted by the SSO service to send the bot a six * digit code confirming their identity. This code will be sent as a standard `message` activity. * * The `recognize()` method automatically handles both flows for the bot but you should ensure * that you don't accidentally filter out the `event` and `invoke` activities before calling * recognize(). Because of this we generally recommend you put the call to recognize() towards * the beginning of your bot logic. * * You should also be prepared for the case where the user fails to enter the correct * "magic code" or simply decides they don't want to click the signin button. * * **Usage Example:** * * ```JavaScript * const token = await loginPrompt.recognize(context); * if (token) { * // Save token and continue. * } * ``` * @param context Context for the current turn of conversation. * @param connectionName Name of the auth connection to use. */ recognize(context: TurnContext): Promise<O|undefined>; /** * Attempts to retrieve the cached token for a signed in user. You will generally want to call * this before calling [prompt()](#prompt) to send the user a signin card. * * **Usage Example:** * * ```JavaScript * const token = await loginPrompt.getUserToken(context); * if (!token) { * await loginPrompt.prompt(context); * } * ``` * @param context Context for the current turn of conversation. */ getUserToken(context: TurnContext): Promise<O|undefined>; /** * Signs the user out of the service. * * **Usage Example:** * * ```JavaScript * await loginPrompt.signOutUser(context); * ``` * @param context Context for the current turn of conversation. */ signOutUser(context: TurnContext): Promise<void>; } /** * :package: **botbuilder-prompts** * * Creates a new prompt that asks the user to sign in using the Bot Frameworks Single Sign On (SSO) * service. * * **Usage Example:** * * ```JavaScript * async function ensureLogin(context, state, botLogic) { * const now = new Date().getTime(); * if (state.token && now < (new Date(state.token.expiration).getTime() - 60000)) { * return botLogic(context); * } else { * const loginPrompt = createOAuthPrompt({ * connectionName: 'GitConnection', * title: 'Login To GitHub' * }); * const token = await state.loginActive ? loginPrompt.recognize(context) : loginPrompt.getUserToken(context); * if (token) { * state.loginActive = false; * state.token = token; * return botLogic(context); * } else if (context.activity.type === 'message') { * if (!state.loginActive) { * state.loginActive = true; * state.loginStart = now; * await loginPrompt.prompt(context); * } else if (now >= (state.loginStart + (5 * 60 * 1000))) { * state.loginActive = false; * await context.sendActivity(`We're having a problem logging you in. Please try again later.`); * } * } * } * } * ``` * @param O (Optional) type of result returned by the `recognize()` method. This defaults to an instance of `TokenResponse` but can be changed by the prompts custom validator. * @param settings Configuration settings for the OAuthPrompt. * @param validator (Optional) validator for providing additional validation logic or customizing the prompt sent to the user when invalid. */ export function createOAuthPrompt<O = TokenResponse>(settings: OAuthPromptSettings, validator?: PromptValidator<TokenResponse, O>): OAuthPrompt<O> { return { prompt: function prompt(context, prompt) { try { // Validate adapter type if (!('getUserToken' in context.adapter)) { throw new Error(`OAuthPrompt.prompt(): not supported for the current adapter.`) } // Format prompt if (typeof prompt !== 'object') { prompt = MessageFactory.attachment(CardFactory.oauthCard( settings.connectionName, settings.title, settings.text )); } else { // Validate prompt if (!Array.isArray(prompt.attachments)) { throw new Error(`OAuthPrompt.prompt(): supplied prompt missing attachments.`) } const found = prompt.attachments.filter(a => a.contentType === CardFactory.contentTypes.oauthCard); if (found.length == 0) { throw new Error(`OAuthPrompt.prompt(): supplied prompt missing OAuthCard.`) } } // Send prompt return Promise.resolve(prompt) .then((p) => { switch (context.activity.channelId) { case "msteams": case "cortana": case "skype": case "skypeforbusiness": return (context.adapter as BotFrameworkAdapter).getSignInLink(context, settings.connectionName).then((link) => { (p.attachments as Attachment[]).forEach(a => { if (a.contentType === CardFactory.contentTypes.oauthCard) { const card: OAuthCard = a.content; const title = card.buttons[0].title; a.contentType = CardFactory.contentTypes.signinCard; a.content = { text: card.text, buttons: [{ type: ActionTypes.Signin, title: title, value: link }] } as SigninCard; } }); return p; }); default: return p; } }) .then((p) => sendPrompt(context, p)); } catch(err) { return Promise.reject(err); } }, recognize: function recognize(context) { // Validate adapter type if (!('getUserToken' in context.adapter)) { throw new Error(`OAuthPrompt.recognize(): not supported for the current adapter.`) } // Attempt to get the token return Promise.resolve() .then(() => { const adapter = context.adapter as BotFrameworkAdapter; if (isTokenResponseEvent(context)) { return Promise.resolve(context.activity.value as TokenResponse); } else if (isTeamsVerificationInvoke(context)) { const code = context.activity.value.state; return context.sendActivity({ type: 'invokeResponse', value: { status: 200 }}) .then(() => adapter.getUserToken(context, settings.connectionName, code)); } else if (context.activity.type === ActivityTypes.Message) { const matched = /(\d{6})/.exec(context.activity.text); if (matched && matched.length > 1) { return adapter.getUserToken(context, settings.connectionName, matched[1]); } else { return Promise.resolve(undefined); } } }) .then((value: TokenResponse|undefined) => validator ? validator(context, value) : value as any); }, getUserToken: function getUserToken(context) { // Validate adapter type if (!('getUserToken' in context.adapter)) { throw new Error(`OAuthPrompt.getUserToken(): not supported for the current adapter.`) } // Get the token and call validator const adapter = context.adapter as BotFrameworkAdapter; return adapter.getUserToken(context, settings.connectionName) .then((value) => { return Promise.resolve(validator ? validator(context, value) : value as any); }); }, signOutUser: function signOutUser(context) { // Validate adapter type if (!('signOutUser' in context.adapter)) { throw new Error(`OAuthPrompt.signOutUser(): not supported for the current adapter.`) } // Sign out user const adapter = context.adapter as BotFrameworkAdapter; return adapter.signOutUser(context, settings.connectionName); } }; } function isTokenResponseEvent(context: TurnContext): boolean { const a = context.activity; return (a.type === ActivityTypes.Event && a.name === 'tokens/response') } function isTeamsVerificationInvoke(context: TurnContext): boolean { const a = context.activity; return (a.type === ActivityTypes.Invoke && a.name === 'signin/verifyState') }