diff --git a/generators/generator-botbuilder/README.md b/generators/generator-botbuilder/README.md index eb6e9700ee..df996c5844 100644 --- a/generators/generator-botbuilder/README.md +++ b/generators/generator-botbuilder/README.md @@ -14,7 +14,6 @@ The generator supports three different template options. The table below can he | Template | Description | | ---------- | --------- | | Echo Bot | A good template if you want a little more than "Hello World!", but not much more. This template handles the very basics of sending messages to a bot, and having the bot process the messages by repeating them back to the user. This template produces a bot that simply "echoes" back to the user anything the user says to the bot. | -| Core Bot | Our most advanced template, the Core template provides 6 core features every bot is likely to have. This template covers the core features of a Conversational-AI bot using [LUIS](https://www.luis.ai). See the **Core Bot Features** table below for more details. | | Empty Bot | A good template if you are familiar with Bot Framework v4, and simply want a basic skeleton project. Also a good option if you want to take sample code from the documentation and paste it into a minimal bot in order to learn. | ### How to Choose a Template @@ -22,7 +21,6 @@ The generator supports three different template options. The table below can he | Template | When This Template is a Good Choice | | -------- | -------- | | Echo Bot | You are new to Bot Framework v4 and want a working bot with minimal features. | -| Core Bot | You understand some of the core concepts of Bot Framework v4 and are beyond the concepts introduced in the Echo Bot template. You're familiar with or are ready to learn concepts such as language understanding using LUIS, managing multi-turn conversations with Dialogs, handling user initiated Dialog interruptions, and using Adaptive Cards to welcome your users. | | Empty Bot | You are a seasoned Bot Framework v4 developer. You've built bots before, and want the minimum skeleton of a bot. | ### Template Overview @@ -31,38 +29,18 @@ The generator supports three different template options. The table below can he The Echo Bot template is slightly more than the a classic "Hello World!" example, but not by much. This template shows the basic structure of a bot, how a bot recieves messages from a user, and how a bot sends messages to a user. The bot will "echo" back to the user, what the user says to the bot. It is a good choice for first time, new to Bot Framework v4 developers. -#### Core Bot Template - -The Core Bot template consists of set of core features most every bot is likely to have. Building off of the core message processing features found in the Echo Bot template, this template adds a number of more sophisticated features. The table below lists these features and provides links to additional documentation. - -| Core Bot Features | Description | -| ------------------ | ----------- | -| [Send and receive messages](https://docs.microsoft.com/azure/bot-service/bot-builder-howto-send-messages?view=azure-bot-service-4.0&tabs=javascript) | The primary way your bot will communicate with users, and likewise receive communication, is through message activities. Some messages may simply consist of plain text, while others may contain richer content such as cards or attachments. | -| [Proactive messaging](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-howto-proactive-message?view=azure-bot-service-4.0) using [Adaptive Cards](https://docs.microsoft.com/azure/bot-service/bot-builder-send-welcome-message?view=azure-bot-service-4.0?#using-adaptive-card-greeting) | The primary goal when creating any bot is to engage your user in a meaningful conversation. One of the best ways to achieve this goal is to ensure that from the moment a user first connects to your bot, they understand your bot’s main purpose and capabilities. We refer to this as "welcoming the user." The Core template uses an [Adaptive Card](http://adaptivecards.io) to implement this behavior. | -| [Language understanding using LUIS](https://docs.microsoft.com/azure/bot-service/bot-builder-howto-v4-luis?view=azure-bot-service-4.0) | The ability to understand what your user means conversationally and contextually can be a difficult task, but can provide your bot a more natural conversation feel. Language Understanding, called LUIS, enables you to do just that so that your bot can recognize the intent of user messages, allow for more natural language from your user, and better direct the conversation flow. | -| [Multi-turn conversation support using Dialogs](https://docs.microsoft.com/azure/bot-service/bot-builder-concept-dialog?view=azure-bot-service-4.0) | The ability to manage conversations is an important part of the bot/user interation. Bot Framework introduces the concept of a Dialog to handle this conversational pattern. Dialog objects process inbound Activities and generate outbound responses. The business logic of the bot runs either directly or indirectly within Dialog classes. | -| [Managing conversation state](https://docs.microsoft.com/azure/bot-service/bot-builder-howto-v4-state?view=azure-bot-service-4.0) | A key to good bot design is to track the context of a conversation, so that your bot remembers things like the answers to previous questions. | -| [How to handle user-initiated interruptions](https://docs.microsoft.com/azure/bot-service/bot-builder-howto-handle-user-interrupt?view=azure-bot-service-4.0) | While you may think that your users will follow your defined conversation flow step by step, chances are good that they will change their minds or ask a question in the middle of the process instead of answering the question. Handling interruptions means making sure your bot is prepared to handle situations like this. | -| [How to unit test a bot](https://aka.ms/cs-unit-test-docs) | Optionally, the Core Bot template can generate corresponding unit tests that shows how to use the testing framework introduced in Bot Framework version 4.5. Selecting this option provides a complete set of units tests for Core Bot. It shows how to write unit tests to test the various features of Core Bot. To add the Core Bot unit tests, run the generator and answer `yes` when prompted. See below for an example of how to do this from the command line. | - #### Empty Bot Template The Empty Bot template is the minimal skeleton code for a bot. It provides a stub `onTurn` handler but does not perform any actions. If you are experienced writing bots with Bot Framework v4 and want the minimum scaffolding, the Empty template is for you. ## Features by Template -| Feature | Empty Bot | Echo Bot | Core Bot | -| --------- | :-----: | :-----: | :-----: | -| Generate code in JavaScript or TypesScript | X | X | X | -| Support local development and testing using the [Bot Framework Emulator v4](https://www.github.com/microsoft/botframework-emulator) | X | X | X | -| Core bot message processing | | X | X | -| Deploy your bot to Microsoft Azure | | X | X | -| Welcome new users using Adaptive Card technology | | | X | -| Support AI-based greetings using [LUIS](https://www.luis.ai) | | | X | -| Use Dialogs to manage more in-depth conversations | | | X | -| Manage conversation state | | | X | -| Handle user interruptions | | | X | -| Unit test a bot using Bot Framework Testing framework (optional) | | | X | +| Feature | Empty Bot | Echo Bot | +| --------- | :-----: | :-----: | +| Generate code in JavaScript or TypesScript | X | X | +| Support local development and testing using the [Bot Framework Emulator v4](https://www.github.com/microsoft/botframework-emulator) | X | X | +| Core bot message processing | | X | +| Deploy your bot to Microsoft Azure | | X | ## Installation @@ -108,17 +86,16 @@ The generator supports a number of command line options that can be used to chan | --botname, -N | The name given to the bot project | | --description, -D | A brief bit of text that describes the purpose of the bot | | --language, -L | The programming language for the project. Options are `JavaScript` or `TypeScript`. | -| --template, -T | The template used to generate the project. Options are `empty`, `echo`, or `core`. See [https://aka.ms/botbuilder-generator](https://aka.ms/botbuilder-generator) for additional information regarding the different template option and their functional differences. | -| --addtests | _A Core Bot Template Only Feature_. The generator will add unit tests to the Core Bot generated bot. This option is not available to other templates at this time. To learn more about the test framework released with Bot Framework v4.5, see [How to unit test bots](https://aka.ms/js-unit-test-docs). This option is intended to enable automated bot generation for testing purposes. | +| --template, -T | The template used to generate the project. Options are `empty` or `echo`. See [https://aka.ms/botbuilder-generator](https://aka.ms/botbuilder-generator) for additional information regarding the different template option and their functional differences. | | --noprompt | The generator will not prompt for confirmation before creating a new bot. Any requirement options not passed on the command line will use a reasonable default value. This option is intended to enable automated bot generation for testing purposes. | #### Example Using Command Line Options -This example shows how to pass command line options to the generator, setting the default language to TypeScript and the default template to Core. +This example shows how to pass command line options to the generator, setting the default language to TypeScript and the default template to Echo. ```bash -# Run the generator defaulting the language to TypeScript and the template to core -yo botbuilder --L "TypeScript" --T "core" +# Run the generator defaulting the language to TypeScript and the template to echo +yo botbuilder --L "TypeScript" --T "Echo" ``` ### Generating a Bot Using --noprompt @@ -133,7 +110,6 @@ The generator can be run in `--noprompt` mode, which can be used for automated b | --description, -D | "Demonstrate the core capabilities of the Microsoft Bot Framework" | | --language, -L | `JavaScript` | | --template, -T | `echo` | -| --addtests | When specified, will cause the Core Bot template to add unit tests. | #### Examples Using --noprompt @@ -151,13 +127,6 @@ This example shows how to run the generator in --noprompt mode, using all the de yo botbuilder --noprompt ``` -This example shows how to run the generator in --noprompt mode, generating a TypeScript Core Bot with unit tests. - -```bash -# Run the generator using all default options -yo botbuilder -N "my-core-bot-with-tests" -D "A core bot with tests in TypeScript" -L "TypeScript" -T "core" --addtests --noprompt -``` - ## Running Your Bot ### Running Your Bot Locally @@ -202,15 +171,6 @@ The code generated by the botbuilder generator is lint compliant. Depending on npm run lint ``` -#### Testing Core Bots with Tests - -Core Bot templates generated with unit tests can be tested using the following: - -```bash -# launch mocha, run unit tests, report code coverage -npm test -``` - ## Deploy Your Bot to Azure After creating the bot and testing it locally, you can deploy it to Azure to make it accessible from anywhere. diff --git a/generators/generator-botbuilder/components/commonFilesWriter.js b/generators/generator-botbuilder/components/commonFilesWriter.js index 9d2615274e..9b04bb1293 100644 --- a/generators/generator-botbuilder/components/commonFilesWriter.js +++ b/generators/generator-botbuilder/components/commonFilesWriter.js @@ -40,7 +40,6 @@ module.exports.commonFilesWriter = (generator, templatePath) => { // write the project files common to all templates // do any text token processing where required - // NOTE: core bot with tests overwrites this file to add a npm test script command generator.fs.copyTpl( generator.templatePath(path.join(templatePath, 'package.json.' + extension)), generator.destinationPath('package.json'), diff --git a/generators/generator-botbuilder/components/constants.js b/generators/generator-botbuilder/components/constants.js index 2dcbd60847..4f5cad8c4d 100644 --- a/generators/generator-botbuilder/components/constants.js +++ b/generators/generator-botbuilder/components/constants.js @@ -4,17 +4,14 @@ // display names of the template options module.exports.BOT_TEMPLATE_NAME_EMPTY = 'Empty Bot'; module.exports.BOT_TEMPLATE_NAME_SIMPLE = 'Echo Bot'; -module.exports.BOT_TEMPLATE_NAME_CORE = 'Core Bot'; // aka.ms links to documentation for each template option module.exports.BOT_HELP_URL_EMPTY = 'https://aka.ms/bot-template-empty'; module.exports.BOT_HELP_URL_SIMPLE = 'https://aka.ms/bot-template-echo'; -module.exports.BOT_HELP_URL_CORE = 'https://aka.ms/bot-template-core'; // --noprompt template values module.exports.BOT_TEMPLATE_NOPROMPT_EMPTY = 'empty'; module.exports.BOT_TEMPLATE_NOPROMPT_SIMPLE = 'echo'; -module.exports.BOT_TEMPLATE_NOPROMPT_CORE = 'core'; // programming language name options module.exports.BOT_LANG_NAME_JAVASCRIPT = 'JavaScript'; diff --git a/generators/generator-botbuilder/components/coreTemplateWriter.js b/generators/generator-botbuilder/components/coreTemplateWriter.js deleted file mode 100644 index 32dd19d5bb..0000000000 --- a/generators/generator-botbuilder/components/coreTemplateWriter.js +++ /dev/null @@ -1,379 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const _ = require('lodash'); -const mkdirp = require('mkdirp'); -const path = require('path'); - -const { commonFilesWriter } = require('./commonFilesWriter'); -const { BOT_TEMPLATE_NAME_CORE, BOT_TEMPLATE_NOPROMPT_CORE } = require('./constants'); -const pkg = require('../package.json'); - -// generators/app/templates folder name -const GENERATOR_TEMPLATE_NAME = 'core'; - -const LANG_JS = 'javascript'; -const LANG_TS = 'typescript'; - -/** - * - * @param {String} language either 'javascript' or 'typescript' - */ -const _getSourceFolders = language => { - if(!language || (_.toLower(language) !== LANG_JS && _.toLower(language) !== LANG_TS)) { - throw new Error(`coreTemplateWriter._getTargetFolders called for invalid language: ${ language }`); - } - - // get the folder structure, based on language - let folders = [ - 'bots', - 'cognitiveModels', - 'dialogs', - 'resources', - ]; - // if we're generating TypeScript, then we need a deploymentScripts folder - if(_.toLower(language) === LANG_TS) { - folders = folders.concat(['deploymentScripts']); - } - return folders; -} - -/** - * - * @param {String} language either 'javascript' or 'typescript' - */ -const _getTargetFolders = language => { - if(!language || (_.toLower(language) !== LANG_JS && _.toLower(language) !== LANG_TS)) { - throw new Error(`coreTemplateWriter._getTargetFolders called for invalid language: ${ language }`); - } - - let folders; - if(_.toLower(language) === LANG_TS) { - folders = [ - path.join('src', 'bots'), - 'cognitiveModels', - path.join('src', 'dialogs'), - 'resources', - 'deploymentScripts', - ]; - } else { - folders = [ - 'bots', - 'cognitiveModels', - 'dialogs', - 'resources', - ]; - } - return folders; -} - -/** - * - * @param {String} language either 'javascript' or 'typescript' - */ -const _getSourceTestFolders = language => { - const lang = _.toLower(language); - if(!lang || (lang !== LANG_JS && lang !== LANG_TS)) { - throw new Error(`coreTemplateWriter._getTargetFolders called for invalid language: ${ language }`); - } - - // get the folder structure, based on language - const folders = [ - 'tests', - path.join('tests', 'bots'), - path.join('tests', 'dialogs'), - path.join('tests', 'dialogs', 'testData'), - path.join('tests', 'dialogs', 'testData'), - ]; - return folders; -} - -/** - * - * @param {String} language either 'javascript' or 'typescript' - */ -const _getTargetTestFolders = language => { - const lang = _.toLower(language); - if(!lang || (lang !== LANG_JS && lang !== LANG_TS)) { - throw new Error(`coreTemplateWriter._getTargetTestFolders called for invalid language: ${ language }`); - } - - let folders; - if(_.toLower(language) === LANG_TS) { - folders = [ - path.join('src', 'tests'), - path.join('src', 'tests', 'bots'), - path.join('src', 'tests', 'dialogs'), - path.join('src', 'tests', 'dialogs', 'testData'), - 'testResources', - ]; - } else { - folders = [ - 'tests', - path.join('tests', 'bots'), - path.join('tests', 'dialogs'), - path.join('tests', 'dialogs', 'testData'), - path.join('tests', 'dialogs', 'testData'), - ]; - } - return folders; -} - -/** - * Write the files that are specific to the core bot template - * - * @param {Generator} generator Yeoman's generator object - * @param {String} templatePath file path to write the generated code - */ -const writeCoreTemplateTestFiles = (generator, templatePath) => { - // lets validate that we should be called - if(generator.templateConfig.addtests !== true) { - throw new Error(`writeCoreTemplateTestFiles called when 'addtests' flag is false: ${ generator.templateConfig.addtests }`); - } - // declare some constants that map to srcFolder and destFolder array offsets - const TEST_FOLDER = 0; - const BOTS_TEST_FOLDER = 1; - const DIALOGS_TEST_FOLDER = 2; - const DIALOGS_TESTDATA_FOLDER = 3; - const DIALOGS_TESTDATA_JSON_FOLDER = 4; - - // get the folder structure, based on language - const srcFolders = _getSourceTestFolders(_.toLower(generator.templateConfig.language)); - const destFolders = _getTargetTestFolders(_.toLower(generator.templateConfig.language)); - - const extension = _.toLower(generator.templateConfig.language) === 'javascript' ? 'js' : 'ts'; - - // create the core bot tests folder structure - for (let cnt = 0; cnt < destFolders.length; ++cnt) { - mkdirp.sync(destFolders[cnt]); - } - - // overwrite the commonFilesWriter version as we want to version that has - // the npm test script command `npm test' - generator.fs.copyTpl( - generator.templatePath(path.join(templatePath, 'package-with-tests.json.' + extension)), - generator.destinationPath('package.json'), - { - botname: generator.templateConfig.botname, - botDescription: generator.templateConfig.description, - version: pkg.version, - npmMain: (extension === 'js' ? `index.js` : `./lib/index.js`) - } - ); - - - - // write out the test folder's README.md file - let sourcePath = path.join(templatePath, srcFolders[TEST_FOLDER]); - let destinationPath = path.join(generator.destinationPath(), destFolders[TEST_FOLDER]); - generator.fs.copyTpl( - path.join(sourcePath, 'README.md'), - path.join(destinationPath, 'README.md'), - { - botname: generator.templateConfig.botname - } - ); - - // write out the bots test folder - sourcePath = path.join(templatePath, srcFolders[BOTS_TEST_FOLDER]); - destinationPath = path.join(generator.destinationPath(), destFolders[BOTS_TEST_FOLDER]); - generator.fs.copy( - path.join(sourcePath, `dialogAndWelcomeBot.test.${extension}`), - path.join(destinationPath, `dialogAndWelcomeBot.test.${extension}`) - ); - - // write out the dialogs test folder - sourcePath = path.join(templatePath, srcFolders[DIALOGS_TEST_FOLDER]); - destinationPath = path.join(generator.destinationPath(), destFolders[DIALOGS_TEST_FOLDER]); - generator.fs.copy( - path.join(sourcePath, `bookingDialog.test.${extension}`), - path.join(destinationPath, `bookingDialog.test.${extension}`) - ); - generator.fs.copy( - path.join(sourcePath, `cancelAndHelpDialog.test.${extension}`), - path.join(destinationPath, `cancelAndHelpDialog.test.${extension}`) - ); - generator.fs.copy( - path.join(sourcePath, `dateResolverDialog.test.${extension}`), - path.join(destinationPath, `dateResolverDialog.test.${extension}`) - ); - generator.fs.copy( - path.join(sourcePath, `mainDialog.test.${extension}`), - path.join(destinationPath, `mainDialog.test.${extension}`) - ); - - // write out the dialogs testData folder (treat .json files separately) - sourcePath = path.join(templatePath, srcFolders[DIALOGS_TESTDATA_FOLDER]); - destinationPath = path.join(generator.destinationPath(), destFolders[DIALOGS_TESTDATA_FOLDER]); - generator.fs.copy( - path.join(sourcePath, `bookingDialogTestCases.${extension}`), - path.join(destinationPath, `bookingDialogTestCases.${extension}`) - ); - generator.fs.copy( - path.join(sourcePath, `dateResolverTestCases.${extension}`), - path.join(destinationPath, `dateResolverTestCases.${extension}`) - ); - - // write out the dialogs testData folder (treat .json files separately) - // tsc won't copy these where I want them, so we move them up to a root folder - sourcePath = path.join(templatePath, srcFolders[DIALOGS_TESTDATA_JSON_FOLDER]); - destinationPath = path.join(generator.destinationPath(), destFolders[DIALOGS_TESTDATA_JSON_FOLDER]); - generator.fs.copy( - path.join(sourcePath, 'FlightFromCdgToJfk.json'), - path.join(destinationPath, 'FlightFromCdgToJfk.json') - ); - generator.fs.copy( - path.join(sourcePath, 'FlightFromMadridToChicago.json'), - path.join(destinationPath, 'FlightFromMadridToChicago.json') - ); - generator.fs.copy( - path.join(sourcePath, 'FlightFromParisToNewYork.json'), - path.join(destinationPath, 'FlightFromParisToNewYork.json') - ); - generator.fs.copy( - path.join(sourcePath, 'FlightToMadrid.json'), - path.join(destinationPath, 'FlightToMadrid.json') - ); -} - -/** - * Write the files that are specific to the core bot template - * - * @param {Generator} generator Yeoman's generator object - * @param {String} templatePath file path to write the generated code - */ -const writeCoreTemplateFiles = (generator, templatePath) => { - const BOTS_FOLDER = 0; - const COGNITIVE_MODELS_FOLDER = 1; - const DIALOGS_FOLDER = 2; - const RESOURCES_FOLDER = 3; - const DEPLOYMENT_SCRIPTS_FOLDER = 4; - const TS_SRC_FOLDER = 'src'; - - // get the folder structure, based on language - const srcFolders = _getSourceFolders(_.toLower(generator.templateConfig.language), generator.options.addtests); - const destFolders = _getTargetFolders(_.toLower(generator.templateConfig.language), generator.options.addtests); - - const extension = _.toLower(generator.templateConfig.language) === 'javascript' ? 'js' : 'ts'; - const srcFolder = _.toLower(generator.templateConfig.language) === 'javascript' ? '' : TS_SRC_FOLDER; - - // create the core bot folder structure - for (let cnt = 0; cnt < destFolders.length; ++cnt) { - mkdirp.sync(destFolders[cnt]); - } - - // write out the bots folder - let sourcePath = path.join(templatePath, srcFolders[BOTS_FOLDER]); - let destinationPath = path.join(generator.destinationPath(), destFolders[BOTS_FOLDER]); - generator.fs.copy( - path.join(sourcePath, `dialogAndWelcomeBot.${extension}`), - path.join(destinationPath, `dialogAndWelcomeBot.${extension}`), - { - botname: generator.templateConfig.botname - } - ); - generator.fs.copy( - path.join(sourcePath, `dialogBot.${extension}`), - path.join(destinationPath, `dialogBot.${extension}`) - ); - - // write out the LUIS model - sourcePath = path.join(templatePath, srcFolders[COGNITIVE_MODELS_FOLDER]); - destinationPath = path.join(generator.destinationPath(), destFolders[COGNITIVE_MODELS_FOLDER]); - generator.fs.copy( - path.join(sourcePath, 'FlightBooking.json'), - path.join(destinationPath, 'FlightBooking.json') - ); - - // if we're writing out TypeScript, then we need to add a webConfigPrep.js - if(_.toLower(generator.templateConfig.language) === LANG_TS) { - sourcePath = path.join(templatePath, srcFolders[DEPLOYMENT_SCRIPTS_FOLDER]); - destinationPath = path.join(generator.destinationPath(), destFolders[DEPLOYMENT_SCRIPTS_FOLDER]); - generator.fs.copy( - path.join(sourcePath, 'webConfigPrep.js'), - path.join(destinationPath, 'webConfigPrep.js') - ); - } - - // write out the dialogs folder - sourcePath = path.join(templatePath, srcFolders[DIALOGS_FOLDER]); - destinationPath = path.join(generator.destinationPath(), destFolders[DIALOGS_FOLDER]); - if(_.toLower(generator.templateConfig.language) === LANG_TS) { - generator.fs.copy( - path.join(sourcePath, `bookingDetails.${extension}`), - path.join(destinationPath, `bookingDetails.${extension}`) - ); - } - generator.fs.copy( - path.join(sourcePath, `bookingDialog.${extension}`), - path.join(destinationPath, `bookingDialog.${extension}`) - ); - generator.fs.copy( - path.join(sourcePath, `cancelAndHelpDialog.${extension}`), - path.join(destinationPath, `cancelAndHelpDialog.${extension}`) - ); - generator.fs.copy( - path.join(sourcePath, `dateResolverDialog.${extension}`), - path.join(destinationPath, `dateResolverDialog.${extension}`) - ); - generator.fs.copy( - path.join(sourcePath, `flightBookingRecognizer.${extension}`), - path.join(destinationPath, `flightBookingRecognizer.${extension}`) - ); - generator.fs.copy( - path.join(sourcePath, `mainDialog.${extension}`), - path.join(destinationPath, `mainDialog.${extension}`) - ); - - // write out the resources folder - // which contains the welcome adaptive card - sourcePath = path.join(templatePath, srcFolders[RESOURCES_FOLDER]); - destinationPath = path.join(generator.destinationPath(), destFolders[RESOURCES_FOLDER]); - generator.fs.copy( - path.join(sourcePath, 'welcomeCard.json'), - path.join(destinationPath, 'welcomeCard.json') - ); - - // write out the index.js and bot.js - destinationPath = path.join(generator.destinationPath(), srcFolder); - - // gen index and main dialog files - generator.fs.copyTpl( - generator.templatePath(path.join(templatePath, `index.${extension}`)), - path.join(destinationPath, `index.${extension}`), - { - botname: generator.templateConfig.botname - } - ); - - // if asked to write out unit tests, then let's have a go at it - if(generator.options.addtests) { - writeCoreTemplateTestFiles(generator, templatePath); - } -} - -/** - * Write project files for Core template - * - * @param {Generator} generator Yeoman's generator object - */ -module.exports.coreTemplateWriter = generator => { - // do some simple sanity checking to ensure we're being - // called correctly - const template = _.toLower(generator.templateConfig.template) - if (template !== _.toLower(BOT_TEMPLATE_NAME_CORE) && template !== _.toLower(BOT_TEMPLATE_NOPROMPT_CORE)) { - throw new Error(`coreTemplateWriter called for wrong template: ${ generator.templateConfig.template }`); - } - const templatePath = path.join(generator.templatePath(), GENERATOR_TEMPLATE_NAME); - - // write files common to all template options - commonFilesWriter(generator, templatePath); - - // write files specific to the core bot template - writeCoreTemplateFiles(generator, templatePath); - - // write out unit tests if asked to do so - if(generator.templateConfig.addtests === true) { - writeCoreTemplateTestFiles(generator, templatePath); - } -} diff --git a/generators/generator-botbuilder/components/prompts.js b/generators/generator-botbuilder/components/prompts.js index 5e9bc81769..7b1101da67 100644 --- a/generators/generator-botbuilder/components/prompts.js +++ b/generators/generator-botbuilder/components/prompts.js @@ -6,13 +6,10 @@ const _ = require('lodash'); const { BOT_TEMPLATE_NAME_EMPTY, BOT_TEMPLATE_NAME_SIMPLE, - BOT_TEMPLATE_NAME_CORE, BOT_TEMPLATE_NOPROMPT_EMPTY, BOT_TEMPLATE_NOPROMPT_SIMPLE, - BOT_TEMPLATE_NOPROMPT_CORE, BOT_HELP_URL_EMPTY, BOT_HELP_URL_SIMPLE, - BOT_HELP_URL_CORE, BOT_LANG_NAME_JAVASCRIPT, BOT_LANG_NAME_TYPESCRIPT } = require('./constants'); @@ -45,7 +42,7 @@ module.exports.configureCommandlineOptions = gen => { alias: 'L' }); - const templateDesc = `The initial bot capabilities. (${BOT_TEMPLATE_NAME_EMPTY} | ${BOT_TEMPLATE_NAME_SIMPLE} | ${BOT_TEMPLATE_NAME_CORE})`; + const templateDesc = `The initial bot capabilities. (${BOT_TEMPLATE_NAME_EMPTY} | ${BOT_TEMPLATE_NAME_SIMPLE})`; gen.option('template', { desc: templateDesc, type: String, @@ -53,13 +50,6 @@ module.exports.configureCommandlineOptions = gen => { alias: 'T' }); - gen.argument('addtests', { - desc: `Generate unit tests (${BOT_TEMPLATE_NAME_CORE} only).`, - type: Boolean, - required: false, - default: false - }); - gen.argument('noprompt', { desc: 'Do not prompt for any information or confirmation', type: Boolean, @@ -153,10 +143,6 @@ module.exports.getPrompts = (generator) => { name: `${BOT_TEMPLATE_NAME_SIMPLE} - ${BOT_HELP_URL_SIMPLE}`, value: BOT_TEMPLATE_NOPROMPT_SIMPLE }, - { - name: `${BOT_TEMPLATE_NAME_CORE} - ${BOT_HELP_URL_CORE}`, - value: BOT_TEMPLATE_NOPROMPT_CORE - }, { name: `${BOT_TEMPLATE_NAME_EMPTY} - ${BOT_HELP_URL_EMPTY}`, value: BOT_TEMPLATE_NOPROMPT_EMPTY @@ -166,18 +152,6 @@ module.exports.getPrompts = (generator) => { }).then(answer => { // store the template prompt answer generator.templateConfig.template = answer.template; - - if(_.toLower(answer.template) === _.toLower(BOT_TEMPLATE_NOPROMPT_CORE)) { - return generator.prompt({ - type: 'confirm', - name: 'addtests', - message: 'Would you like to add unit tests to test your new bot?', - default: true - }).then(answer => { - // store the addtests prompt answer - generator.templateConfig.addtests = answer.addtests; - }); - } }); }, diff --git a/generators/generator-botbuilder/generators/app/index.js b/generators/generator-botbuilder/generators/app/index.js index 676429b8e8..cae3e4c708 100644 --- a/generators/generator-botbuilder/generators/app/index.js +++ b/generators/generator-botbuilder/generators/app/index.js @@ -8,7 +8,6 @@ const chalk = require('chalk'); const pkg = require('../../package.json'); const prompts = require('../../components/prompts'); -const { coreTemplateWriter } = require('../../components/coreTemplateWriter'); const { echoTemplateWriter } = require('../../components/echoTemplateWriter'); const { emptyTemplateWriter } = require('../../components/emptyTemplateWriter'); const { @@ -16,10 +15,8 @@ const { BOT_LANG_NAME_TYPESCRIPT, BOT_TEMPLATE_NAME_EMPTY, BOT_TEMPLATE_NAME_SIMPLE, - BOT_TEMPLATE_NAME_CORE, BOT_TEMPLATE_NOPROMPT_EMPTY, BOT_TEMPLATE_NOPROMPT_SIMPLE, - BOT_TEMPLATE_NOPROMPT_CORE } = require('../../components/constants'); _.extend(Generator.prototype, require('yeoman-generator/lib/actions/install')); @@ -131,11 +128,6 @@ module.exports = class extends Generator { echoTemplateWriter(this); break; - case _.toLower(BOT_TEMPLATE_NAME_CORE): - case _.toLower(BOT_TEMPLATE_NOPROMPT_CORE): - coreTemplateWriter(this); - break; - default: const errorMsg = `ERROR: Unable to generate a new bot. Invalid template: [${template}]`; this.log(chalk.red(errorMsg)); @@ -170,23 +162,11 @@ module.exports = class extends Generator { const template = (this.templateConfig.template ? _.toLower(this.templateConfig.template) : undefined); const tmplEmpty = _.toLower(BOT_TEMPLATE_NOPROMPT_EMPTY); const tmplSimple = _.toLower(BOT_TEMPLATE_NOPROMPT_SIMPLE); - const tmplCore = _.toLower(BOT_TEMPLATE_NOPROMPT_CORE); - if (!template || (template !== tmplEmpty && template !== tmplSimple && template !== tmplCore)) { + if (!template || (template !== tmplEmpty && template !== tmplSimple)) { throw new Error('Must specify a template when using --noprompt argument. Use --template or -T'); } - // let's see if unit tests are requested - if(this.templateConfig.addtests) { - // so they're asking for tests, let's make sure they've specified the corebot template - // or else we have an invalid set of command line arguments. unit tests are only available - // with the corebot template - if(template !== tmplCore) { - throw new Error('Invalid use of --addtests. Can only be specified when using --template "core" or -T "core" '); - } - } else { - this.templateConfig.addtests = false; - } - // when run using --noprompt and we have all the required templateConfig, then set final confirmation to true + // when run using --noprompt and we have all the required templateConfig, then set final confirmation to true // so we can go forward and create the new bot without prompting the user for confirmation this.templateConfig.finalConfirmation = true; } diff --git a/generators/generator-botbuilder/generators/app/templates/core/README.md.js b/generators/generator-botbuilder/generators/app/templates/core/README.md.js deleted file mode 100644 index 68a97ab200..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/README.md.js +++ /dev/null @@ -1,86 +0,0 @@ -# <%= botname %> - -<%= description %> - -This bot has been created using [Bot Framework](https://dev.botframework.com), it shows how to: - -- Use [LUIS](https://www.luis.ai) to implement core AI capabilities -- Implement a multi-turn conversation using Dialogs -- Handle user interruptions for such things as `Help` or `Cancel` -- Prompt for and validate requests for information from the user - -## Prerequisites - -This sample **requires** prerequisites in order to run. - -### Overview - -This bot uses [LUIS](https://www.luis.ai), an AI based cognitive service, to implement language understanding. - -- [Node.js](https://nodejs.org) version 10.14.1 or higher - - ```bash - # determine node version - node --version - ``` - -### Create a LUIS Application to enable language understanding - -The LUIS model for this example can be found under `cognitiveModels/FlightBooking.json` and the LUIS language model setup, training, and application configuration steps can be found [here](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-howto-v4-luis?view=azure-bot-service-4.0&tabs=javascript). - -Once you created the LUIS model, update `.env` with your `LuisAppId`, `LuisAPIKey` and `LuisAPIHostName`. - -```text -LuisAppId="Your LUIS App Id" -LuisAPIKey="Your LUIS Subscription key here" -LuisAPIHostName="Your LUIS App region here (i.e: westus.api.cognitive.microsoft.com)" -``` - -# To run the bot - -- Install modules - - ```bash - npm install - ``` -- Setup LUIS - -The prerequisite outlined above contain the steps necessary to provision a language understanding model on www.luis.ai. Refer to _Create a LUIS Application to enable language understanding_ above for directions to setup and configure LUIS. - -- Start the bot - - ```bash - npm start - ``` -## Testing the bot using Bot Framework Emulator - -[Bot Framework Emulator](https://github.com/microsoft/botframework-emulator) is a desktop application that allows bot developers to test and debug their bots on localhost or running remotely through a tunnel. - -- Install the Bot Framework Emulator version 4.9.0 or greater from [here](https://github.com/Microsoft/BotFramework-Emulator/releases) - -### Connect to the bot using Bot Framework Emulator - -- Launch Bot Framework Emulator -- File -> Open Bot -- Enter a Bot URL of `http://localhost:3978/api/messages` - -## Deploy the bot to Azure - -To learn more about deploying a bot to Azure, see [Deploy your bot to Azure](https://aka.ms/azuredeployment) for a complete list of deployment instructions. - - -## Further reading - -- [Bot Framework Documentation](https://docs.botframework.com) -- [Bot Basics](https://docs.microsoft.com/azure/bot-service/bot-builder-basics?view=azure-bot-service-4.0) -- [Dialogs](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-concept-dialog?view=azure-bot-service-4.0) -- [Gathering Input Using Prompts](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-prompts?view=azure-bot-service-4.0) -- [Activity processing](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-concept-activity-processing?view=azure-bot-service-4.0) -- [Azure Bot Service Introduction](https://docs.microsoft.com/azure/bot-service/bot-service-overview-introduction?view=azure-bot-service-4.0) -- [Azure Bot Service Documentation](https://docs.microsoft.com/azure/bot-service/?view=azure-bot-service-4.0) -- [Azure CLI](https://docs.microsoft.com/cli/azure/?view=azure-cli-latest) -- [Azure Portal](https://portal.azure.com) -- [Language Understanding using LUIS](https://docs.microsoft.com/en-us/azure/cognitive-services/luis/) -- [Channels and Bot Connector Service](https://docs.microsoft.com/en-us/azure/bot-service/bot-concepts?view=azure-bot-service-4.0) -- [Restify](https://www.npmjs.com/package/restify) -- [dotenv](https://www.npmjs.com/package/dotenv) diff --git a/generators/generator-botbuilder/generators/app/templates/core/README.md.ts b/generators/generator-botbuilder/generators/app/templates/core/README.md.ts deleted file mode 100644 index 0e3fe548f2..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/README.md.ts +++ /dev/null @@ -1,99 +0,0 @@ -# <%= botname %> - -<%= description %> - -This bot has been created using [Bot Framework](https://dev.botframework.com), it shows how to: - -- Use [LUIS](https://www.luis.ai) to implement core AI capabilities -- Implement a multi-turn conversation using Dialogs -- Handle user interruptions for such things as `Help` or `Cancel` -- Prompt for and validate requests for information from the user - -## Prerequisites - -This sample **requires** prerequisites in order to run. - -### Overview - -This bot uses [LUIS](https://www.luis.ai), an AI based cognitive service, to implement language understanding. - -- [Node.js](https://nodejs.org) version 10.14.1 or higher - - - ```bash - # determine node version - node --version - ``` - -### Create a LUIS Application to enable language understanding - -The LUIS model for this example can be found under `cognitiveModels/FlightBooking.json` and the LUIS language model setup, training, and application configuration steps can be found [here](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-howto-v4-luis?view=azure-bot-service-4.0&tabs=javascript). - -Once you created the LUIS model, update `.env` with your `LuisAppId`, `LuisAPIKey` and `LuisAPIHostName`. - -```text -LuisAppId="Your LUIS App Id" -LuisAPIKey="Your LUIS Subscription key here" -LuisAPIHostName="Your LUIS App region here (i.e: westus.api.cognitive.microsoft.com)" -``` - -## To run the bot - -- Install modules - - ```bash - npm install - ``` -- Build the bot source code - - ```bash - npm run build - ``` -- Setup LUIS - -The prerequisite outlined above contain the steps necessary to provision a language understanding model on www.luis.ai. Refer to _Create a LUIS Application to enable language understanding_ above for directions to setup and configure LUIS. - -- Start the bot - - ```bash - npm start - ``` -## Testing the bot using Bot Framework Emulator - -[Bot Framework Emulator](https://github.com/microsoft/botframework-emulator) is a desktop application that allows bot developers to test and debug their bots on localhost or running remotely through a tunnel. - -- Install the Bot Framework Emulator version 4.9.0 or greater from [here](https://github.com/Microsoft/BotFramework-Emulator/releases) - -### Connect to the bot using Bot Framework Emulator - -- Launch Bot Framework Emulator -- File -> Open Bot -- Enter a Bot URL of `http://localhost:3978/api/messages` - -## Deploy the bot to Azure - -### Publishing Changes to Azure Bot Service - - ```bash - # build the TypeScript bot before you publish - npm run build - ``` - -To learn more about deploying a bot to Azure, see [Deploy your bot to Azure](https://aka.ms/azuredeployment) for a complete list of deployment instructions. - -## Further reading - -- [Bot Framework Documentation](https://docs.botframework.com) -- [Bot Basics](https://docs.microsoft.com/azure/bot-service/bot-builder-basics?view=azure-bot-service-4.0) -- [Dialogs](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-concept-dialog?view=azure-bot-service-4.0) -- [Gathering Input Using Prompts](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-prompts?view=azure-bot-service-4.0) -- [Activity processing](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-concept-activity-processing?view=azure-bot-service-4.0) -- [Azure Bot Service Introduction](https://docs.microsoft.com/azure/bot-service/bot-service-overview-introduction?view=azure-bot-service-4.0) -- [Azure Bot Service Documentation](https://docs.microsoft.com/azure/bot-service/?view=azure-bot-service-4.0) -- [Azure CLI](https://docs.microsoft.com/cli/azure/?view=azure-cli-latest) -- [Azure Portal](https://portal.azure.com) -- [Language Understanding using LUIS](https://docs.microsoft.com/en-us/azure/cognitive-services/luis/) -- [Channels and Bot Connector Service](https://docs.microsoft.com/en-us/azure/bot-service/bot-concepts?view=azure-bot-service-4.0) -- [TypeScript](https://www.typescriptlang.org) -- [Restify](https://www.npmjs.com/package/restify) -- [dotenv](https://www.npmjs.com/package/dotenv) diff --git a/generators/generator-botbuilder/generators/app/templates/core/_env b/generators/generator-botbuilder/generators/app/templates/core/_env deleted file mode 100644 index 98a9adcd35..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/_env +++ /dev/null @@ -1,7 +0,0 @@ -MicrosoftAppType= -MicrosoftAppId= -MicrosoftAppPassword= -MicrosoftAppTenantId= -LuisAppId= -LuisAPIKey= -LuisAPIHostName= diff --git a/generators/generator-botbuilder/generators/app/templates/core/_eslintrc.js b/generators/generator-botbuilder/generators/app/templates/core/_eslintrc.js deleted file mode 100644 index 73a8094046..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/_eslintrc.js +++ /dev/null @@ -1,15 +0,0 @@ -/* eslint-disable */ -module.exports = { - "extends": "standard", - "rules": { - "semi": [2, "always"], - "indent": [2, 4], - "no-return-await": 0, - "space-before-function-paren": [2, { - "named": "never", - "anonymous": "never", - "asyncArrow": "always" - }], - "template-curly-spacing": [2, "always"] - } -}; diff --git a/generators/generator-botbuilder/generators/app/templates/core/_gitignore b/generators/generator-botbuilder/generators/app/templates/core/_gitignore deleted file mode 100644 index d2e4927b5c..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/_gitignore +++ /dev/null @@ -1,5 +0,0 @@ -lib/ -node_modules/ -.nyc_output/ -.vscode/ -.env \ No newline at end of file diff --git a/generators/generator-botbuilder/generators/app/templates/core/bots/dialogAndWelcomeBot.js b/generators/generator-botbuilder/generators/app/templates/core/bots/dialogAndWelcomeBot.js deleted file mode 100644 index edc32b2750..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/bots/dialogAndWelcomeBot.js +++ /dev/null @@ -1,25 +0,0 @@ -const { CardFactory } = require('botbuilder-core'); -const { DialogBot } = require('./dialogBot'); -const WelcomeCard = require('../resources/welcomeCard.json'); - -class DialogAndWelcomeBot extends DialogBot { - constructor(conversationState, userState, dialog) { - super(conversationState, userState, dialog); - - this.onMembersAdded(async (context, next) => { - const membersAdded = context.activity.membersAdded; - for (let cnt = 0; cnt < membersAdded.length; cnt++) { - if (membersAdded[cnt].id !== context.activity.recipient.id) { - const welcomeCard = CardFactory.adaptiveCard(WelcomeCard); - await context.sendActivity({ attachments: [welcomeCard] }); - await dialog.run(context, conversationState.createProperty('DialogState')); - } - } - - // By calling next() you ensure that the next BotHandler is run. - await next(); - }); - } -} - -module.exports.DialogAndWelcomeBot = DialogAndWelcomeBot; diff --git a/generators/generator-botbuilder/generators/app/templates/core/bots/dialogAndWelcomeBot.ts b/generators/generator-botbuilder/generators/app/templates/core/bots/dialogAndWelcomeBot.ts deleted file mode 100644 index 372e4ef059..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/bots/dialogAndWelcomeBot.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { BotState, CardFactory } from 'botbuilder'; -import { Dialog, DialogState } from 'botbuilder-dialogs'; -import { MainDialog } from '../dialogs/mainDialog'; -import { DialogBot } from './dialogBot'; - -const WelcomeCard = require('../../resources/welcomeCard.json'); - -export class DialogAndWelcomeBot extends DialogBot { - constructor(conversationState: BotState, userState: BotState, dialog: Dialog) { - super(conversationState, userState, dialog); - - this.onMembersAdded(async (context, next) => { - const membersAdded = context.activity.membersAdded; - for (const member of membersAdded) { - if (member.id !== context.activity.recipient.id) { - const welcomeCard = CardFactory.adaptiveCard(WelcomeCard); - await context.sendActivity({ attachments: [welcomeCard] }); - await (dialog as MainDialog).run(context, conversationState.createProperty('DialogState')); - } - } - // By calling next() you ensure that the next BotHandler is run. - await next(); - }); - } -} diff --git a/generators/generator-botbuilder/generators/app/templates/core/bots/dialogBot.js b/generators/generator-botbuilder/generators/app/templates/core/bots/dialogBot.js deleted file mode 100644 index 055354f36c..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/bots/dialogBot.js +++ /dev/null @@ -1,42 +0,0 @@ -const { ActivityHandler } = require('botbuilder'); - -class DialogBot extends ActivityHandler { - /** - * - * @param {ConversationState} conversationState - * @param {UserState} userState - * @param {Dialog} dialog - */ - constructor(conversationState, userState, dialog) { - super(); - if (!conversationState) throw new Error('[DialogBot]: Missing parameter. conversationState is required'); - if (!userState) throw new Error('[DialogBot]: Missing parameter. userState is required'); - if (!dialog) throw new Error('[DialogBot]: Missing parameter. dialog is required'); - - this.conversationState = conversationState; - this.userState = userState; - this.dialog = dialog; - this.dialogState = this.conversationState.createProperty('DialogState'); - - this.onMessage(async (context, next) => { - console.log('Running dialog with Message Activity.'); - - // Run the Dialog with the new message Activity. - await this.dialog.run(context, this.dialogState); - - // By calling next() you ensure that the next BotHandler is run. - await next(); - }); - - this.onDialog(async (context, next) => { - // Save any state changes. The load happened during the execution of the Dialog. - await this.conversationState.saveChanges(context, false); - await this.userState.saveChanges(context, false); - - // By calling next() you ensure that the next BotHandler is run. - await next(); - }); - } -} - -module.exports.DialogBot = DialogBot; diff --git a/generators/generator-botbuilder/generators/app/templates/core/bots/dialogBot.ts b/generators/generator-botbuilder/generators/app/templates/core/bots/dialogBot.ts deleted file mode 100644 index 7b015b1f8c..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/bots/dialogBot.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { ActivityHandler, BotState, ConversationState, StatePropertyAccessor, UserState } from 'botbuilder'; -import { Dialog, DialogState } from 'botbuilder-dialogs'; -import { MainDialog } from '../dialogs/mainDialog'; - -export class DialogBot extends ActivityHandler { - private conversationState: BotState; - private userState: BotState; - private dialog: Dialog; - private dialogState: StatePropertyAccessor; - - /** - * - * @param {BotState} conversationState - * @param {BotState} userState - * @param {Dialog} dialog - */ - constructor(conversationState: BotState, userState: BotState, dialog: Dialog) { - super(); - if (!conversationState) { - throw new Error('[DialogBot]: Missing parameter. conversationState is required'); - } - if (!userState) { - throw new Error('[DialogBot]: Missing parameter. userState is required'); - } - if (!dialog) { - throw new Error('[DialogBot]: Missing parameter. dialog is required'); - } - - this.conversationState = conversationState as ConversationState; - this.userState = userState as UserState; - this.dialog = dialog; - this.dialogState = this.conversationState.createProperty('DialogState'); - - this.onMessage(async (context, next) => { - console.log('Running dialog with Message Activity.'); - - // Run the Dialog with the new message Activity. - await (this.dialog as MainDialog).run(context, this.dialogState); - - // By calling next() you ensure that the next BotHandler is run. - await next(); - }); - - this.onDialog(async (context, next) => { - // Save any state changes. The load happened during the execution of the Dialog. - await this.conversationState.saveChanges(context, false); - await this.userState.saveChanges(context, false); - - // By calling next() you ensure that the next BotHandler is run. - await next(); - }); - } -} diff --git a/generators/generator-botbuilder/generators/app/templates/core/cognitiveModels/FlightBooking.json b/generators/generator-botbuilder/generators/app/templates/core/cognitiveModels/FlightBooking.json deleted file mode 100644 index d44c61eb95..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/cognitiveModels/FlightBooking.json +++ /dev/null @@ -1,339 +0,0 @@ -{ - "luis_schema_version": "3.2.0", - "versionId": "0.1", - "name": "FlightBooking", - "desc": "Luis Model for CoreBot", - "culture": "en-us", - "tokenizerVersion": "1.0.0", - "intents": [ - { - "name": "BookFlight" - }, - { - "name": "Cancel" - }, - { - "name": "GetWeather" - }, - { - "name": "None" - } - ], - "entities": [], - "composites": [ - { - "name": "From", - "children": [ - "Airport" - ], - "roles": [] - }, - { - "name": "To", - "children": [ - "Airport" - ], - "roles": [] - } - ], - "closedLists": [ - { - "name": "Airport", - "subLists": [ - { - "canonicalForm": "Paris", - "list": [ - "paris", - "cdg" - ] - }, - { - "canonicalForm": "London", - "list": [ - "london", - "lhr" - ] - }, - { - "canonicalForm": "Berlin", - "list": [ - "berlin", - "txl" - ] - }, - { - "canonicalForm": "New York", - "list": [ - "new york", - "jfk" - ] - }, - { - "canonicalForm": "Seattle", - "list": [ - "seattle", - "sea" - ] - } - ], - "roles": [] - } - ], - "patternAnyEntities": [], - "regex_entities": [], - "prebuiltEntities": [ - { - "name": "datetimeV2", - "roles": [] - } - ], - "model_features": [], - "regex_features": [], - "patterns": [], - "utterances": [ - { - "text": "book a flight", - "intent": "BookFlight", - "entities": [] - }, - { - "text": "book a flight from new york", - "intent": "BookFlight", - "entities": [ - { - "entity": "From", - "startPos": 19, - "endPos": 26 - } - ] - }, - { - "text": "book a flight from seattle", - "intent": "BookFlight", - "entities": [ - { - "entity": "From", - "startPos": 19, - "endPos": 25 - } - ] - }, - { - "text": "book a hotel in new york", - "intent": "None", - "entities": [] - }, - { - "text": "book a restaurant", - "intent": "None", - "entities": [] - }, - { - "text": "book flight from london to paris on feb 14th", - "intent": "BookFlight", - "entities": [ - { - "entity": "From", - "startPos": 17, - "endPos": 22 - }, - { - "entity": "To", - "startPos": 27, - "endPos": 31 - } - ] - }, - { - "text": "book flight to berlin on feb 14th", - "intent": "BookFlight", - "entities": [ - { - "entity": "To", - "startPos": 15, - "endPos": 20 - } - ] - }, - { - "text": "book me a flight from london to paris", - "intent": "BookFlight", - "entities": [ - { - "entity": "From", - "startPos": 22, - "endPos": 27 - }, - { - "entity": "To", - "startPos": 32, - "endPos": 36 - } - ] - }, - { - "text": "bye", - "intent": "Cancel", - "entities": [] - }, - { - "text": "cancel booking", - "intent": "Cancel", - "entities": [] - }, - { - "text": "exit", - "intent": "Cancel", - "entities": [] - }, - { - "text": "find an airport near me", - "intent": "None", - "entities": [] - }, - { - "text": "flight to paris", - "intent": "BookFlight", - "entities": [ - { - "entity": "To", - "startPos": 10, - "endPos": 14 - } - ] - }, - { - "text": "flight to paris from london on feb 14th", - "intent": "BookFlight", - "entities": [ - { - "entity": "To", - "startPos": 10, - "endPos": 14 - }, - { - "entity": "From", - "startPos": 21, - "endPos": 26 - } - ] - }, - { - "text": "fly from berlin to paris on may 5th", - "intent": "BookFlight", - "entities": [ - { - "entity": "From", - "startPos": 9, - "endPos": 14 - }, - { - "entity": "To", - "startPos": 19, - "endPos": 23 - } - ] - }, - { - "text": "go to paris", - "intent": "BookFlight", - "entities": [ - { - "entity": "To", - "startPos": 6, - "endPos": 10 - } - ] - }, - { - "text": "going from paris to berlin", - "intent": "BookFlight", - "entities": [ - { - "entity": "From", - "startPos": 11, - "endPos": 15 - }, - { - "entity": "To", - "startPos": 20, - "endPos": 25 - } - ] - }, - { - "text": "i'd like to rent a car", - "intent": "None", - "entities": [] - }, - { - "text": "ignore", - "intent": "Cancel", - "entities": [] - }, - { - "text": "travel from new york to paris", - "intent": "BookFlight", - "entities": [ - { - "entity": "From", - "startPos": 12, - "endPos": 19 - }, - { - "entity": "To", - "startPos": 24, - "endPos": 28 - } - ] - }, - { - "text": "travel to new york", - "intent": "BookFlight", - "entities": [ - { - "entity": "To", - "startPos": 10, - "endPos": 17 - } - ] - }, - { - "text": "travel to paris", - "intent": "BookFlight", - "entities": [ - { - "entity": "To", - "startPos": 10, - "endPos": 14 - } - ] - }, - { - "text": "what's the forecast for this friday?", - "intent": "GetWeather", - "entities": [] - }, - { - "text": "what's the weather like for tomorrow", - "intent": "GetWeather", - "entities": [] - }, - { - "text": "what's the weather like in new york", - "intent": "GetWeather", - "entities": [] - }, - { - "text": "what's the weather like?", - "intent": "GetWeather", - "entities": [] - }, - { - "text": "winter is coming", - "intent": "None", - "entities": [] - } - ], - "settings": [] -} diff --git a/generators/generator-botbuilder/generators/app/templates/core/deploymentScripts/webConfigPrep.js b/generators/generator-botbuilder/generators/app/templates/core/deploymentScripts/webConfigPrep.js deleted file mode 100644 index 25191ee0f9..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/deploymentScripts/webConfigPrep.js +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -// DO NOT MODIFY THIS CODE -// This script is run as part of the Post Deploy step when -// deploying the bot to Azure. It ensures the Azure Web App -// is configured correctly to host a TypeScript authored bot. -const fs = require('fs'); -const path = require('path'); -const replace = require('replace'); -const WEB_CONFIG_FILE = './web.config'; - -if (fs.existsSync(path.resolve(WEB_CONFIG_FILE))) { - replace({ - regex: "url=\"index.js\"", - replacement: "url=\"lib/index.js\"", - paths: ['./web.config'], - recursive: false, - silent: true, - }) -} diff --git a/generators/generator-botbuilder/generators/app/templates/core/deploymentTemplates/deployUseExistResourceGroup/parameters-for-template-AzureBot-with-rg.json b/generators/generator-botbuilder/generators/app/templates/core/deploymentTemplates/deployUseExistResourceGroup/parameters-for-template-AzureBot-with-rg.json deleted file mode 100644 index c2c03ef307..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/deploymentTemplates/deployUseExistResourceGroup/parameters-for-template-AzureBot-with-rg.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "azureBotId": { - "value": "" - }, - "azureBotSku": { - "value": "S1" - }, - "azureBotRegion": { - "value": "global" - }, - "botEndpoint": { - "value": "" - }, - "appType": { - "value": "MultiTenant" - }, - "appId": { - "value": "" - }, - "UMSIName": { - "value": "" - }, - "UMSIResourceGroupName": { - "value": "" - }, - "tenantId": { - "value": "" - } - } -} \ No newline at end of file diff --git a/generators/generator-botbuilder/generators/app/templates/core/deploymentTemplates/deployUseExistResourceGroup/parameters-for-template-BotApp-with-rg.json b/generators/generator-botbuilder/generators/app/templates/core/deploymentTemplates/deployUseExistResourceGroup/parameters-for-template-BotApp-with-rg.json deleted file mode 100644 index c4b2909008..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/deploymentTemplates/deployUseExistResourceGroup/parameters-for-template-BotApp-with-rg.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "appServiceName": { - "value": "" - }, - "existingAppServicePlanName": { - "value": "" - }, - "existingAppServicePlanLocation": { - "value": "" - }, - "newAppServicePlanName": { - "value": "" - }, - "newAppServicePlanLocation": { - "value": "" - }, - "newAppServicePlanSku": { - "value": { - "name": "S1", - "tier": "Standard", - "size": "S1", - "family": "S", - "capacity": 1 - } - }, - "appType": { - "value": "MultiTenant" - }, - "appId": { - "value": "" - }, - "appSecret": { - "value": "" - }, - "UMSIName": { - "value": "" - }, - "UMSIResourceGroupName": { - "value": "" - }, - "tenantId": { - "value": "" - } - } -} \ No newline at end of file diff --git a/generators/generator-botbuilder/generators/app/templates/core/deploymentTemplates/deployUseExistResourceGroup/readme.md b/generators/generator-botbuilder/generators/app/templates/core/deploymentTemplates/deployUseExistResourceGroup/readme.md deleted file mode 100644 index 628f0a9546..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/deploymentTemplates/deployUseExistResourceGroup/readme.md +++ /dev/null @@ -1,48 +0,0 @@ -# Usage -The BotApp must be deployed prior to AzureBot. - -Command line: -- az login -- az deployment group create --resource-group --template-file --parameters @ - -# parameters-for-template-BotApp-with-rg: - -- **appServiceName**:(required) The Name of the Bot App Service. - -- (choose an existingAppServicePlan or create a new AppServicePlan) - - **existingAppServicePlanName**: The name of the App Service Plan. - - **existingAppServicePlanLocation**: The location of the App Service Plan. - - **newAppServicePlanName**: The name of the App Service Plan. - - **newAppServicePlanLocation**: The location of the App Service Plan. - - **newAppServicePlanSku**: The SKU of the App Service Plan. Defaults to Standard values. - -- **appType**: Type of Bot Authentication. set as MicrosoftAppType in the Web App's Application Settings. **Allowed values are: MultiTenant(default), SingleTenant, UserAssignedMSI.** - -- **appId**:(required) Active Directory App ID or User-Assigned Managed Identity Client ID, set as MicrosoftAppId in the Web App's Application Settings. - -- **appSecret**:(required for MultiTenant and SingleTenant) Active Directory App Password, set as MicrosoftAppPassword in the Web App's Application Settings. - -- **UMSIName**:(required for UserAssignedMSI) The User-Assigned Managed Identity Resource used for the Bot's Authentication. - -- **UMSIResourceGroupName**:(required for UserAssignedMSI) The User-Assigned Managed Identity Resource Group used for the Bot's Authentication. - -- **tenantId**: The Azure AD Tenant ID to use as part of the Bot's Authentication. Only used for SingleTenant and UserAssignedMSI app types. Defaults to . - -MoreInfo: https://docs.microsoft.com/en-us/azure/bot-service/tutorial-provision-a-bot?view=azure-bot-service-4.0&tabs=userassigned%2Cnewgroup#create-an-identity-resource - - - -# parameters-for-template-AzureBot-with-rg: - -- **azureBotId**:(required) The globally unique and immutable bot ID. -- **azureBotSku**: The pricing tier of the Bot Service Registration. **Allowed values are: F0, S1(default)**. -- **azureBotRegion**: Specifies the location of the new AzureBot. **Allowed values are: global(default), westeurope**. -- **botEndpoint**: Use to handle client messages, Such as https://.azurewebsites.net/api/messages. - -- **appType**: Type of Bot Authentication. set as MicrosoftAppType in the Web App's Application Settings. **Allowed values are: MultiTenant(default), SingleTenant, UserAssignedMSI.** -- **appId**:(required) Active Directory App ID or User-Assigned Managed Identity Client ID, set as MicrosoftAppId in the Web App's Application Settings. -- **UMSIName**:(required for UserAssignedMSI) The User-Assigned Managed Identity Resource used for the Bot's Authentication. -- **UMSIResourceGroupName**:(required for UserAssignedMSI) The User-Assigned Managed Identity Resource Group used for the Bot's Authentication. -- **tenantId**: The Azure AD Tenant ID to use as part of the Bot's Authentication. Only used for SingleTenant and UserAssignedMSI app types. Defaults to . - -MoreInfo: https://docs.microsoft.com/en-us/azure/bot-service/tutorial-provision-a-bot?view=azure-bot-service-4.0&tabs=userassigned%2Cnewgroup#create-an-identity-resource \ No newline at end of file diff --git a/generators/generator-botbuilder/generators/app/templates/core/deploymentTemplates/deployUseExistResourceGroup/template-AzureBot-with-rg.json b/generators/generator-botbuilder/generators/app/templates/core/deploymentTemplates/deployUseExistResourceGroup/template-AzureBot-with-rg.json deleted file mode 100644 index 103ca19e27..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/deploymentTemplates/deployUseExistResourceGroup/template-AzureBot-with-rg.json +++ /dev/null @@ -1,119 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "azureBotId": { - "type": "string", - "metadata": { - "description": "The globally unique and immutable bot ID." - } - }, - "azureBotSku": { - "defaultValue": "S1", - "type": "string", - "metadata": { - "description": "The pricing tier of the Bot Service Registration. Allowed values are: F0, S1(default)." - } - }, - "azureBotRegion": { - "type": "string", - "defaultValue": "global", - "metadata": { - "description": "Specifies the location of the new AzureBot. Allowed values are: global(default), westeurope." - } - }, - "botEndpoint": { - "type": "string", - "metadata": { - "description": "Use to handle client messages, Such as https://.azurewebsites.net/api/messages." - } - }, - "appType": { - "type": "string", - "defaultValue": "MultiTenant", - "allowedValues": [ - "MultiTenant", - "SingleTenant", - "UserAssignedMSI" - ], - "metadata": { - "description": "Type of Bot Authentication. set as MicrosoftAppType in the Web App's Application Settings. Allowed values are: MultiTenant, SingleTenant, UserAssignedMSI. Defaults to \"MultiTenant\"." - } - }, - "appId": { - "type": "string", - "metadata": { - "description": "Active Directory App ID or User-Assigned Managed Identity Client ID, set as MicrosoftAppId in the Web App's Application Settings." - } - }, - "UMSIName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The User-Assigned Managed Identity Resource used for the Bot's Authentication." - } - }, - "UMSIResourceGroupName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The User-Assigned Managed Identity Resource Group used for the Bot's Authentication." - } - }, - "tenantId": { - "type": "string", - "defaultValue": "[subscription().tenantId]", - "metadata": { - "description": "The Azure AD Tenant ID to use as part of the Bot's Authentication. Only used for SingleTenant and UserAssignedMSI app types. Defaults to \"Subscription Tenant ID\"." - } - } - }, - "variables": { - "tenantId": "[if(empty(parameters('tenantId')), subscription().tenantId, parameters('tenantId'))]", - "msiResourceId": "[concat(subscription().id, '/resourceGroups/', parameters('UMSIResourceGroupName'), '/providers/', 'Microsoft.ManagedIdentity/userAssignedIdentities/', parameters('UMSIName'))]", - "appTypeDef": { - "MultiTenant": { - "tenantId": "", - "msiResourceId": "" - }, - "SingleTenant": { - "tenantId": "[variables('tenantId')]", - "msiResourceId": "" - }, - "UserAssignedMSI": { - "tenantId": "[variables('tenantId')]", - "msiResourceId": "[variables('msiResourceId')]" - } - }, - "appType": { - "tenantId": "[variables('appTypeDef')[parameters('appType')].tenantId]", - "msiResourceId": "[variables('appTypeDef')[parameters('appType')].msiResourceId]" - } - }, - "resources": [ - { - "apiVersion": "2021-05-01-preview", - "type": "Microsoft.BotService/botServices", - "name": "[parameters('azureBotId')]", - "location": "[parameters('azureBotRegion')]", - "kind": "azurebot", - "sku": { - "name": "[parameters('azureBotSku')]" - }, - "properties": { - "displayName": "[parameters('azureBotId')]", - "iconUrl": "https://docs.botframework.com/static/devportal/client/images/bot-framework-default.png", - "endpoint": "[parameters('botEndpoint')]", - "msaAppId": "[parameters('appId')]", - "msaAppTenantId": "[variables('appType').tenantId]", - "msaAppMSIResourceId": "[variables('appType').msiResourceId]", - "msaAppType": "[parameters('appType')]", - "luisAppIds": [], - "schemaTransformationVersion": "1.3", - "isCmekEnabled": false, - "isIsolated": false - }, - "dependsOn": [] - } - ] -} \ No newline at end of file diff --git a/generators/generator-botbuilder/generators/app/templates/core/deploymentTemplates/deployUseExistResourceGroup/template-BotApp-with-rg.json b/generators/generator-botbuilder/generators/app/templates/core/deploymentTemplates/deployUseExistResourceGroup/template-BotApp-with-rg.json deleted file mode 100644 index be94279ecc..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/deploymentTemplates/deployUseExistResourceGroup/template-BotApp-with-rg.json +++ /dev/null @@ -1,189 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "appServiceName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The globally unique name of the Web App." - } - }, - "existingAppServicePlanName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Name of the existing App Service Plan used to create the Web App for the bot." - } - }, - "existingAppServicePlanLocation": { - "type": "string", - "metadata": { - "description": "The location of the App Service Plan." - } - }, - "newAppServicePlanName": { - "type": "string", - "metadata": { - "description": "The name of the new App Service Plan." - } - }, - "newAppServicePlanLocation": { - "type": "string", - "metadata": { - "description": "The location of the App Service Plan." - } - }, - "newAppServicePlanSku": { - "type": "object", - "defaultValue": { - "name": "S1", - "tier": "Standard", - "size": "S1", - "family": "S", - "capacity": 1 - }, - "metadata": { - "description": "The SKU of the App Service Plan. Defaults to Standard values." - } - }, - "appType": { - "type": "string", - "defaultValue": "MultiTenant", - "allowedValues": [ - "MultiTenant", - "SingleTenant", - "UserAssignedMSI" - ], - "metadata": { - "description": "Type of Bot Authentication. set as MicrosoftAppType in the Web App's Application Settings. Allowed values are: MultiTenant, SingleTenant, UserAssignedMSI. Defaults to \"MultiTenant\"." - } - }, - "appId": { - "type": "string", - "metadata": { - "description": "Active Directory App ID or User-Assigned Managed Identity Client ID, set as MicrosoftAppId in the Web App's Application Settings." - } - }, - "appSecret": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Active Directory App Password, set as MicrosoftAppPassword in the Web App's Application Settings. Required for MultiTenant and SingleTenant app types. Defaults to \"\"." - } - }, - "UMSIName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The User-Assigned Managed Identity Resource used for the Bot's Authentication. Defaults to \"\"." - } - }, - "UMSIResourceGroupName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The User-Assigned Managed Identity Resource Group used for the Bot's Authentication. Defaults to \"\"." - } - }, - "tenantId": { - "type": "string", - "defaultValue": "[subscription().tenantId]", - "metadata": { - "description": "The Azure AD Tenant ID to use as part of the Bot's Authentication. Only used for SingleTenant and UserAssignedMSI app types. Defaults to \"Subscription Tenant ID\"." - } - } - }, - "variables": { - "tenantId": "[if(empty(parameters('tenantId')), subscription().tenantId, parameters('tenantId'))]", - "defaultAppServicePlanName": "[if(empty(parameters('existingAppServicePlanName')), 'createNewAppServicePlan', parameters('existingAppServicePlanName'))]", - "useExistingServicePlan": "[not(equals(variables('defaultAppServicePlanName'), 'createNewAppServicePlan'))]", - "servicePlanName": "[if(variables('useExistingServicePlan'), parameters('existingAppServicePlanName'), parameters('newAppServicePlanName'))]", - "servicePlanLocation": "[if(variables('useExistingServicePlan'), parameters('existingAppServicePlanLocation'), parameters('newAppServicePlanLocation'))]", - "msiResourceId": "[concat(subscription().id, '/resourceGroups/', parameters('UMSIResourceGroupName'), '/providers/', 'Microsoft.ManagedIdentity/userAssignedIdentities/', parameters('UMSIName'))]", - "appTypeDef": { - "MultiTenant": { - "tenantId": "", - "identity": { "type": "None" } - }, - "SingleTenant": { - "tenantId": "[variables('tenantId')]", - "identity": { "type": "None" } - }, - "UserAssignedMSI": { - "tenantId": "[variables('tenantId')]", - "identity": { - "type": "UserAssigned", - "userAssignedIdentities": { - "[variables('msiResourceId')]": {} - } - } - } - }, - "appType": { - "tenantId": "[variables('appTypeDef')[parameters('appType')].tenantId]", - "identity": "[variables('appTypeDef')[parameters('appType')].identity]" - } - }, - "resources": [ - { - "comments": "Create a new App Service Plan if no existing App Service Plan name was passed in.", - "type": "Microsoft.Web/serverfarms", - "condition": "[not(variables('useExistingServicePlan'))]", - "name": "[variables('servicePlanName')]", - "apiVersion": "2018-02-01", - "location": "[parameters('newAppServicePlanLocation')]", - "sku": "[parameters('newAppServicePlanSku')]", - "properties": { - "name": "[variables('servicePlanName')]" - } - }, - { - "comments": "Create a Web App using an App Service Plan", - "type": "Microsoft.Web/sites", - "apiVersion": "2015-08-01", - "location": "[variables('servicePlanLocation')]", - "kind": "app", - "dependsOn": [ - "[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]" - ], - "name": "[parameters('appServiceName')]", - "identity": "[variables('appType').identity]", - "properties": { - "name": "[parameters('appServiceName')]", - "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]", - "siteConfig": { - "appSettings": [ - { - "name": "WEBSITE_NODE_DEFAULT_VERSION", - "value": "10.14.1" - }, - { - "name": "MicrosoftAppType", - "value": "[parameters('appType')]" - }, - { - "name": "MicrosoftAppId", - "value": "[parameters('appId')]" - }, - { - "name": "MicrosoftAppPassword", - "value": "[parameters('appSecret')]" - }, - { - "name": "MicrosoftAppTenantId", - "value": "[variables('appType').tenantId]" - } - ], - "cors": { - "allowedOrigins": [ - "https://botservice.hosting.portal.azure.net", - "https://hosting.onecloud.azure-test.net/" - ] - }, - "webSocketsEnabled": true - } - } - } - ] -} \ No newline at end of file diff --git a/generators/generator-botbuilder/generators/app/templates/core/deploymentTemplates/deployWithNewResourceGroup/parameters-for-template-AzureBot-new-rg.json b/generators/generator-botbuilder/generators/app/templates/core/deploymentTemplates/deployWithNewResourceGroup/parameters-for-template-AzureBot-new-rg.json deleted file mode 100644 index 44f169e4d5..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/deploymentTemplates/deployWithNewResourceGroup/parameters-for-template-AzureBot-new-rg.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "groupName": { - "value": "" - }, - "groupLocation": { - "value": "" - }, - "azureBotId": { - "value": "" - }, - "azureBotSku": { - "value": "S1" - }, - "azureBotRegion": { - "value": "global" - }, - "botEndpoint": { - "value": "" - }, - "appType": { - "value": "MultiTenant" - }, - "appId": { - "value": "" - }, - "UMSIName": { - "value": "" - }, - "UMSIResourceGroupName": { - "value": "" - }, - "tenantId": { - "value": "" - } - } -} \ No newline at end of file diff --git a/generators/generator-botbuilder/generators/app/templates/core/deploymentTemplates/deployWithNewResourceGroup/parameters-for-template-BotApp-new-rg.json b/generators/generator-botbuilder/generators/app/templates/core/deploymentTemplates/deployWithNewResourceGroup/parameters-for-template-BotApp-new-rg.json deleted file mode 100644 index 8abb03d597..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/deploymentTemplates/deployWithNewResourceGroup/parameters-for-template-BotApp-new-rg.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "groupName": { - "value": "" - }, - "groupLocation": { - "value": "" - }, - "appServiceName": { - "value": "" - }, - "appServicePlanName": { - "value": "" - }, - "appServicePlanLocation": { - "value": "" - }, - "appServicePlanSku": { - "value": { - "name": "S1", - "tier": "Standard", - "size": "S1", - "family": "S", - "capacity": 1 - } - }, - "appType": { - "value": "MultiTenant" - }, - "appId": { - "value": "" - }, - "appSecret": { - "value": "" - }, - "UMSIName": { - "value": "" - }, - "UMSIResourceGroupName": { - "value": "" - }, - "tenantId": { - "value": "" - } - } -} \ No newline at end of file diff --git a/generators/generator-botbuilder/generators/app/templates/core/deploymentTemplates/deployWithNewResourceGroup/readme.md b/generators/generator-botbuilder/generators/app/templates/core/deploymentTemplates/deployWithNewResourceGroup/readme.md deleted file mode 100644 index 23bf7a5a51..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/deploymentTemplates/deployWithNewResourceGroup/readme.md +++ /dev/null @@ -1,45 +0,0 @@ -# Usage -The BotApp must be deployed prior to AzureBot. - -Command line: -- az login -- az deployment sub create --template-file --location --parameters @ - -# parameters-for-template-BotApp-new-rg: - -- **groupName**:(required) Specifies the name of the new Resource Group. -- **groupLocation**:(required) Specifies the location of the new Resource Group. - -- **appServiceName**:(required) The location of the App Service Plan. -- **appServicePlanName**:(required) The name of the App Service Plan. -- **appServicePlanLocation**: The location of the App Service Plan. Defaults to use groupLocation. -- **appServicePlanSku**: The SKU of the App Service Plan. Defaults to Standard values. - -- **appType**: Type of Bot Authentication. set as MicrosoftAppType in the Web App's Application Settings. **Allowed values are: MultiTenant(default), SingleTenant, UserAssignedMSI.** -- **appId**:(required) Active Directory App ID or User-Assigned Managed Identity Client ID, set as MicrosoftAppId in the Web App's Application Settings. -- **appSecret**:(required for MultiTenant and SingleTenant) Active Directory App Password, set as MicrosoftAppPassword in the Web App's Application Settings. -- **UMSIName**:(required for UserAssignedMSI) The User-Assigned Managed Identity Resource used for the Bot's Authentication. -- **UMSIResourceGroupName**:(required for UserAssignedMSI) The User-Assigned Managed Identity Resource Group used for the Bot's Authentication. -- **tenantId**: The Azure AD Tenant ID to use as part of the Bot's Authentication. Only used for SingleTenant and UserAssignedMSI app types. Defaults to . - -MoreInfo: https://docs.microsoft.com/en-us/azure/bot-service/tutorial-provision-a-bot?view=azure-bot-service-4.0&tabs=userassigned%2Cnewgroup#create-an-identity-resource - - - -# parameters-for-template-AzureBot-new-rg: - -- **groupName**:(required) Specifies the name of the new Resource Group. -- **groupLocation**:(required) Specifies the location of the new Resource Group. - -- **azureBotId**:(required) The globally unique and immutable bot ID. Also used to configure the displayName of the bot, which is mutable. -- **azureBotSku**: The pricing tier of the Bot Service Registration. **Allowed values are: F0, S1(default)**. -- **azureBotRegion**: Specifies the location of the new AzureBot. **Allowed values are: global(default), westeurope**. -- **botEndpoint**: Use to handle client messages, Such as https://.azurewebsites.net/api/messages. - -- **appType**: Type of Bot Authentication. set as MicrosoftAppType in the Web App's Application Settings. **Allowed values are: MultiTenant(default), SingleTenant, UserAssignedMSI.** -- **appId**:(required) Active Directory App ID or User-Assigned Managed Identity Client ID, set as MicrosoftAppId in the Web App's Application Settings. -- **UMSIName**:(required for UserAssignedMSI) The User-Assigned Managed Identity Resource used for the Bot's Authentication. -- **UMSIResourceGroupName**:(required for UserAssignedMSI) The User-Assigned Managed Identity Resource Group used for the Bot's Authentication. -- **tenantId**: The Azure AD Tenant ID to use as part of the Bot's Authentication. Only used for SingleTenant and UserAssignedMSI app types. Defaults to . - -MoreInfo: https://docs.microsoft.com/en-us/azure/bot-service/tutorial-provision-a-bot?view=azure-bot-service-4.0&tabs=userassigned%2Cnewgroup#create-an-identity-resource \ No newline at end of file diff --git a/generators/generator-botbuilder/generators/app/templates/core/deploymentTemplates/deployWithNewResourceGroup/template-AzureBot-new-rg.json b/generators/generator-botbuilder/generators/app/templates/core/deploymentTemplates/deployWithNewResourceGroup/template-AzureBot-new-rg.json deleted file mode 100644 index da68591251..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/deploymentTemplates/deployWithNewResourceGroup/template-AzureBot-new-rg.json +++ /dev/null @@ -1,157 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "groupName": { - "type": "string", - "metadata": { - "description": "Specifies the name of the Resource Group." - } - }, - "groupLocation": { - "type": "string", - "metadata": { - "description": "Specifies the location of the Resource Group." - } - }, - "azureBotId": { - "type": "string", - "metadata": { - "description": "The globally unique and immutable bot ID." - } - }, - "azureBotSku": { - "type": "string", - "defaultValue": "S1", - "metadata": { - "description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1." - } - }, - "azureBotRegion": { - "type": "string", - "defaultValue": "global", - "metadata": { - "description": "" - } - }, - "botEndpoint": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Use to handle client messages, Such as https://.azurewebsites.net/api/messages." - } - }, - "appType": { - "type": "string", - "defaultValue": "MultiTenant", - "allowedValues": [ - "MultiTenant", - "SingleTenant", - "UserAssignedMSI" - ], - "metadata": { - "description": "Type of Bot Authentication. set as MicrosoftAppType in the Web App's Application Settings. Allowed values are: MultiTenant, SingleTenant, UserAssignedMSI. Defaults to \"MultiTenant\"." - } - }, - "appId": { - "type": "string", - "metadata": { - "description": "Active Directory App ID or User-Assigned Managed Identity Client ID, set as MicrosoftAppId in the Web App's Application Settings." - } - }, - "tenantId": { - "type": "string", - "defaultValue": "[subscription().tenantId]", - "metadata": { - "description": "The Azure AD Tenant ID to use as part of the Bot's Authentication. Only used for SingleTenant and UserAssignedMSI app types. Defaults to \"Subscription Tenant ID\"." - } - }, - "UMSIName": { - "type": "string", - "metadata": { - "description": "The User-Assigned Managed Identity Resource used for the Bot's Authentication." - } - }, - "UMSIResourceGroupName": { - "type": "string", - "metadata": { - "description": "The User-Assigned Managed Identity Resource Group used for the Bot's Authentication." - } - } - }, - "variables": { - "tenantId": "[if(empty(parameters('tenantId')), subscription().tenantId, parameters('tenantId'))]", - "msiResourceId": "[concat(subscription().id, '/resourceGroups/', parameters('UMSIResourceGroupName'), '/providers/', 'Microsoft.ManagedIdentity/userAssignedIdentities/', parameters('UMSIName'))]", - "appTypeDef": { - "MultiTenant": { - "tenantId": "", - "msiResourceId": "" - }, - "SingleTenant": { - "tenantId": "[variables('tenantId')]", - "msiResourceId": "" - }, - "UserAssignedMSI": { - "tenantId": "[variables('tenantId')]", - "msiResourceId": "[variables('msiResourceId')]" - } - }, - "appType": { - "tenantId": "[variables('appTypeDef')[parameters('appType')].tenantId]", - "msiResourceId": "[variables('appTypeDef')[parameters('appType')].msiResourceId]" - } - }, - "resources": [ - { - "name": "[parameters('groupName')]", - "type": "Microsoft.Resources/resourceGroups", - "apiVersion": "2018-05-01", - "location": "[parameters('groupLocation')]", - "properties": {} - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2018-05-01", - "name": "storageDeployment", - "resourceGroup": "[parameters('groupName')]", - "dependsOn": [ - "[resourceId('Microsoft.Resources/resourceGroups/', parameters('groupName'))]" - ], - "properties": { - "mode": "Incremental", - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "parameters": {}, - "variables": {}, - "resources": [ - { - "apiVersion": "2021-03-01", - "type": "Microsoft.BotService/botServices", - "name": "[parameters('azureBotId')]", - "location": "[parameters('azureBotRegion')]", - "kind": "azurebot", - "sku": { - "name": "[parameters('azureBotSku')]" - }, - "properties": { - "name": "[parameters('azureBotId')]", - "displayName": "[parameters('azureBotId')]", - "iconUrl": "https://docs.botframework.com/static/devportal/client/images/bot-framework-default.png", - "endpoint": "[parameters('botEndpoint')]", - "msaAppId": "[parameters('appId')]", - "msaAppTenantId": "[variables('appType').tenantId]", - "msaAppMSIResourceId": "[variables('appType').msiResourceId]", - "msaAppType": "[parameters('appType')]", - "luisAppIds": [], - "schemaTransformationVersion": "1.3", - "isCmekEnabled": false, - "isIsolated": false - } - } - ] - } - } - } - ] -} \ No newline at end of file diff --git a/generators/generator-botbuilder/generators/app/templates/core/deploymentTemplates/deployWithNewResourceGroup/template-BotApp-new-rg.json b/generators/generator-botbuilder/generators/app/templates/core/deploymentTemplates/deployWithNewResourceGroup/template-BotApp-new-rg.json deleted file mode 100644 index ad06507010..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/deploymentTemplates/deployWithNewResourceGroup/template-BotApp-new-rg.json +++ /dev/null @@ -1,211 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "groupName": { - "type": "string", - "metadata": { - "description": "Specifies the name of the Resource Group." - } - }, - "groupLocation": { - "type": "string", - "metadata": { - "description": "Specifies the location of the Resource Group." - } - }, - "appServiceName": { - "type": "string", - "metadata": { - "description": "The globally unique name of the Web App." - } - }, - "appServicePlanName": { - "type": "string", - "metadata": { - "description": "The name of the App Service Plan." - } - }, - "appServicePlanLocation": { - "type": "string", - "metadata": { - "description": "The location of the App Service Plan." - } - }, - "appServicePlanSku": { - "type": "object", - "defaultValue": { - "name": "S1", - "tier": "Standard", - "size": "S1", - "family": "S", - "capacity": 1 - }, - "metadata": { - "description": "The SKU of the App Service Plan. Defaults to Standard values." - } - }, - "tenantId": { - "type": "string", - "defaultValue": "[subscription().tenantId]", - "metadata": { - "description": "The Azure AD Tenant ID to use as part of the Bot's Authentication. Only used for SingleTenant and UserAssignedMSI app types. Defaults to \"Subscription Tenant ID\"." - } - }, - "appType": { - "type": "string", - "defaultValue": "MultiTenant", - "allowedValues": [ - "MultiTenant", - "SingleTenant", - "UserAssignedMSI" - ], - "metadata": { - "description": "Type of Bot Authentication. set as MicrosoftAppType in the Web App's Application Settings. Allowed values are: MultiTenant, SingleTenant, UserAssignedMSI. Defaults to \"MultiTenant\"." - } - }, - "appId": { - "type": "string", - "metadata": { - "description": "Active Directory App ID or User-Assigned Managed Identity Client ID, set as MicrosoftAppId in the Web App's Application Settings." - } - }, - "appSecret": { - "type": "string", - "metadata": { - "description": "Active Directory App Password, set as MicrosoftAppPassword in the Web App's Application Settings. Required for MultiTenant and SingleTenant app types." - } - }, - "UMSIName": { - "type": "string", - "metadata": { - "description": "The User-Assigned Managed Identity Resource used for the Bot's Authentication." - } - }, - "UMSIResourceGroupName": { - "type": "string", - "metadata": { - "description": "The User-Assigned Managed Identity Resource Group used for the Bot's Authentication." - } - } - }, - "variables": { - "tenantId": "[if(empty(parameters('tenantId')), subscription().tenantId, parameters('tenantId'))]", - "appServicePlanName": "[parameters('appServicePlanName')]", - "resourcesLocation": "[if(empty(parameters('appServicePlanLocation')), parameters('groupLocation'), parameters('appServicePlanLocation'))]", - "appServiceName": "[parameters('appServiceName')]", - "resourceGroupId": "[concat(subscription().id, '/resourceGroups/', parameters('groupName'))]", - "msiResourceId": "[concat(subscription().id, '/resourceGroups/', parameters('UMSIResourceGroupName'), '/providers/', 'Microsoft.ManagedIdentity/userAssignedIdentities/', parameters('UMSIName'))]", - "appTypeDef": { - "MultiTenant": { - "tenantId": "", - "identity": { "type": "None" } - }, - "SingleTenant": { - "tenantId": "[variables('tenantId')]", - "identity": { "type": "None" } - }, - "UserAssignedMSI": { - "tenantId": "[variables('tenantId')]", - "identity": { - "type": "UserAssigned", - "userAssignedIdentities": { - "[variables('msiResourceId')]": {} - } - } - } - }, - "appType": { - "tenantId": "[variables('appTypeDef')[parameters('appType')].tenantId]", - "identity": "[variables('appTypeDef')[parameters('appType')].identity]" - } - }, - "resources": [ - { - "name": "[parameters('groupName')]", - "type": "Microsoft.Resources/resourceGroups", - "apiVersion": "2018-05-01", - "location": "[parameters('groupLocation')]", - "properties": {} - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2018-05-01", - "name": "storageDeployment", - "resourceGroup": "[parameters('groupName')]", - "dependsOn": [ - "[resourceId('Microsoft.Resources/resourceGroups/', parameters('groupName'))]" - ], - "properties": { - "mode": "Incremental", - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "parameters": {}, - "variables": {}, - "resources": [ - { - "comments": "Create a new App Service Plan", - "type": "Microsoft.Web/serverfarms", - "name": "[variables('appServicePlanName')]", - "apiVersion": "2018-02-01", - "location": "[variables('resourcesLocation')]", - "sku": "[parameters('appServicePlanSku')]", - "properties": { - "name": "[variables('appServicePlanName')]" - } - }, - { - "comments": "Create a Web App using the new App Service Plan", - "type": "Microsoft.Web/sites", - "apiVersion": "2015-08-01", - "location": "[variables('resourcesLocation')]", - "kind": "app", - "dependsOn": [ - "[concat(variables('resourceGroupId'), '/providers/Microsoft.Web/serverfarms/', variables('appServicePlanName'))]" - ], - "name": "[variables('appServiceName')]", - "identity": "[variables('appType').identity]", - "properties": { - "name": "[variables('appServiceName')]", - "serverFarmId": "[variables('appServicePlanName')]", - "siteConfig": { - "appSettings": [ - { - "name": "WEBSITE_NODE_DEFAULT_VERSION", - "value": "10.14.1" - }, - { - "name": "MicrosoftAppType", - "value": "[parameters('appType')]" - }, - { - "name": "MicrosoftAppId", - "value": "[parameters('appId')]" - }, - { - "name": "MicrosoftAppPassword", - "value": "[parameters('appSecret')]" - }, - { - "name": "MicrosoftAppTenantId", - "value": "[variables('appType').tenantId]" - } - ], - "cors": { - "allowedOrigins": [ - "https://botservice.hosting.portal.azure.net", - "https://hosting.onecloud.azure-test.net/" - ] - }, - "webSocketsEnabled": true - } - } - } - ], - "outputs": {} - } - } - } - ] -} \ No newline at end of file diff --git a/generators/generator-botbuilder/generators/app/templates/core/dialogs/bookingDetails.ts b/generators/generator-botbuilder/generators/app/templates/core/dialogs/bookingDetails.ts deleted file mode 100644 index 45259caa9d..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/dialogs/bookingDetails.ts +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -export class BookingDetails { - intent: string; - origin: string; - destination: string; - travelDate: string; -} diff --git a/generators/generator-botbuilder/generators/app/templates/core/dialogs/bookingDialog.js b/generators/generator-botbuilder/generators/app/templates/core/dialogs/bookingDialog.js deleted file mode 100644 index 1933a315f3..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/dialogs/bookingDialog.js +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const { TimexProperty } = require('@microsoft/recognizers-text-data-types-timex-expression'); -const { InputHints, MessageFactory } = require('botbuilder'); -const { ConfirmPrompt, TextPrompt, WaterfallDialog } = require('botbuilder-dialogs'); -const { CancelAndHelpDialog } = require('./cancelAndHelpDialog'); -const { DateResolverDialog } = require('./dateResolverDialog'); - -const CONFIRM_PROMPT = 'confirmPrompt'; -const DATE_RESOLVER_DIALOG = 'dateResolverDialog'; -const TEXT_PROMPT = 'textPrompt'; -const WATERFALL_DIALOG = 'waterfallDialog'; - -class BookingDialog extends CancelAndHelpDialog { - constructor(id) { - super(id || 'bookingDialog'); - - this.addDialog(new TextPrompt(TEXT_PROMPT)) - .addDialog(new ConfirmPrompt(CONFIRM_PROMPT)) - .addDialog(new DateResolverDialog(DATE_RESOLVER_DIALOG)) - .addDialog(new WaterfallDialog(WATERFALL_DIALOG, [ - this.destinationStep.bind(this), - this.originStep.bind(this), - this.travelDateStep.bind(this), - this.confirmStep.bind(this), - this.finalStep.bind(this) - ])); - - this.initialDialogId = WATERFALL_DIALOG; - } - - /** - * If a destination city has not been provided, prompt for one. - */ - async destinationStep(stepContext) { - const bookingDetails = stepContext.options; - - if (!bookingDetails.destination) { - const messageText = 'To what city would you like to travel?'; - const msg = MessageFactory.text(messageText, messageText, InputHints.ExpectingInput); - return await stepContext.prompt(TEXT_PROMPT, { prompt: msg }); - } - return await stepContext.next(bookingDetails.destination); - } - - /** - * If an origin city has not been provided, prompt for one. - */ - async originStep(stepContext) { - const bookingDetails = stepContext.options; - - // Capture the response to the previous step's prompt - bookingDetails.destination = stepContext.result; - if (!bookingDetails.origin) { - const messageText = 'From what city will you be travelling?'; - const msg = MessageFactory.text(messageText, 'From what city will you be travelling?', InputHints.ExpectingInput); - return await stepContext.prompt(TEXT_PROMPT, { prompt: msg }); - } - return await stepContext.next(bookingDetails.origin); - } - - /** - * If a travel date has not been provided, prompt for one. - * This will use the DATE_RESOLVER_DIALOG. - */ - async travelDateStep(stepContext) { - const bookingDetails = stepContext.options; - - // Capture the results of the previous step - bookingDetails.origin = stepContext.result; - if (!bookingDetails.travelDate || this.isAmbiguous(bookingDetails.travelDate)) { - return await stepContext.beginDialog(DATE_RESOLVER_DIALOG, { date: bookingDetails.travelDate }); - } - return await stepContext.next(bookingDetails.travelDate); - } - - /** - * Confirm the information the user has provided. - */ - async confirmStep(stepContext) { - const bookingDetails = stepContext.options; - - // Capture the results of the previous step - bookingDetails.travelDate = stepContext.result; - const messageText = `Please confirm, I have you traveling to: ${ bookingDetails.destination } from: ${ bookingDetails.origin } on: ${ bookingDetails.travelDate }. Is this correct?`; - const msg = MessageFactory.text(messageText, messageText, InputHints.ExpectingInput); - - // Offer a YES/NO prompt. - return await stepContext.prompt(CONFIRM_PROMPT, { prompt: msg }); - } - - /** - * Complete the interaction and end the dialog. - */ - async finalStep(stepContext) { - if (stepContext.result === true) { - const bookingDetails = stepContext.options; - return await stepContext.endDialog(bookingDetails); - } - return await stepContext.endDialog(); - } - - isAmbiguous(timex) { - const timexPropery = new TimexProperty(timex); - return !timexPropery.types.has('definite'); - } -} - -module.exports.BookingDialog = BookingDialog; diff --git a/generators/generator-botbuilder/generators/app/templates/core/dialogs/bookingDialog.ts b/generators/generator-botbuilder/generators/app/templates/core/dialogs/bookingDialog.ts deleted file mode 100644 index 63510c0e21..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/dialogs/bookingDialog.ts +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -import { TimexProperty } from '@microsoft/recognizers-text-data-types-timex-expression'; -import { InputHints, MessageFactory } from 'botbuilder'; -import { - ConfirmPrompt, - DialogTurnResult, - TextPrompt, - WaterfallDialog, - WaterfallStepContext -} from 'botbuilder-dialogs'; -import { BookingDetails } from './bookingDetails'; -import { CancelAndHelpDialog } from './cancelAndHelpDialog'; -import { DateResolverDialog } from './dateResolverDialog'; - -const CONFIRM_PROMPT = 'confirmPrompt'; -const DATE_RESOLVER_DIALOG = 'dateResolverDialog'; -const TEXT_PROMPT = 'textPrompt'; -const WATERFALL_DIALOG = 'waterfallDialog'; - -export class BookingDialog extends CancelAndHelpDialog { - constructor(id: string) { - super(id || 'bookingDialog'); - - this.addDialog(new TextPrompt(TEXT_PROMPT)) - .addDialog(new ConfirmPrompt(CONFIRM_PROMPT)) - .addDialog(new DateResolverDialog(DATE_RESOLVER_DIALOG)) - .addDialog(new WaterfallDialog(WATERFALL_DIALOG, [ - this.destinationStep.bind(this), - this.originStep.bind(this), - this.travelDateStep.bind(this), - this.confirmStep.bind(this), - this.finalStep.bind(this) - ])); - - this.initialDialogId = WATERFALL_DIALOG; - } - - /** - * If a destination city has not been provided, prompt for one. - */ - private async destinationStep(stepContext: WaterfallStepContext): Promise { - const bookingDetails = stepContext.options as BookingDetails; - - if (!bookingDetails.destination) { - const messageText = 'To what city would you like to travel?'; - const msg = MessageFactory.text(messageText, messageText, InputHints.ExpectingInput); - return await stepContext.prompt(TEXT_PROMPT, { prompt: msg }); - } else { - return await stepContext.next(bookingDetails.destination); - } - } - - /** - * If an origin city has not been provided, prompt for one. - */ - private async originStep(stepContext: WaterfallStepContext): Promise { - const bookingDetails = stepContext.options as BookingDetails; - - // Capture the response to the previous step's prompt - bookingDetails.destination = stepContext.result; - if (!bookingDetails.origin) { - const messageText = 'From what city will you be travelling?'; - const msg = MessageFactory.text(messageText, messageText, InputHints.ExpectingInput); - return await stepContext.prompt(TEXT_PROMPT, { prompt: msg }); - } else { - return await stepContext.next(bookingDetails.origin); - } - } - - /** - * If a travel date has not been provided, prompt for one. - * This will use the DATE_RESOLVER_DIALOG. - */ - private async travelDateStep(stepContext: WaterfallStepContext): Promise { - const bookingDetails = stepContext.options as BookingDetails; - - // Capture the results of the previous step - bookingDetails.origin = stepContext.result; - if (!bookingDetails.travelDate || this.isAmbiguous(bookingDetails.travelDate)) { - return await stepContext.beginDialog(DATE_RESOLVER_DIALOG, { date: bookingDetails.travelDate }); - } else { - return await stepContext.next(bookingDetails.travelDate); - } - } - - /** - * Confirm the information the user has provided. - */ - private async confirmStep(stepContext: WaterfallStepContext): Promise { - const bookingDetails = stepContext.options as BookingDetails; - - // Capture the results of the previous step - bookingDetails.travelDate = stepContext.result; - const messageText = `Please confirm, I have you traveling to: ${ bookingDetails.destination } from: ${ bookingDetails.origin } on: ${ bookingDetails.travelDate }. Is this correct?`; - const msg = MessageFactory.text(messageText, messageText, InputHints.ExpectingInput); - - // Offer a YES/NO prompt. - return await stepContext.prompt(CONFIRM_PROMPT, { prompt: msg }); - } - - /** - * Complete the interaction and end the dialog. - */ - private async finalStep(stepContext: WaterfallStepContext): Promise { - if (stepContext.result === true) { - const bookingDetails = stepContext.options as BookingDetails; - - return await stepContext.endDialog(bookingDetails); - } - return await stepContext.endDialog(); - } - - private isAmbiguous(timex: string): boolean { - const timexPropery = new TimexProperty(timex); - return !timexPropery.types.has('definite'); - } -} diff --git a/generators/generator-botbuilder/generators/app/templates/core/dialogs/cancelAndHelpDialog.js b/generators/generator-botbuilder/generators/app/templates/core/dialogs/cancelAndHelpDialog.js deleted file mode 100644 index b6bcf29877..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/dialogs/cancelAndHelpDialog.js +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const { InputHints } = require('botbuilder'); -const { ComponentDialog, DialogTurnStatus } = require('botbuilder-dialogs'); - -/** - * This base class watches for common phrases like "help" and "cancel" and takes action on them - * BEFORE they reach the normal bot logic. - */ -class CancelAndHelpDialog extends ComponentDialog { - async onContinueDialog(innerDc) { - const result = await this.interrupt(innerDc); - if (result) { - return result; - } - return await super.onContinueDialog(innerDc); - } - - async interrupt(innerDc) { - if (innerDc.context.activity.text) { - const text = innerDc.context.activity.text.toLowerCase(); - - switch (text) { - case 'help': - case '?': { - const helpMessageText = 'Show help here'; - await innerDc.context.sendActivity(helpMessageText, helpMessageText, InputHints.ExpectingInput); - return { status: DialogTurnStatus.waiting }; - } - case 'cancel': - case 'quit': { - const cancelMessageText = 'Cancelling...'; - await innerDc.context.sendActivity(cancelMessageText, cancelMessageText, InputHints.IgnoringInput); - return await innerDc.cancelAllDialogs(); - } - } - } - } -} - -module.exports.CancelAndHelpDialog = CancelAndHelpDialog; diff --git a/generators/generator-botbuilder/generators/app/templates/core/dialogs/cancelAndHelpDialog.ts b/generators/generator-botbuilder/generators/app/templates/core/dialogs/cancelAndHelpDialog.ts deleted file mode 100644 index ed590f6749..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/dialogs/cancelAndHelpDialog.ts +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -import { InputHints } from 'botbuilder'; -import { ComponentDialog, DialogContext, DialogTurnResult, DialogTurnStatus } from 'botbuilder-dialogs'; - -/** - * This base class watches for common phrases like "help" and "cancel" and takes action on them - * BEFORE they reach the normal bot logic. - */ -export class CancelAndHelpDialog extends ComponentDialog { - constructor(id: string) { - super(id); - } - - async onContinueDialog(innerDc: DialogContext): Promise { - const result = await this.interrupt(innerDc); - if (result) { - return result; - } - return await super.onContinueDialog(innerDc); - } - - private async interrupt(innerDc: DialogContext): Promise { - if (innerDc.context.activity.text) { - const text = innerDc.context.activity.text.toLowerCase(); - - switch (text) { - case 'help': - case '?': - const helpMessageText = 'Show help here'; - await innerDc.context.sendActivity(helpMessageText, helpMessageText, InputHints.ExpectingInput); - return { status: DialogTurnStatus.waiting }; - case 'cancel': - case 'quit': - const cancelMessageText = 'Cancelling...'; - await innerDc.context.sendActivity(cancelMessageText, cancelMessageText, InputHints.IgnoringInput); - return await innerDc.cancelAllDialogs(); - } - } - } -} diff --git a/generators/generator-botbuilder/generators/app/templates/core/dialogs/dateResolverDialog.js b/generators/generator-botbuilder/generators/app/templates/core/dialogs/dateResolverDialog.js deleted file mode 100644 index 1779c6cb07..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/dialogs/dateResolverDialog.js +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const { InputHints, MessageFactory } = require('botbuilder'); -const { DateTimePrompt, WaterfallDialog } = require('botbuilder-dialogs'); -const { CancelAndHelpDialog } = require('./cancelAndHelpDialog'); -const { TimexProperty } = require('@microsoft/recognizers-text-data-types-timex-expression'); - -const DATETIME_PROMPT = 'datetimePrompt'; -const WATERFALL_DIALOG = 'waterfallDialog'; - -class DateResolverDialog extends CancelAndHelpDialog { - constructor(id) { - super(id || 'dateResolverDialog'); - this.addDialog(new DateTimePrompt(DATETIME_PROMPT, this.dateTimePromptValidator.bind(this))) - .addDialog(new WaterfallDialog(WATERFALL_DIALOG, [ - this.initialStep.bind(this), - this.finalStep.bind(this) - ])); - - this.initialDialogId = WATERFALL_DIALOG; - } - - async initialStep(stepContext) { - const timex = stepContext.options.date; - - const promptMessageText = 'On what date would you like to travel?'; - const promptMessage = MessageFactory.text(promptMessageText, promptMessageText, InputHints.ExpectingInput); - - const repromptMessageText = "I'm sorry, for best results, please enter your travel date including the month, day and year."; - const repromptMessage = MessageFactory.text(repromptMessageText, repromptMessageText, InputHints.ExpectingInput); - - if (!timex) { - // We were not given any date at all so prompt the user. - return await stepContext.prompt(DATETIME_PROMPT, - { - prompt: promptMessage, - retryPrompt: repromptMessage - }); - } - // We have a Date we just need to check it is unambiguous. - const timexProperty = new TimexProperty(timex); - if (!timexProperty.types.has('definite')) { - // This is essentially a "reprompt" of the data we were given up front. - return await stepContext.prompt(DATETIME_PROMPT, { prompt: repromptMessage }); - } - return await stepContext.next([{ timex: timex }]); - } - - async finalStep(stepContext) { - const timex = stepContext.result[0].timex; - return await stepContext.endDialog(timex); - } - - async dateTimePromptValidator(promptContext) { - if (promptContext.recognized.succeeded) { - // This value will be a TIMEX. And we are only interested in a Date so grab the first result and drop the Time part. - // TIMEX is a format that represents DateTime expressions that include some ambiguity. e.g. missing a Year. - const timex = promptContext.recognized.value[0].timex.split('T')[0]; - - // If this is a definite Date including year, month and day we are good otherwise reprompt. - // A better solution might be to let the user know what part is actually missing. - return new TimexProperty(timex).types.has('definite'); - } - return false; - } -} - -module.exports.DateResolverDialog = DateResolverDialog; diff --git a/generators/generator-botbuilder/generators/app/templates/core/dialogs/dateResolverDialog.ts b/generators/generator-botbuilder/generators/app/templates/core/dialogs/dateResolverDialog.ts deleted file mode 100644 index 447526281d..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/dialogs/dateResolverDialog.ts +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -import { TimexProperty } from '@microsoft/recognizers-text-data-types-timex-expression'; -import { InputHints, MessageFactory } from 'botbuilder'; -import { DateTimePrompt, DateTimeResolution, DialogTurnResult, PromptValidatorContext, WaterfallDialog, WaterfallStepContext } from 'botbuilder-dialogs'; -import { CancelAndHelpDialog } from './cancelAndHelpDialog'; - -const DATETIME_PROMPT = 'datetimePrompt'; -const WATERFALL_DIALOG = 'waterfallDialog'; - -export class DateResolverDialog extends CancelAndHelpDialog { - - private static async dateTimePromptValidator(promptContext: PromptValidatorContext): Promise { - if (promptContext.recognized.succeeded) { - // This value will be a TIMEX. And we are only interested in a Date so grab the first result and drop the Time part. - // TIMEX is a format that represents DateTime expressions that include some ambiguity. e.g. missing a Year. - const timex = promptContext.recognized.value[0].timex.split('T')[0]; - - // If this is a definite Date including year, month and day we are good otherwise reprompt. - // A better solution might be to let the user know what part is actually missing. - return new TimexProperty(timex).types.has('definite'); - } - return false; - } - - constructor(id: string) { - super(id || 'dateResolverDialog'); - this.addDialog(new DateTimePrompt(DATETIME_PROMPT, DateResolverDialog.dateTimePromptValidator.bind(this))) - .addDialog(new WaterfallDialog(WATERFALL_DIALOG, [ - this.initialStep.bind(this), - this.finalStep.bind(this) - ])); - - this.initialDialogId = WATERFALL_DIALOG; - } - - private async initialStep(stepContext: WaterfallStepContext): Promise { - const timex = (stepContext.options as any).date; - - const promptMessageText = 'On what date would you like to travel?'; - const promptMessage = MessageFactory.text(promptMessageText, promptMessageText, InputHints.ExpectingInput); - - const repromptMessageText = 'I\'m sorry, for best results, please enter your travel date including the month, day and year.'; - const repromptMessage = MessageFactory.text(repromptMessageText, repromptMessageText, InputHints.ExpectingInput); - - if (!timex) { - // We were not given any date at all so prompt the user. - return await stepContext.prompt(DATETIME_PROMPT, - { - prompt: promptMessage, - retryPrompt: repromptMessage - }); - } - // We have a Date we just need to check it is unambiguous. - const timexProperty = new TimexProperty(timex); - if (!timexProperty.types.has('definite')) { - // This is essentially a "reprompt" of the data we were given up front. - return await stepContext.prompt(DATETIME_PROMPT, { prompt: repromptMessage }); - } - return await stepContext.next([{ timex }]); - } - - private async finalStep(stepContext: WaterfallStepContext): Promise { - const timex = stepContext.result[0].timex; - return await stepContext.endDialog(timex); - } -} diff --git a/generators/generator-botbuilder/generators/app/templates/core/dialogs/flightBookingRecognizer.js b/generators/generator-botbuilder/generators/app/templates/core/dialogs/flightBookingRecognizer.js deleted file mode 100644 index 10c69a952b..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/dialogs/flightBookingRecognizer.js +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const { LuisRecognizer } = require('botbuilder-ai'); - -class FlightBookingRecognizer { - constructor(config) { - const luisIsConfigured = config && config.applicationId && config.endpointKey && config.endpoint; - if (luisIsConfigured) { - // Set the recognizer options depending on which endpoint version you want to use e.g v2 or v3. - // More details can be found in https://docs.microsoft.com/en-gb/azure/cognitive-services/luis/luis-migration-api-v3 - const recognizerOptions = { - apiVersion: 'v3' - }; - - this.recognizer = new LuisRecognizer(config, recognizerOptions); - } - } - - get isConfigured() { - return (this.recognizer !== undefined); - } - - /** - * Returns an object with preformatted LUIS results for the bot's dialogs to consume. - * @param {TurnContext} context - */ - async executeLuisQuery(context) { - return await this.recognizer.recognize(context); - } - - getFromEntities(result) { - let fromValue, fromAirportValue; - if (result.entities.$instance.From) { - fromValue = result.entities.$instance.From[0].text; - } - if (fromValue && result.entities.From[0].Airport) { - fromAirportValue = result.entities.From[0].Airport[0][0]; - } - - return { from: fromValue, airport: fromAirportValue }; - } - - getToEntities(result) { - let toValue, toAirportValue; - if (result.entities.$instance.To) { - toValue = result.entities.$instance.To[0].text; - } - if (toValue && result.entities.To[0].Airport) { - toAirportValue = result.entities.To[0].Airport[0][0]; - } - - return { to: toValue, airport: toAirportValue }; - } - - /** - * This value will be a TIMEX. And we are only interested in a Date so grab the first result and drop the Time part. - * TIMEX is a format that represents DateTime expressions that include some ambiguity. e.g. missing a Year. - */ - getTravelDate(result) { - const datetimeEntity = result.entities.datetime; - if (!datetimeEntity || !datetimeEntity[0]) return undefined; - - const timex = datetimeEntity[0].timex; - if (!timex || !timex[0]) return undefined; - - const datetime = timex[0].split('T')[0]; - return datetime; - } -} - -module.exports.FlightBookingRecognizer = FlightBookingRecognizer; diff --git a/generators/generator-botbuilder/generators/app/templates/core/dialogs/flightBookingRecognizer.ts b/generators/generator-botbuilder/generators/app/templates/core/dialogs/flightBookingRecognizer.ts deleted file mode 100644 index d92b66d2cb..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/dialogs/flightBookingRecognizer.ts +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -import { RecognizerResult, TurnContext } from 'botbuilder'; -import { LuisApplication, LuisRecognizer, LuisRecognizerOptionsV3 } from 'botbuilder-ai'; - -export class FlightBookingRecognizer { - private recognizer: LuisRecognizer; - - constructor(config: LuisApplication) { - const luisIsConfigured = config && config.applicationId && config.endpoint && config.endpointKey; - if (luisIsConfigured) { - // Set the recognizer options depending on which endpoint version you want to use e.g LuisRecognizerOptionsV2 or LuisRecognizerOptionsV3. - // More details can be found in https://docs.microsoft.com/en-gb/azure/cognitive-services/luis/luis-migration-api-v3 - const recognizerOptions: LuisRecognizerOptionsV3 = { - apiVersion : 'v3' - }; - - this.recognizer = new LuisRecognizer(config, recognizerOptions); - } - } - - get isConfigured(): boolean { - return (this.recognizer !== undefined); - } - - /** - * Returns an object with preformatted LUIS results for the bot's dialogs to consume. - * @param {TurnContext} context - */ - async executeLuisQuery(context: TurnContext): Promise { - return this.recognizer.recognize(context); - } - - getFromEntities(result) { - let fromValue, fromAirportValue; - if (result.entities.$instance.From) { - fromValue = result.entities.$instance.From[0].text; - } - if (fromValue && result.entities.From[0].Airport) { - fromAirportValue = result.entities.From[0].Airport[0][0]; - } - - return { from: fromValue, airport: fromAirportValue }; - } - - getToEntities(result) { - let toValue, toAirportValue; - if (result.entities.$instance.To) { - toValue = result.entities.$instance.To[0].text; - } - if (toValue && result.entities.To[0].Airport) { - toAirportValue = result.entities.To[0].Airport[0][0]; - } - - return { to: toValue, airport: toAirportValue }; - } - - /** - * This value will be a TIMEX. And we are only interested in a Date so grab the first result and drop the Time part. - * TIMEX is a format that represents DateTime expressions that include some ambiguity. e.g. missing a Year. - */ - getTravelDate(result) { - const datetimeEntity = result.entities.datetime; - if (!datetimeEntity || !datetimeEntity[0]) return undefined; - - const timex = datetimeEntity[0].timex; - if (!timex || !timex[0]) return undefined; - - const datetime = timex[0].split('T')[0]; - return datetime; - } -} diff --git a/generators/generator-botbuilder/generators/app/templates/core/dialogs/mainDialog.js b/generators/generator-botbuilder/generators/app/templates/core/dialogs/mainDialog.js deleted file mode 100644 index 2dc5697587..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/dialogs/mainDialog.js +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const { TimexProperty } = require('@microsoft/recognizers-text-data-types-timex-expression'); -const { MessageFactory, InputHints } = require('botbuilder'); -const { LuisRecognizer } = require('botbuilder-ai'); -const { ComponentDialog, DialogSet, DialogTurnStatus, TextPrompt, WaterfallDialog } = require('botbuilder-dialogs'); - -const MAIN_WATERFALL_DIALOG = 'mainWaterfallDialog'; - -class MainDialog extends ComponentDialog { - constructor(luisRecognizer, bookingDialog) { - super('MainDialog'); - - if (!luisRecognizer) throw new Error('[MainDialog]: Missing parameter \'luisRecognizer\' is required'); - this.luisRecognizer = luisRecognizer; - - if (!bookingDialog) throw new Error('[MainDialog]: Missing parameter \'bookingDialog\' is required'); - - // Define the main dialog and its related components. - // This is a sample "book a flight" dialog. - this.addDialog(new TextPrompt('TextPrompt')) - .addDialog(bookingDialog) - .addDialog(new WaterfallDialog(MAIN_WATERFALL_DIALOG, [ - this.introStep.bind(this), - this.actStep.bind(this), - this.finalStep.bind(this) - ])); - - this.initialDialogId = MAIN_WATERFALL_DIALOG; - } - - /** - * The run method handles the incoming activity (in the form of a TurnContext) and passes it through the dialog system. - * If no dialog is active, it will start the default dialog. - * @param {*} turnContext - * @param {*} accessor - */ - async run(turnContext, accessor) { - const dialogSet = new DialogSet(accessor); - dialogSet.add(this); - - const dialogContext = await dialogSet.createContext(turnContext); - const results = await dialogContext.continueDialog(); - if (results.status === DialogTurnStatus.empty) { - await dialogContext.beginDialog(this.id); - } - } - - /** - * First step in the waterfall dialog. Prompts the user for a command. - * Currently, this expects a booking request, like "book me a flight from Paris to Berlin on march 22" - * Note that the sample LUIS model will only recognize Paris, Berlin, New York and London as airport cities. - */ - async introStep(stepContext) { - if (!this.luisRecognizer.isConfigured) { - const messageText = 'NOTE: LUIS is not configured. To enable all capabilities, add `LuisAppId`, `LuisAPIKey` and `LuisAPIHostName` to the .env file.'; - await stepContext.context.sendActivity(messageText, null, InputHints.IgnoringInput); - return await stepContext.next(); - } - - const messageText = stepContext.options.restartMsg ? stepContext.options.restartMsg : 'What can I help you with today?\nSay something like "Book a flight from Paris to Berlin on March 22, 2020"'; - const promptMessage = MessageFactory.text(messageText, messageText, InputHints.ExpectingInput); - return await stepContext.prompt('TextPrompt', { prompt: promptMessage }); - } - - /** - * Second step in the waterfall. This will use LUIS to attempt to extract the origin, destination and travel dates. - * Then, it hands off to the bookingDialog child dialog to collect any remaining details. - */ - async actStep(stepContext) { - const bookingDetails = {}; - - if (!this.luisRecognizer.isConfigured) { - // LUIS is not configured, we just run the BookingDialog path. - return await stepContext.beginDialog('bookingDialog', bookingDetails); - } - - // Call LUIS and gather any potential booking details. (Note the TurnContext has the response to the prompt) - const luisResult = await this.luisRecognizer.executeLuisQuery(stepContext.context); - switch (LuisRecognizer.topIntent(luisResult)) { - case 'BookFlight': { - // Extract the values for the composite entities from the LUIS result. - const fromEntities = this.luisRecognizer.getFromEntities(luisResult); - const toEntities = this.luisRecognizer.getToEntities(luisResult); - - // Show a warning for Origin and Destination if we can't resolve them. - await this.showWarningForUnsupportedCities(stepContext.context, fromEntities, toEntities); - - // Initialize BookingDetails with any entities we may have found in the response. - bookingDetails.destination = toEntities.airport; - bookingDetails.origin = fromEntities.airport; - bookingDetails.travelDate = this.luisRecognizer.getTravelDate(luisResult); - console.log('LUIS extracted these booking details:', JSON.stringify(bookingDetails)); - - // Run the BookingDialog passing in whatever details we have from the LUIS call, it will fill out the remainder. - return await stepContext.beginDialog('bookingDialog', bookingDetails); - } - - case 'GetWeather': { - // We haven't implemented the GetWeatherDialog so we just display a TODO message. - const getWeatherMessageText = 'TODO: get weather flow here'; - await stepContext.context.sendActivity(getWeatherMessageText, getWeatherMessageText, InputHints.IgnoringInput); - break; - } - - default: { - // Catch all for unhandled intents - const didntUnderstandMessageText = `Sorry, I didn't get that. Please try asking in a different way (intent was ${ LuisRecognizer.topIntent(luisResult) })`; - await stepContext.context.sendActivity(didntUnderstandMessageText, didntUnderstandMessageText, InputHints.IgnoringInput); - } - } - - return await stepContext.next(); - } - - /** - * Shows a warning if the requested From or To cities are recognized as entities but they are not in the Airport entity list. - * In some cases LUIS will recognize the From and To composite entities as a valid cities but the From and To Airport values - * will be empty if those entity values can't be mapped to a canonical item in the Airport. - */ - async showWarningForUnsupportedCities(context, fromEntities, toEntities) { - const unsupportedCities = []; - if (fromEntities.from && !fromEntities.airport) { - unsupportedCities.push(fromEntities.from); - } - - if (toEntities.to && !toEntities.airport) { - unsupportedCities.push(toEntities.to); - } - - if (unsupportedCities.length) { - const messageText = `Sorry but the following airports are not supported: ${ unsupportedCities.join(', ') }`; - await context.sendActivity(messageText, messageText, InputHints.IgnoringInput); - } - } - - /** - * This is the final step in the main waterfall dialog. - * It wraps up the sample "book a flight" interaction with a simple confirmation. - */ - async finalStep(stepContext) { - // If the child dialog ("bookingDialog") was cancelled or the user failed to confirm, the Result here will be null. - if (stepContext.result) { - const result = stepContext.result; - // Now we have all the booking details. - - // This is where calls to the booking AOU service or database would go. - - // If the call to the booking service was successful tell the user. - const timeProperty = new TimexProperty(result.travelDate); - const travelDateMsg = timeProperty.toNaturalLanguage(new Date(Date.now())); - const msg = `I have you booked to ${ result.destination } from ${ result.origin } on ${ travelDateMsg }.`; - await stepContext.context.sendActivity(msg, msg, InputHints.IgnoringInput); - } - - // Restart the main dialog with a different message the second time around - return await stepContext.replaceDialog(this.initialDialogId, { restartMsg: 'What else can I do for you?' }); - } -} - -module.exports.MainDialog = MainDialog; diff --git a/generators/generator-botbuilder/generators/app/templates/core/dialogs/mainDialog.ts b/generators/generator-botbuilder/generators/app/templates/core/dialogs/mainDialog.ts deleted file mode 100644 index a1913b9235..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/dialogs/mainDialog.ts +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -import { TimexProperty } from '@microsoft/recognizers-text-data-types-timex-expression'; -import { BookingDetails } from './bookingDetails'; - -import { InputHints, MessageFactory, StatePropertyAccessor, TurnContext } from 'botbuilder'; -import { LuisRecognizer } from 'botbuilder-ai'; - -import { - ComponentDialog, - DialogSet, - DialogState, - DialogTurnResult, - DialogTurnStatus, - TextPrompt, - WaterfallDialog, - WaterfallStepContext -} from 'botbuilder-dialogs'; -import { BookingDialog } from './bookingDialog'; -import { FlightBookingRecognizer } from './flightBookingRecognizer'; - -const MAIN_WATERFALL_DIALOG = 'mainWaterfallDialog'; - -export class MainDialog extends ComponentDialog { - private luisRecognizer: FlightBookingRecognizer; - - constructor(luisRecognizer: FlightBookingRecognizer, bookingDialog: BookingDialog) { - super('MainDialog'); - - if (!luisRecognizer) throw new Error('[MainDialog]: Missing parameter \'luisRecognizer\' is required'); - this.luisRecognizer = luisRecognizer; - - if (!bookingDialog) throw new Error('[MainDialog]: Missing parameter \'bookingDialog\' is required'); - - // Define the main dialog and its related components. - // This is a sample "book a flight" dialog. - this.addDialog(new TextPrompt('TextPrompt')) - .addDialog(bookingDialog) - .addDialog(new WaterfallDialog(MAIN_WATERFALL_DIALOG, [ - this.introStep.bind(this), - this.actStep.bind(this), - this.finalStep.bind(this) - ])); - - this.initialDialogId = MAIN_WATERFALL_DIALOG; - } - - /** - * The run method handles the incoming activity (in the form of a DialogContext) and passes it through the dialog system. - * If no dialog is active, it will start the default dialog. - * @param {TurnContext} context - */ - async run(context: TurnContext, accessor: StatePropertyAccessor) { - const dialogSet = new DialogSet(accessor); - dialogSet.add(this); - - const dialogContext = await dialogSet.createContext(context); - const results = await dialogContext.continueDialog(); - if (results.status === DialogTurnStatus.empty) { - await dialogContext.beginDialog(this.id); - } - } - - /** - * First step in the waterfall dialog. Prompts the user for a command. - * Currently, this expects a booking request, like "book me a flight from Paris to Berlin on march 22" - * Note that the sample LUIS model will only recognize Paris, Berlin, New York and London as airport cities. - */ - private async introStep(stepContext: WaterfallStepContext): Promise { - if (!this.luisRecognizer.isConfigured) { - const luisConfigMsg = 'NOTE: LUIS is not configured. To enable all capabilities, add `LuisAppId`, `LuisAPIKey` and `LuisAPIHostName` to the .env file.'; - await stepContext.context.sendActivity(luisConfigMsg, null, InputHints.IgnoringInput); - return await stepContext.next(); - } - - const messageText = (stepContext.options as any).restartMsg ? (stepContext.options as any).restartMsg : 'What can I help you with today?\nSay something like "Book a flight from Paris to Berlin on March 22, 2020"'; - const promptMessage = MessageFactory.text(messageText, messageText, InputHints.ExpectingInput); - return await stepContext.prompt('TextPrompt', { prompt: promptMessage }); - } - - /** - * Second step in the waterall. This will use LUIS to attempt to extract the origin, destination and travel dates. - * Then, it hands off to the bookingDialog child dialog to collect any remaining details. - */ - private async actStep(stepContext: WaterfallStepContext): Promise { - const bookingDetails = new BookingDetails(); - - if (!this.luisRecognizer.isConfigured) { - // LUIS is not configured, we just run the BookingDialog path. - return await stepContext.beginDialog('bookingDialog', bookingDetails); - } - - // Call LUIS and gather any potential booking details. (Note the TurnContext has the response to the prompt) - const luisResult = await this.luisRecognizer.executeLuisQuery(stepContext.context); - switch (LuisRecognizer.topIntent(luisResult)) { - case 'BookFlight': - // Extract the values for the composite entities from the LUIS result. - const fromEntities = this.luisRecognizer.getFromEntities(luisResult); - const toEntities = this.luisRecognizer.getToEntities(luisResult); - - // Show a warning for Origin and Destination if we can't resolve them. - await this.showWarningForUnsupportedCities(stepContext.context, fromEntities, toEntities); - - // Initialize BookingDetails with any entities we may have found in the response. - bookingDetails.destination = toEntities.airport; - bookingDetails.origin = fromEntities.airport; - bookingDetails.travelDate = this.luisRecognizer.getTravelDate(luisResult); - console.log('LUIS extracted these booking details:', JSON.stringify(bookingDetails)); - - // Run the BookingDialog passing in whatever details we have from the LUIS call, it will fill out the remainder. - return await stepContext.beginDialog('bookingDialog', bookingDetails); - - case 'GetWeather': - // We haven't implemented the GetWeatherDialog so we just display a TODO message. - const getWeatherMessageText = 'TODO: get weather flow here'; - await stepContext.context.sendActivity(getWeatherMessageText, getWeatherMessageText, InputHints.IgnoringInput); - break; - - default: - // Catch all for unhandled intents - const didntUnderstandMessageText = `Sorry, I didn't get that. Please try asking in a different way (intent was ${ LuisRecognizer.topIntent(luisResult) })`; - await stepContext.context.sendActivity(didntUnderstandMessageText, didntUnderstandMessageText, InputHints.IgnoringInput); - } - - return await stepContext.next(); - } - - /** - * Shows a warning if the requested From or To cities are recognized as entities but they are not in the Airport entity list. - * In some cases LUIS will recognize the From and To composite entities as a valid cities but the From and To Airport values - * will be empty if those entity values can't be mapped to a canonical item in the Airport. - */ - private async showWarningForUnsupportedCities(context, fromEntities, toEntities) { - const unsupportedCities = []; - if (fromEntities.from && !fromEntities.airport) { - unsupportedCities.push(fromEntities.from); - } - - if (toEntities.to && !toEntities.airport) { - unsupportedCities.push(toEntities.to); - } - - if (unsupportedCities.length) { - const messageText = `Sorry but the following airports are not supported: ${ unsupportedCities.join(', ') }`; - await context.sendActivity(messageText, messageText, InputHints.IgnoringInput); - } - } - - /** - * This is the final step in the main waterfall dialog. - * It wraps up the sample "book a flight" interaction with a simple confirmation. - */ - private async finalStep(stepContext: WaterfallStepContext): Promise { - // If the child dialog ("bookingDialog") was cancelled or the user failed to confirm, the Result here will be null. - if (stepContext.result) { - const result = stepContext.result as BookingDetails; - // Now we have all the booking details. - - // This is where calls to the booking AOU service or database would go. - - // If the call to the booking service was successful tell the user. - const timeProperty = new TimexProperty(result.travelDate); - const travelDateMsg = timeProperty.toNaturalLanguage(new Date(Date.now())); - const msg = `I have you booked to ${ result.destination } from ${ result.origin } on ${ travelDateMsg }.`; - await stepContext.context.sendActivity(msg); - } - - // Restart the main dialog waterfall with a different message the second time around - return await stepContext.replaceDialog(this.initialDialogId, { restartMsg: 'What else can I do for you?' }); - } -} diff --git a/generators/generator-botbuilder/generators/app/templates/core/index.js b/generators/generator-botbuilder/generators/app/templates/core/index.js deleted file mode 100644 index f98bd26d85..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/index.js +++ /dev/null @@ -1,120 +0,0 @@ -// index.js is used to setup and configure your bot - -// Import required packages -const path = require('path'); - -// Note: Ensure you have a .env file and include LuisAppId, LuisAPIKey and LuisAPIHostName. -const ENV_FILE = path.join(__dirname, '.env'); -require('dotenv').config({ path: ENV_FILE }); - -const restify = require('restify'); - -// Import required bot services. -// See https://aka.ms/bot-services to learn more about the different parts of a bot. -const { - CloudAdapter, - ConfigurationServiceClientCredentialFactory, - ConversationState, - createBotFrameworkAuthenticationFromConfiguration, - InputHints, - MemoryStorage, - UserState -} = require('botbuilder'); - -const { FlightBookingRecognizer } = require('./dialogs/flightBookingRecognizer'); - -// This bot's main dialog. -const { DialogAndWelcomeBot } = require('./bots/dialogAndWelcomeBot'); -const { MainDialog } = require('./dialogs/mainDialog'); - -// the bot's booking dialog -const { BookingDialog } = require('./dialogs/bookingDialog'); -const BOOKING_DIALOG = 'bookingDialog'; - -const credentialsFactory = new ConfigurationServiceClientCredentialFactory({ - MicrosoftAppId: process.env.MicrosoftAppId, - MicrosoftAppPassword: process.env.MicrosoftAppPassword, - MicrosoftAppType: process.env.MicrosoftAppType, - MicrosoftAppTenantId: process.env.MicrosoftAppTenantId -}); - -const botFrameworkAuthentication = createBotFrameworkAuthenticationFromConfiguration(null, credentialsFactory); - -// Create adapter. -// See https://aka.ms/about-bot-adapter to learn more about adapters. -const adapter = new CloudAdapter(botFrameworkAuthentication); - -// Catch-all for errors. -const onTurnErrorHandler = async (context, error) => { - // This check writes out errors to console log .vs. app insights. - // NOTE: In production environment, you should consider logging this to Azure - // application insights. - console.error(`\n [onTurnError] unhandled error: ${ error }`); - - // Send a trace activity, which will be displayed in Bot Framework Emulator - await context.sendTraceActivity( - 'OnTurnError Trace', - `${ error }`, - 'https://www.botframework.com/schemas/error', - 'TurnError' - ); - - // Send a message to the user - let onTurnErrorMessage = 'The bot encountered an error or bug.'; - await context.sendActivity(onTurnErrorMessage, onTurnErrorMessage, InputHints.ExpectingInput); - onTurnErrorMessage = 'To continue to run this bot, please fix the bot source code.'; - await context.sendActivity(onTurnErrorMessage, onTurnErrorMessage, InputHints.ExpectingInput); - // Clear out state - await conversationState.delete(context); -}; - -// Set the onTurnError for the singleton CloudAdapter. -adapter.onTurnError = onTurnErrorHandler; - -// Define a state store for your bot. See https://aka.ms/about-bot-state to learn more about using MemoryStorage. -// A bot requires a state store to persist the dialog and user state between messages. - -// For local development, in-memory storage is used. -// CAUTION: The Memory Storage used here is for local bot debugging only. When the bot -// is restarted, anything stored in memory will be gone. -const memoryStorage = new MemoryStorage(); -const conversationState = new ConversationState(memoryStorage); -const userState = new UserState(memoryStorage); - -// If configured, pass in the FlightBookingRecognizer. (Defining it externally allows it to be mocked for tests) -const { LuisAppId, LuisAPIKey, LuisAPIHostName } = process.env; -const luisConfig = { applicationId: LuisAppId, endpointKey: LuisAPIKey, endpoint: `https://${ LuisAPIHostName }` }; - -const luisRecognizer = new FlightBookingRecognizer(luisConfig); - -// Create the main dialog. -const bookingDialog = new BookingDialog(BOOKING_DIALOG); -const dialog = new MainDialog(luisRecognizer, bookingDialog); -const bot = new DialogAndWelcomeBot(conversationState, userState, dialog); - -// Create HTTP server -const server = restify.createServer(); -server.use(restify.plugins.bodyParser()); - -server.listen(process.env.port || process.env.PORT || 3978, function() { - console.log(`\n${ server.name } listening to ${ server.url }`); - console.log('\nGet Bot Framework Emulator: https://aka.ms/botframework-emulator'); - console.log('\nTo talk to your bot, open the emulator select "Open Bot"'); -}); - -// Listen for incoming activities and route them to your bot main dialog. -server.post('/api/messages', async (req, res) => { - // Route received a request to adapter for processing - await adapter.process(req, res, (context) => bot.run(context)); -}); - -// Listen for Upgrade requests for Streaming. -server.on('upgrade', async (req, socket, head) => { - // Create an adapter scoped to this WebSocket connection to allow storing session data. - const streamingAdapter = new CloudAdapter(botFrameworkAuthentication); - - // Set onTurnError for the CloudAdapter created for each connection. - streamingAdapter.onTurnError = onTurnErrorHandler; - - await streamingAdapter.process(req, socket, head, (context) => bot.run(context)); -}); diff --git a/generators/generator-botbuilder/generators/app/templates/core/index.ts b/generators/generator-botbuilder/generators/app/templates/core/index.ts deleted file mode 100644 index db81db1846..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/index.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { config } from 'dotenv'; -import * as path from 'path'; - -// Note: Ensure you have a .env file and include LuisAppId, LuisAPIKey and LuisAPIHostName. -const ENV_FILE = path.join(__dirname, '..', '.env'); -config({ path: ENV_FILE }); - -import * as restify from 'restify'; - -import { INodeSocket } from 'botframework-streaming'; - -// Import required bot services. -// See https://aka.ms/bot-services to learn more about the different parts of a bot. -import { - CloudAdapter, - ConfigurationServiceClientCredentialFactory, - ConversationState, - createBotFrameworkAuthenticationFromConfiguration, - MemoryStorage, - UserState -} from 'botbuilder'; -import { LuisApplication } from 'botbuilder-ai'; - -// The bot and its main dialog. -import { DialogAndWelcomeBot } from './bots/dialogAndWelcomeBot'; -import { MainDialog } from './dialogs/mainDialog'; - -// The bot's booking dialog -import { BookingDialog } from './dialogs/bookingDialog'; -const BOOKING_DIALOG = 'bookingDialog'; - -// The helper-class recognizer that calls LUIS -import { FlightBookingRecognizer } from './dialogs/flightBookingRecognizer'; - -const credentialsFactory = new ConfigurationServiceClientCredentialFactory({ - MicrosoftAppId: process.env.MicrosoftAppId, - MicrosoftAppPassword: process.env.MicrosoftAppPassword, - MicrosoftAppType: process.env.MicrosoftAppType, - MicrosoftAppTenantId: process.env.MicrosoftAppTenantId -}); - -const botFrameworkAuthentication = createBotFrameworkAuthenticationFromConfiguration(null, credentialsFactory); - -// Create adapter. -// See https://aka.ms/about-bot-adapter to learn more about adapters. -const adapter = new CloudAdapter(botFrameworkAuthentication); - -// Catch-all for errors. -const onTurnErrorHandler = async (context, error) => { - // This check writes out errors to console log .vs. app insights. - // NOTE: In production environment, you should consider logging this to Azure - // application insights. - console.error(`\n [onTurnError] unhandled error: ${ error }`); - - // Send a trace activity, which will be displayed in Bot Framework Emulator - await context.sendTraceActivity( - 'OnTurnError Trace', - `${ error }`, - 'https://www.botframework.com/schemas/error', - 'TurnError' - ); - - // Send a message to the user - await context.sendActivity('The bot encountered an error or bug.'); - await context.sendActivity('To continue to run this bot, please fix the bot source code.'); - // Clear out state - await conversationState.delete(context); -}; - -// Set the onTurnError for the singleton CloudAdapter. -adapter.onTurnError = onTurnErrorHandler; - -// Define a state store for your bot. See https://aka.ms/about-bot-state to learn more about using MemoryStorage. -// A bot requires a state store to persist the dialog and user state between messages. -let conversationState: ConversationState; -let userState: UserState; - -// For local development, in-memory storage is used. -// CAUTION: The Memory Storage used here is for local bot debugging only. When the bot -// is restarted, anything stored in memory will be gone. -const memoryStorage = new MemoryStorage(); -conversationState = new ConversationState(memoryStorage); -userState = new UserState(memoryStorage); - -// If configured, pass in the FlightBookingRecognizer. (Defining it externally allows it to be mocked for tests) -let luisRecognizer; -const { LuisAppId, LuisAPIKey, LuisAPIHostName } = process.env; -const luisConfig: LuisApplication = { applicationId: LuisAppId, endpointKey: LuisAPIKey, endpoint: `https://${ LuisAPIHostName }` }; - -luisRecognizer = new FlightBookingRecognizer(luisConfig); - -// Create the main dialog. -const bookingDialog = new BookingDialog(BOOKING_DIALOG); -const dialog = new MainDialog(luisRecognizer, bookingDialog); -const bot = new DialogAndWelcomeBot(conversationState, userState, dialog); - -// Create HTTP server -const server = restify.createServer(); -server.use(restify.plugins.bodyParser()); - -server.listen(process.env.port || process.env.PORT || 3978, () => { - console.log(`\n${ server.name } listening to ${ server.url }`); - console.log('\nGet Bot Framework Emulator: https://aka.ms/botframework-emulator'); - console.log('\nTo talk to your bot, open the emulator select "Open Bot"'); -}); - -// Listen for incoming activities and route them to your bot main dialog. -server.post('/api/messages', async (req, res) => { - // Route received a request to adapter for processing - await adapter.process(req, res, (context) => bot.run(context)); -}); - -// Listen for Upgrade requests for Streaming. -server.on('upgrade', async (req, socket, head) => { - // Create an adapter scoped to this WebSocket connection to allow storing session data. - const streamingAdapter = new CloudAdapter(botFrameworkAuthentication); - - // Set onTurnError for the CloudAdapter created for each connection. - streamingAdapter.onTurnError = onTurnErrorHandler; - - await streamingAdapter.process(req, socket as unknown as INodeSocket, head, (context) => bot.run(context)); -}); diff --git a/generators/generator-botbuilder/generators/app/templates/core/package-with-tests.json.js b/generators/generator-botbuilder/generators/app/templates/core/package-with-tests.json.js deleted file mode 100644 index 34fa721c40..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/package-with-tests.json.js +++ /dev/null @@ -1,38 +0,0 @@ -{ - "name": "<%= botname %>", - "version": "4.1.6", - "description": "<%= botDescription %>", - "author": "Generated using Microsoft Bot Builder Yeoman generator v<%= version %>", - "license": "MIT", - "main": "<%= npmMain %>", - "scripts": { - "start": "node ./index.js", - "watch": "nodemon ./index.js", - "lint": "eslint .", - "test": "nyc mocha tests/**/*.test.js" - }, - "repository": { - "type": "git", - "url": "https://github.com" - }, - "dependencies": { - "@microsoft/recognizers-text-data-types-timex-expression": "1.1.4", - "botbuilder": "4.1.6", - "botbuilder-ai": "4.1.6", - "botbuilder-dialogs": "4.1.6", - "botbuilder-testing": "4.1.6", - "dotenv": "^8.2.0", - "restify": "^11.1.0" - }, - "devDependencies": { - "eslint": "^7.0.0", - "eslint-config-standard": "^14.1.1", - "eslint-plugin-import": "^2.20.2", - "eslint-plugin-node": "^11.1.0", - "eslint-plugin-promise": "^4.2.1", - "eslint-plugin-standard": "^4.0.1", - "mocha": "^7.1.2", - "nodemon": "^2.0.4", - "nyc": "^15.0.1" - } -} diff --git a/generators/generator-botbuilder/generators/app/templates/core/package-with-tests.json.ts b/generators/generator-botbuilder/generators/app/templates/core/package-with-tests.json.ts deleted file mode 100644 index bc24385700..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/package-with-tests.json.ts +++ /dev/null @@ -1,60 +0,0 @@ -{ - "name": "<%= botname %>", - "version": "4.1.6", - "description": "<%= botDescription %>", - "author": "Generated using Microsoft Bot Builder Yeoman generator v<%= version %>", - "license": "MIT", - "main": "<%= npmMain %>", - "scripts": { - "build": "tsc --build", - "lint": "tslint -c tslint.json 'src/**/*.ts'", - "postinstall": "npm run build && node ./deploymentScripts/webConfigPrep.js", - "start": "tsc --build && node ./lib/index.js", - "test": "tsc --build && nyc mocha lib/tests/**/*.test.js", - "watch": "nodemon --watch ./src -e ts --exec \"npm run start\"" - }, - "repository": { - "type": "git", - "url": "https://github.com" - }, - "nyc": { - "extension": [ - ".ts", - ".tsx" - ], - "exclude": [ - "**/.eslintrc.js", - "**/*.d.ts", - "**/*.test.*", - "**/tests", - "**/coverage", - "**/deploymentScripts", - "**/src/index.ts" - ], - "reporter": [ - "text" - ], - "all": true - }, - "dependencies": { - "@microsoft/recognizers-text-data-types-timex-expression": "1.1.4", - "botbuilder": "4.1.6", - "botbuilder-ai": "4.1.6", - "botbuilder-dialogs": "4.1.6", - "botbuilder-testing": "4.1.6", - "dotenv": "^8.2.0", - "replace": "~1.2.0", - "restify": "~11.1.0" -}, - "devDependencies": { - "@types/mocha": "^7.0.2", - "@types/node": "^18.19.47", - "@types/restify": "8.4.2", - "mocha": "^7.1.2", - "nodemon": "^2.0.4", - "nyc": "^15.0.1", - "ts-node": "^8.10.1", - "tslint": "^6.1.2", - "typescript": "^5.6.3" - } -} diff --git a/generators/generator-botbuilder/generators/app/templates/core/package.json.js b/generators/generator-botbuilder/generators/app/templates/core/package.json.js deleted file mode 100644 index 6b8ae1f9c1..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/package.json.js +++ /dev/null @@ -1,37 +0,0 @@ -{ - "name": "<%= botname %>", - "version": "4.1.6", - "description": "<%= botDescription %>", - "author": "Generated using Microsoft Bot Builder Yeoman generator v<%= version %>", - "license": "MIT", - "main": "<%= npmMain %>", - "scripts": { - "start": "node ./index.js", - "watch": "nodemon ./index.js", - "lint": "eslint .", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "repository": { - "type": "git", - "url": "https://github.com" - }, - "dependencies": { - "@microsoft/recognizers-text-data-types-timex-expression": "1.1.4", - "botbuilder": "4.1.6", - "botbuilder-ai": "4.1.6", - "botbuilder-dialogs": "4.1.6", - "dotenv": "~8.2.0", - "restify": "~11.1.0" - }, - "devDependencies": { - "eslint": "^7.0.0", - "eslint-config-standard": "^14.1.1", - "eslint-plugin-import": "^2.20.2", - "eslint-plugin-node": "^11.1.0", - "eslint-plugin-promise": "^4.2.1", - "eslint-plugin-standard": "^4.0.1", - "mocha": "^7.1.2", - "nodemon": "^2.0.4", - "nyc": "^15.0.1" - } -} diff --git a/generators/generator-botbuilder/generators/app/templates/core/package.json.ts b/generators/generator-botbuilder/generators/app/templates/core/package.json.ts deleted file mode 100644 index 9514e6dc6f..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/package.json.ts +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "<%= botname %>", - "version": "4.1.6", - "description": "<%= botDescription %>", - "author": "Generated using Microsoft Bot Builder Yeoman generator v<%= version %>", - "license": "MIT", - "main": "<%= npmMain %>", - "scripts": { - "build": "tsc --build", - "lint": "tslint -c tslint.json 'src/**/*.ts'", - "postinstall": "npm run build && node ./deploymentScripts/webConfigPrep.js", - "start": "tsc --build && node ./lib/index.js", - "test": "echo \"Error: no test specified\" && exit 1", - "watch": "nodemon --watch ./src -e ts --exec \"npm run start\"" - }, - "repository": { - "type": "git", - "url": "https://github.com" - }, - "dependencies": { - "@microsoft/recognizers-text-data-types-timex-expression": "1.1.4", - "botbuilder": "4.1.6", - "botbuilder-ai": "4.1.6", - "botbuilder-dialogs": "4.1.6", - "dotenv": "~8.2.0", - "replace": "~1.2.0", - "restify": "~11.1.0" - }, - "devDependencies": { - "@types/mocha": "^7.0.2", - "@types/node": "^18.19.47", - "@types/restify": "8.4.2", - "mocha": "^7.1.2", - "nodemon": "^2.0.4", - "nyc": "^15.0.1", - "ts-node": "^8.10.1", - "tslint": "^6.1.2", - "typescript": "^5.6.3" - } -} diff --git a/generators/generator-botbuilder/generators/app/templates/core/resources/welcomeCard.json b/generators/generator-botbuilder/generators/app/templates/core/resources/welcomeCard.json deleted file mode 100644 index dd69d43431..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/resources/welcomeCard.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", - "type": "AdaptiveCard", - "version": "1.0", - "body": [ - { - "type": "Image", - "url": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQtB3AwMUeNoq4gUBGe6Ocj8kyh3bXa9ZbV7u1fVKQoyKFHdkqU", - "size": "stretch" - }, - { - "type": "TextBlock", - "spacing": "medium", - "size": "default", - "weight": "bolder", - "text": "Welcome to Bot Framework!", - "wrap": true, - "maxLines": 0 - }, - { - "type": "TextBlock", - "size": "default", - "isSubtle": true, - "text": "Now that you have successfully run your bot, follow the links in this Adaptive Card to expand your knowledge of Bot Framework.", - "wrap": true, - "maxLines": 0 - } - ], - "actions": [ - { - "type": "Action.OpenUrl", - "title": "Get an overview", - "url": "https://docs.microsoft.com/en-us/azure/bot-service/?view=azure-bot-service-4.0" - }, - { - "type": "Action.OpenUrl", - "title": "Ask a question", - "url": "https://stackoverflow.com/questions/tagged/botframework" - }, - { - "type": "Action.OpenUrl", - "title": "Learn how to deploy", - "url": "https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-howto-deploy-azure?view=azure-bot-service-4.0" - } - ] -} diff --git a/generators/generator-botbuilder/generators/app/templates/core/tests/README.md b/generators/generator-botbuilder/generators/app/templates/core/tests/README.md deleted file mode 100644 index fe05f77659..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/tests/README.md +++ /dev/null @@ -1,58 +0,0 @@ - -# <%= botname %> tests - -Bot Framework v4 bot tests for `<%= botname %>` bot. - -This project uses the [botbuilder-testing](https://www.npmjs.com/package/botbuilder-testing) package and [mocha](https://github.com/mochajs/mocha) to create unit tests for your bot. - -This project shows how to: - -- Create unit tests for dialogs and bots -- Create different types of data driven tests using mocha tests -- Create mock objects for the different dependencies of a dialog (i.e. LUIS recognizers, other dialogs, configuration, etc.) -- Assert the activities returned by a dialog turn against expected values -- Assert the results returned by a dialog - -## Overview - -In this sample, dialogs are unit tested through the `DialogTestClient` class which provides a mechanism for testing them in isolation outside of a bot and without having to deploy your code to a web service. - -This class is used to write unit tests for dialogs that test their responses on a turn-by-turn basis. Any dialog built using the botbuilder dialogs library should work. - -Here is a simple example on how a test that uses `DialogTestClient` looks like: - -```javascript -const sut = new BookingDialog(); -const testClient = new DialogTestClient('msteams', sut); - -let reply = await testClient.sendActivity('hi'); -assert.strictEqual(reply.text, 'Where would you like to travel to?'); - -reply = await testClient.sendActivity('Seattle'); -assert.strictEqual(reply.text, 'Where are you traveling from?'); - -reply = await testClient.sendActivity('New York'); -assert.strictEqual(reply.text, 'When would you like to travel?'); - -reply = await testClient.sendActivity('tomorrow'); -assert.strictEqual(reply.text, 'OK, I will book a flight from Seattle to New York for tomorrow, Is this Correct?'); - -reply = await testClient.sendActivity('yes'); -assert.strictEqual(reply.text, 'Sure thing, wait while I finalize your reservation...'); - -reply = testClient.getNextReply(); -assert.strictEqual(reply.text, 'All set, I have booked your flight to Seattle for tomorrow'); -``` - -The project includes several examples on how to test different bot components: - -- [cancelAndHelpDialog.test](dialogs/cancelAndHelpDialog.test.js) shows how to write a simple data driven test for `CancelAndHelpDialog` using a test case array. -- [bookingDialog.test](dialogs/bookingDialog.test.js) shows how to write a data driven test using a `bookingDialogTestCases` module to generate the test cases. -- [mainDialog.test](dialogs/mainDialog.test.js) showcases how to use mock objects to mock the dialog's LUIS and `BookingDialog` dependencies to test `MainDialog` in isolation. -- [dialogAndWelcomeBot.test](bots/dialogAndWelcomeBot.test.js) provides an example on how to write a test for the bot's `ActivityHandler` using `TestAdapter`. - -## Further reading - -- [How to unit test bots](https://aka.ms/js-unit-test-docs) -- [Mocha](https://github.com/mochajs/mocha) -- [Bot Testing](https://github.com/microsoft/botframework-sdk/blob/master/specs/testing/testing.md) diff --git a/generators/generator-botbuilder/generators/app/templates/core/tests/bots/dialogAndWelcomeBot.test.js b/generators/generator-botbuilder/generators/app/templates/core/tests/bots/dialogAndWelcomeBot.test.js deleted file mode 100644 index b8413628e5..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/tests/bots/dialogAndWelcomeBot.test.js +++ /dev/null @@ -1,70 +0,0 @@ -/* eslint-env node, mocha */ -const { TestAdapter, ActivityTypes, TurnContext, ConversationState, MemoryStorage, UserState } = require('botbuilder'); -const { DialogSet, DialogTurnStatus, Dialog } = require('botbuilder-dialogs'); -const { DialogAndWelcomeBot } = require('../../bots/dialogAndWelcomeBot'); -const assert = require('assert'); - -/** - * A simple mock for a root dialog that gets invoked by the bot. - */ -class MockRootDialog extends Dialog { - constructor() { - super('mockRootDialog'); - } - - async beginDialog(dc, options) { - await dc.context.sendActivity(`${ this.id } mock invoked`); - return await dc.endDialog(); - } - - async run(turnContext, accessor) { - const dialogSet = new DialogSet(accessor); - dialogSet.add(this); - - const dialogContext = await dialogSet.createContext(turnContext); - const results = await dialogContext.continueDialog(); - if (results.status === DialogTurnStatus.empty) { - await dialogContext.beginDialog(this.id); - } - } -} - -describe('DialogAndWelcomeBot', () => { - const testAdapter = new TestAdapter(); - - async function processActivity(activity, bot) { - const context = new TurnContext(testAdapter, activity); - await bot.run(context); - } - - it('Shows welcome card on member added and starts main dialog', async () => { - const mockRootDialog = new MockRootDialog(); - const memoryStorage = new MemoryStorage(); - const sut = new DialogAndWelcomeBot(new ConversationState(memoryStorage), new UserState(memoryStorage), mockRootDialog, console); - - // Create conversationUpdate activity - const conversationUpdateActivity = { - type: ActivityTypes.ConversationUpdate, - channelId: 'test', - conversation: { - id: 'someId' - }, - membersAdded: [ - { id: 'theUser' } - ], - recipient: { id: 'theBot' } - }; - - // Send the conversation update activity to the bot. - await processActivity(conversationUpdateActivity, sut); - - // Assert we got the welcome card - let reply = testAdapter.activityBuffer.shift(); - assert.strictEqual(reply.attachments.length, 1); - assert.strictEqual(reply.attachments[0].contentType, 'application/vnd.microsoft.card.adaptive'); - - // Assert that we started the main dialog. - reply = testAdapter.activityBuffer.shift(); - assert.strictEqual(reply.text, 'mockRootDialog mock invoked'); - }); -}); diff --git a/generators/generator-botbuilder/generators/app/templates/core/tests/bots/dialogAndWelcomeBot.test.ts b/generators/generator-botbuilder/generators/app/templates/core/tests/bots/dialogAndWelcomeBot.test.ts deleted file mode 100644 index 57d31b0115..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/tests/bots/dialogAndWelcomeBot.test.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { ActivityTypes, ConversationState, MemoryStorage, TestAdapter, TurnContext, UserState } from 'botbuilder'; -import { Dialog, DialogSet, DialogTurnStatus } from 'botbuilder-dialogs'; -import { DialogAndWelcomeBot } from '../../bots/dialogAndWelcomeBot'; -const assert = require('assert'); - -/** - * A simple mock for a root dialog that gets invoked by the bot. - */ -class MockRootDialog extends Dialog { - constructor() { - super('mockRootDialog'); - } - - async beginDialog(dc, options) { - await dc.context.sendActivity(`${ this.id } mock invoked`); - return await dc.endDialog(); - } - - async run(turnContext, accessor) { - const dialogSet = new DialogSet(accessor); - dialogSet.add(this); - - const dialogContext = await dialogSet.createContext(turnContext); - const results = await dialogContext.continueDialog(); - if (results.status === DialogTurnStatus.empty) { - await dialogContext.beginDialog(this.id); - } - } -} - -describe('DialogAndWelcomeBot', () => { - const testAdapter = new TestAdapter(async (context) => undefined); - - async function processActivity(activity, bot) { - const context = new TurnContext(testAdapter, activity); - await bot.run(context); - } - - it('Shows welcome card on member added and starts main dialog', async () => { - const mockRootDialog = new MockRootDialog(); - const memoryStorage = new MemoryStorage(); - const sut = new DialogAndWelcomeBot(new ConversationState(memoryStorage), new UserState(memoryStorage), mockRootDialog); - - // Create conversationUpdate activity - const conversationUpdateActivity = { - channelId: 'test', - conversation: { - id: 'someId' - }, - membersAdded: [ - { id: 'theUser' } - ], - recipient: { id: 'theBot' }, - type: ActivityTypes.ConversationUpdate - }; - - // Send the conversation update activity to the bot. - await processActivity(conversationUpdateActivity, sut); - - // Assert we got the welcome card - let reply = testAdapter.activityBuffer.shift(); - assert.strictEqual(reply.attachments.length, 1); - assert.strictEqual(reply.attachments[0].contentType, 'application/vnd.microsoft.card.adaptive'); - - // Assert that we started the main dialog. - reply = testAdapter.activityBuffer.shift(); - assert.strictEqual(reply.text, 'mockRootDialog mock invoked'); - }); -}); diff --git a/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/bookingDialog.test.js b/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/bookingDialog.test.js deleted file mode 100644 index 9048c70fdf..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/bookingDialog.test.js +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. - */ - -/* eslint-env node, mocha */ -const { DialogTestClient, DialogTestLogger } = require('botbuilder-testing'); -const { BookingDialog } = require('../../dialogs/bookingDialog'); -const assert = require('assert'); - -describe('BookingDialog', () => { - const testCases = require('./testData/bookingDialogTestCases.js'); - const sut = new BookingDialog('bookingDialog'); - - testCases.map(testData => { - it(testData.name, async () => { - const client = new DialogTestClient('test', sut, testData.initialData, [new DialogTestLogger()]); - - // Execute the test case - console.log(`Test Case: ${ testData.name }`); - console.log(`Dialog Input ${ JSON.stringify(testData.initialData) }`); - for (let i = 0; i < testData.steps.length; i++) { - const reply = await client.sendActivity(testData.steps[i][0]); - assert.strictEqual((reply ? reply.text : null), testData.steps[i][1], `${ reply ? reply.text : null } != ${ testData.steps[i][1] }`); - } - - assert.strictEqual(client.dialogTurnResult.status, testData.expectedStatus, `${ testData.expectedStatus } != ${ client.dialogTurnResult.status }`); - - console.log(`Dialog result: ${ JSON.stringify(client.dialogTurnResult.result) }`); - if (testData.expectedResult !== undefined) { - // Check dialog results - const result = client.dialogTurnResult.result; - assert.strictEqual(result.destination, testData.expectedResult.destination); - assert.strictEqual(result.origin, testData.expectedResult.origin); - assert.strictEqual(result.travelDate, testData.expectedResult.travelDate); - } else { - assert.strictEqual(client.dialogTurnResult.result, undefined); - } - }); - }); -}); diff --git a/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/bookingDialog.test.ts b/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/bookingDialog.test.ts deleted file mode 100644 index 905c12acf6..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/bookingDialog.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. - */ -import { DialogTestClient, DialogTestLogger } from 'botbuilder-testing'; -import { BookingDialog } from '../../dialogs/bookingDialog'; -const assert = require('assert'); - -describe('BookingDialog', () => { - const testCases = require('./testData/bookingDialogTestCases'); - const sut = new BookingDialog('bookingDialog'); - - testCases.map((testData) => { - it(testData.name, async () => { - const client = new DialogTestClient('test', sut, testData.initialData, [new DialogTestLogger()]); - - // Execute the test case - console.log(`Test Case: ${ testData.name }`); - console.log(`Dialog Input ${ JSON.stringify(testData.initialData) }`); - for (const step of testData.steps) { - const reply = await client.sendActivity(step[0]); - assert.strictEqual((reply ? reply.text : null), step[1], `${ reply ? reply.text : null } != ${ step[1] }`); - } - - assert.strictEqual(client.dialogTurnResult.status, testData.expectedStatus, `${ testData.expectedStatus } != ${ client.dialogTurnResult.status }`); - - console.log(`Dialog result: ${ JSON.stringify(client.dialogTurnResult.result) }`); - if (testData.expectedResult !== undefined) { - // Check dialog results - const result = client.dialogTurnResult.result; - assert.strictEqual(result.destination, testData.expectedResult.destination); - assert.strictEqual(result.origin, testData.expectedResult.origin); - assert.strictEqual(result.travelDate, testData.expectedResult.travelDate); - } else { - assert.strictEqual(client.dialogTurnResult.result, undefined); - } - }); - }); -}); diff --git a/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/cancelAndHelpDialog.test.js b/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/cancelAndHelpDialog.test.js deleted file mode 100644 index f37982ee40..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/cancelAndHelpDialog.test.js +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. - */ - -/* eslint-env node, mocha */ -const { MessageFactory } = require('botbuilder'); -const { DialogTestClient, DialogTestLogger } = require('botbuilder-testing'); -const { TextPrompt, WaterfallDialog } = require('botbuilder-dialogs'); -const { CancelAndHelpDialog } = require('../../dialogs/cancelAndHelpDialog'); -const assert = require('assert'); - -/** - * An waterfall dialog derived from CancelAndHelpDialog for testing - */ -class TestCancelAndHelpDialog extends CancelAndHelpDialog { - constructor() { - super('TestCancelAndHelpDialog'); - - this.addDialog(new TextPrompt('TextPrompt')) - .addDialog(new WaterfallDialog('WaterfallDialog', [ - this.promptStep.bind(this), - this.finalStep.bind(this) - ])); - - this.initialDialogId = 'WaterfallDialog'; - } - - async promptStep(stepContext) { - return await stepContext.prompt('TextPrompt', { prompt: MessageFactory.text('Hi there') }); - } - - async finalStep(stepContext) { - return await stepContext.endDialog(); - } -} - -describe('CancelAndHelpDialog', () => { - describe('Should be able to cancel', () => { - const testCases = ['cancel', 'quit']; - - testCases.map(testData => { - it(testData, async () => { - const sut = new TestCancelAndHelpDialog(); - const client = new DialogTestClient('test', sut, null, [new DialogTestLogger()]); - - // Execute the test case - let reply = await client.sendActivity('Hi'); - assert.strictEqual(reply.text, 'Hi there'); - assert.strictEqual(client.dialogTurnResult.status, 'waiting'); - - reply = await client.sendActivity(testData); - assert.strictEqual(reply.text, 'Cancelling...'); - assert.strictEqual(client.dialogTurnResult.status, 'complete'); - }); - }); - }); - - describe('Should be able to get help', () => { - const testCases = ['help', '?']; - - testCases.map(testData => { - it(testData, async () => { - const sut = new TestCancelAndHelpDialog(); - const client = new DialogTestClient('test', sut, null, [new DialogTestLogger()]); - - // Execute the test case - let reply = await client.sendActivity('Hi'); - assert.strictEqual(reply.text, 'Hi there'); - assert.strictEqual(client.dialogTurnResult.status, 'waiting'); - - reply = await client.sendActivity(testData); - assert.strictEqual(reply.text, 'Show help here'); - assert.strictEqual(client.dialogTurnResult.status, 'waiting'); - }); - }); - }); -}); diff --git a/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/cancelAndHelpDialog.test.ts b/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/cancelAndHelpDialog.test.ts deleted file mode 100644 index 42d6060cbb..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/cancelAndHelpDialog.test.ts +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. - */ -import { MessageFactory } from 'botbuilder'; -import { TextPrompt, WaterfallDialog } from 'botbuilder-dialogs'; -import { DialogTestClient, DialogTestLogger } from 'botbuilder-testing'; -import { CancelAndHelpDialog } from '../../dialogs/cancelAndHelpDialog'; -const assert = require('assert'); - -/** - * An waterfall dialog derived from CancelAndHelpDialog for testing - */ -class TestCancelAndHelpDialog extends CancelAndHelpDialog { - constructor() { - super('TestCancelAndHelpDialog'); - - this.addDialog(new TextPrompt('TextPrompt')) - .addDialog(new WaterfallDialog('WaterfallDialog', [ - this.promptStep.bind(this), - this.finalStep.bind(this) - ])); - - this.initialDialogId = 'WaterfallDialog'; - } - - async promptStep(stepContext) { - return await stepContext.prompt('TextPrompt', { prompt: MessageFactory.text('Hi there') }); - } - - async finalStep(stepContext) { - return await stepContext.endDialog(); - } -} - -describe('CancelAndHelpDialog', () => { - describe('Should be able to cancel', () => { - const testCases = ['cancel', 'quit']; - - testCases.map((testData) => { - it(testData, async () => { - const sut = new TestCancelAndHelpDialog(); - const client = new DialogTestClient('test', sut, null, [new DialogTestLogger()]); - - // Execute the test case - let reply = await client.sendActivity('Hi'); - assert.strictEqual(reply.text, 'Hi there'); - assert.strictEqual(client.dialogTurnResult.status, 'waiting'); - - reply = await client.sendActivity(testData); - assert.strictEqual(reply.text, 'Cancelling...'); - assert.strictEqual(client.dialogTurnResult.status, 'complete'); - }); - }); - }); - - describe('Should be able to get help', () => { - const testCases = ['help', '?']; - - testCases.map((testData) => { - it(testData, async () => { - const sut = new TestCancelAndHelpDialog(); - const client = new DialogTestClient('test', sut, null, [new DialogTestLogger()]); - - // Execute the test case - let reply = await client.sendActivity('Hi'); - assert.strictEqual(reply.text, 'Hi there'); - assert.strictEqual(client.dialogTurnResult.status, 'waiting'); - - reply = await client.sendActivity(testData); - assert.strictEqual(reply.text, 'Show help here'); - assert.strictEqual(client.dialogTurnResult.status, 'waiting'); - }); - }); - }); -}); diff --git a/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/dateResolverDialog.test.js b/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/dateResolverDialog.test.js deleted file mode 100644 index 4a63b9b66d..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/dateResolverDialog.test.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. - */ - -/* eslint-env node, mocha */ -const { DialogTestClient, DialogTestLogger } = require('botbuilder-testing'); -const { DateResolverDialog } = require('../../dialogs/dateResolverDialog'); -const assert = require('assert'); - -describe('DateResolverDialog', () => { - const testCases = require('./testData/dateResolverTestCases.js'); - const sut = new DateResolverDialog('dateResolver'); - - testCases.map(testData => { - it(testData.name, async () => { - const client = new DialogTestClient('test', sut, testData.initialData, [new DialogTestLogger()]); - - // Execute the test case - console.log(`Test Case: ${ testData.name }`); - console.log(`Dialog Input ${ JSON.stringify(testData.initialData) }`); - for (let i = 0; i < testData.steps.length; i++) { - const reply = await client.sendActivity(testData.steps[i][0]); - assert.strictEqual((reply ? reply.text : null), testData.steps[i][1], `${ reply ? reply.text : null } != ${ testData.steps[i][1] }`); - } - console.log(`Dialog result: ${ client.dialogTurnResult.result }`); - assert.strictEqual(client.dialogTurnResult.result, testData.expectedResult, `${ testData.expectedResult } != ${ client.dialogTurnResult.result }`); - }); - }); -}); diff --git a/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/dateResolverDialog.test.ts b/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/dateResolverDialog.test.ts deleted file mode 100644 index 8e35dbda1b..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/dateResolverDialog.test.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. - */ -import { DialogTestClient, DialogTestLogger } from 'botbuilder-testing'; -import { DateResolverDialog } from '../../dialogs/dateResolverDialog'; -const assert = require('assert'); - -describe('DateResolverDialog', () => { - const testCases = require('./testData/dateResolverTestCases'); - const sut = new DateResolverDialog('dateResolver'); - - testCases.map((testData) => { - it(testData.name, async () => { - const client = new DialogTestClient('test', sut, testData.initialData, [new DialogTestLogger()]); - - // Execute the test case - console.log(`Test Case: ${ testData.name }`); - console.log(`Dialog Input ${ JSON.stringify(testData.initialData) }`); - for (const step of testData.steps) { - const reply = await client.sendActivity(step[0]); - assert.strictEqual((reply ? reply.text : null), step[1], `${ reply ? reply.text : null } != ${ step[1] }`); - } - console.log(`Dialog result: ${ client.dialogTurnResult.result }`); - assert.strictEqual(client.dialogTurnResult.result, testData.expectedResult, `${ testData.expectedResult } != ${ client.dialogTurnResult.result }`); - }); - }); -}); diff --git a/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/mainDialog.test.js b/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/mainDialog.test.js deleted file mode 100644 index 34666bfe1f..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/mainDialog.test.js +++ /dev/null @@ -1,156 +0,0 @@ -/** - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. - */ - -/* eslint-env node, mocha */ -const { TextPrompt } = require('botbuilder-dialogs'); -const { DialogTestClient, DialogTestLogger } = require('botbuilder-testing'); -const { FlightBookingRecognizer } = require('../../dialogs/flightBookingRecognizer'); -const { MainDialog } = require('../../dialogs/mainDialog'); -const { BookingDialog } = require('../../dialogs/bookingDialog'); -const assert = require('assert'); - -/** - * A mock FlightBookingRecognizer for our main dialog tests that takes - * a mock luis result and can set as isConfigured === false. - */ -class MockFlightBookingRecognizer extends FlightBookingRecognizer { - constructor(isConfigured, mockResult) { - super(isConfigured); - this.isLuisConfigured = isConfigured; - this.mockResult = mockResult; - } - - async executeLuisQuery(context) { - return this.mockResult; - } - - get isConfigured() { - return (this.isLuisConfigured); - } -} - -/** - * A simple mock for Booking dialog that just returns a preset booking info for tests. - */ -class MockBookingDialog extends BookingDialog { - constructor() { - super('bookingDialog'); - } - - async beginDialog(dc, options) { - const bookingDetails = { - origin: 'New York', - destination: 'Seattle', - travelDate: '2025-07-08' - }; - await dc.context.sendActivity(`${ this.id } mock invoked`); - return await dc.endDialog(bookingDetails); - } -} - -/** -* A specialized mock for BookingDialog that displays a dummy TextPrompt. -* The dummy prompt is used to prevent the MainDialog waterfall from moving to the next step -* and assert that the main dialog was called. -*/ -class MockBookingDialogWithPrompt extends BookingDialog { - constructor() { - super('bookingDialog'); - } - - async beginDialog(dc, options) { - dc.dialogs.add(new TextPrompt('MockDialog')); - return await dc.prompt('MockDialog', { prompt: `${ this.id } mock invoked` }); - } -}; - -describe('MainDialog', () => { - it('Shows message if LUIS is not configured and calls BookingDialogDirectly', async () => { - const mockRecognizer = new MockFlightBookingRecognizer(false); - const mockBookingDialog = new MockBookingDialogWithPrompt(); - const sut = new MainDialog(mockRecognizer, mockBookingDialog); - const client = new DialogTestClient('test', sut, null, [new DialogTestLogger()]); - - const reply = await client.sendActivity('hi'); - assert.strictEqual(reply.text, 'NOTE: LUIS is not configured. To enable all capabilities, add `LuisAppId`, `LuisAPIKey` and `LuisAPIHostName` to the .env file.', 'Did not warn about missing luis'); - }); - - it('Shows prompt if LUIS is configured', async () => { - const mockRecognizer = new MockFlightBookingRecognizer(true); - const mockBookingDialog = new MockBookingDialog(); - const sut = new MainDialog(mockRecognizer, mockBookingDialog); - const client = new DialogTestClient('test', sut, null, [new DialogTestLogger()]); - - const reply = await client.sendActivity('hi'); - assert.strictEqual(reply.text, 'What can I help you with today?\nSay something like "Book a flight from Paris to Berlin on March 22, 2020"', 'Did not show prompt'); - }); - - describe('Invokes tasks based on LUIS intent', () => { - // Create array with test case data. - const testCases = [ - { utterance: 'I want to book a flight', intent: 'BookFlight', invokedDialogResponse: 'bookingDialog mock invoked', taskConfirmationMessage: 'I have you booked to Seattle from New York' }, - { utterance: 'What\'s the weather like?', intent: 'GetWeather', invokedDialogResponse: 'TODO: get weather flow here', taskConfirmationMessage: undefined }, - { utterance: 'bananas', intent: 'None', invokedDialogResponse: 'Sorry, I didn\'t get that. Please try asking in a different way (intent was None)', taskConfirmationMessage: undefined } - ]; - - testCases.map(testData => { - it(testData.intent, async () => { - // Create LuisResult for the mock recognizer. - const mockLuisResult = JSON.parse(`{"intents": {"${ testData.intent }": {"score": 1}}, "entities": {"$instance": {}}}`); - const mockRecognizer = new MockFlightBookingRecognizer(true, mockLuisResult); - const bookingDialog = new MockBookingDialog(); - const sut = new MainDialog(mockRecognizer, bookingDialog); - const client = new DialogTestClient('test', sut, null, [new DialogTestLogger()]); - - // Execute the test case - console.log(`Test Case: ${ testData.intent }`); - let reply = await client.sendActivity('Hi'); - assert.strictEqual(reply.text, 'What can I help you with today?\nSay something like "Book a flight from Paris to Berlin on March 22, 2020"'); - - reply = await client.sendActivity(testData.utterance); - assert.strictEqual(reply.text, testData.invokedDialogResponse); - - // The Booking dialog displays an additional confirmation message, assert that it is what we expect. - if (testData.taskConfirmationMessage) { - reply = client.getNextReply(); - assert(reply.text.startsWith(testData.taskConfirmationMessage)); - } - - // Validate that the MainDialog starts over once the task is completed. - reply = client.getNextReply(); - assert.strictEqual(reply.text, 'What else can I do for you?'); - }); - }); - }); - - describe('Shows unsupported cities warning', () => { - // Create array with test case data. - const testCases = [ - { jsonFile: 'FlightToMadrid.json', expectedMessage: 'Sorry but the following airports are not supported: madrid' }, - { jsonFile: 'FlightFromMadridToChicago.json', expectedMessage: 'Sorry but the following airports are not supported: madrid, chicago' }, - { jsonFile: 'FlightFromCdgToJfk.json', expectedMessage: 'Sorry but the following airports are not supported: cdg' }, - { jsonFile: 'FlightFromParisToNewYork.json', expectedMessage: 'bookingDialog mock invoked' } - ]; - - testCases.map(testData => { - it(testData.jsonFile, async () => { - // Create LuisResult for the mock recognizer. - const mockLuisResult = require(`./testData/${ testData.jsonFile }`); - const mockRecognizer = new MockFlightBookingRecognizer(true, mockLuisResult); - const bookingDialog = new MockBookingDialog(); - const sut = new MainDialog(mockRecognizer, bookingDialog); - const client = new DialogTestClient('test', sut, null, [new DialogTestLogger()]); - - // Execute the test case - console.log(`Test Case: ${ mockLuisResult.text }`); - let reply = await client.sendActivity('Hi'); - assert.strictEqual(reply.text, 'What can I help you with today?\nSay something like "Book a flight from Paris to Berlin on March 22, 2020"'); - - reply = await client.sendActivity(mockLuisResult.text); - assert.strictEqual(reply.text, testData.expectedMessage); - }); - }); - }); -}); diff --git a/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/mainDialog.test.ts b/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/mainDialog.test.ts deleted file mode 100644 index f8f1f34e56..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/mainDialog.test.ts +++ /dev/null @@ -1,156 +0,0 @@ -/** - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. - */ -import { TextPrompt } from 'botbuilder-dialogs'; -import { DialogTestClient, DialogTestLogger } from 'botbuilder-testing'; -import { BookingDialog } from '../../dialogs/bookingDialog'; -import { FlightBookingRecognizer } from '../../dialogs/flightBookingRecognizer'; -import { MainDialog } from '../../dialogs/mainDialog'; -const assert = require('assert'); - -// tslint:disable max-classes-per-file -/** - * A mock FlightBookingRecognizer for our main dialog tests that takes - * a mock luis result and can set as isConfigured === false. - */ -class MockFlightBookingRecognizer extends FlightBookingRecognizer { - private isLuisConfigured: boolean; - constructor(isConfigured, private mockResult?: any) { - super(isConfigured); - this.isLuisConfigured = isConfigured; - this.mockResult = mockResult; - } - - async executeLuisQuery(context) { - return this.mockResult; - } - - get isConfigured() { - return (this.isLuisConfigured); - } -} - -/** - * A simple mock for Booking dialog that just returns a preset booking info for tests. - */ -class MockBookingDialog extends BookingDialog { - constructor() { - super('bookingDialog'); - } - - async beginDialog(dc, options) { - const bookingDetails = { - destination: 'Seattle', - origin: 'New York', - travelDate: '2025-07-08' - }; - await dc.context.sendActivity(`${ this.id } mock invoked`); - return await dc.endDialog(bookingDetails); - } -} - -/** - * A specialized mock for BookingDialog that displays a dummy TextPrompt. - * The dummy prompt is used to prevent the MainDialog waterfall from moving to the next step - * and assert that the main dialog was called. - */ -class MockBookingDialogWithPrompt extends BookingDialog { - constructor() { - super('bookingDialog'); - } - - async beginDialog(dc, options) { - dc.dialogs.add(new TextPrompt('MockDialog')); - return await dc.prompt('MockDialog', { prompt: `${ this.id } mock invoked` }); - } -} - -describe('MainDialog', () => { - it('Shows message if LUIS is not configured and calls BookingDialogDirectly', async () => { - const mockRecognizer = new MockFlightBookingRecognizer(false); - const mockBookingDialog = new MockBookingDialogWithPrompt(); - const sut = new MainDialog(mockRecognizer, mockBookingDialog); - const client = new DialogTestClient('test', sut, null, [new DialogTestLogger()]); - - const reply = await client.sendActivity('hi'); - assert.strictEqual(reply.text, 'NOTE: LUIS is not configured. To enable all capabilities, add `LuisAppId`, `LuisAPIKey` and `LuisAPIHostName` to the .env file.', 'Did not warn about missing luis'); - }); - - it('Shows prompt if LUIS is configured', async () => { - const mockRecognizer = new MockFlightBookingRecognizer(true); - const mockBookingDialog = new MockBookingDialog(); - const sut = new MainDialog(mockRecognizer, mockBookingDialog); - const client = new DialogTestClient('test', sut, null, [new DialogTestLogger()]); - - const reply = await client.sendActivity('hi'); - assert.strictEqual(reply.text, 'What can I help you with today?\nSay something like "Book a flight from Paris to Berlin on March 22, 2020"', 'Did not show prompt'); - }); - - describe('Invokes tasks based on LUIS intent', () => { - // Create array with test case data. - const testCases = [ - { utterance: 'I want to book a flight', intent: 'BookFlight', invokedDialogResponse: 'bookingDialog mock invoked', taskConfirmationMessage: 'I have you booked to Seattle from New York' }, - { utterance: `What's the weather like?`, intent: 'GetWeather', invokedDialogResponse: 'TODO: get weather flow here', taskConfirmationMessage: undefined }, - { utterance: 'bananas', intent: 'None', invokedDialogResponse: `Sorry, I didn't get that. Please try asking in a different way (intent was None)`, taskConfirmationMessage: undefined } - ]; - - testCases.map((testData) => { - it(testData.intent, async () => { - // Create LuisResult for the mock recognizer. - const mockLuisResult = JSON.parse(`{"intents": {"${ testData.intent }": {"score": 1}}, "entities": {"$instance": {}}}`); - const mockRecognizer = new MockFlightBookingRecognizer(true, mockLuisResult); - const bookingDialog = new MockBookingDialog(); - const sut = new MainDialog(mockRecognizer, bookingDialog); - const client = new DialogTestClient('test', sut, null, [new DialogTestLogger()]); - - // Execute the test case - console.log(`Test Case: ${ testData.intent }`); - let reply = await client.sendActivity('Hi'); - assert.strictEqual(reply.text, 'What can I help you with today?\nSay something like "Book a flight from Paris to Berlin on March 22, 2020"'); - - reply = await client.sendActivity(testData.utterance); - assert.strictEqual(reply.text, testData.invokedDialogResponse); - - // The Booking dialog displays an additional confirmation message, assert that it is what we expect. - if (testData.taskConfirmationMessage) { - reply = client.getNextReply(); - assert(reply.text.startsWith(testData.taskConfirmationMessage)); - } - - // Validate that the MainDialog starts over once the task is completed. - reply = client.getNextReply(); - assert.strictEqual(reply.text, 'What else can I do for you?'); - }); - }); - }); - - describe('Shows unsupported cities warning', () => { - // Create array with test case data. - const testCases = [ - { jsonFile: 'FlightToMadrid.json', expectedMessage: 'Sorry but the following airports are not supported: madrid' }, - { jsonFile: 'FlightFromMadridToChicago.json', expectedMessage: 'Sorry but the following airports are not supported: madrid, chicago' }, - { jsonFile: 'FlightFromCdgToJfk.json', expectedMessage: 'Sorry but the following airports are not supported: cdg' }, - { jsonFile: 'FlightFromParisToNewYork.json', expectedMessage: 'bookingDialog mock invoked' } - ]; - - testCases.map((testData) => { - it(testData.jsonFile, async () => { - // Create LuisResult for the mock recognizer. - const mockLuisResult = require(`../../../testResources/${ testData.jsonFile }`); - const mockRecognizer = new MockFlightBookingRecognizer(true, mockLuisResult); - const bookingDialog = new MockBookingDialog(); - const sut = new MainDialog(mockRecognizer, bookingDialog); - const client = new DialogTestClient('test', sut, null, [new DialogTestLogger()]); - - // Execute the test case - console.log(`Test Case: ${ mockLuisResult.text }`); - let reply = await client.sendActivity('Hi'); - assert.strictEqual(reply.text, 'What can I help you with today?\nSay something like "Book a flight from Paris to Berlin on March 22, 2020"'); - - reply = await client.sendActivity(mockLuisResult.text); - assert.strictEqual(reply.text, testData.expectedMessage); - }); - }); - }); -}); diff --git a/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/testData/FlightFromCdgToJfk.json b/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/testData/FlightFromCdgToJfk.json deleted file mode 100644 index 679604f79c..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/testData/FlightFromCdgToJfk.json +++ /dev/null @@ -1,103 +0,0 @@ -{ - "text": "flight from cdg to jfk", - "intents": { - "BookFlight": { - "score": 0.712058365 - } - }, - "entities": { - "$instance": { - "From": [ - { - "startIndex": 12, - "endIndex": 15, - "score": 0.9553623, - "text": "cdg", - "type": "From" - } - ], - "To": [ - { - "startIndex": 19, - "endIndex": 22, - "score": 0.8905674, - "text": "jfk", - "type": "To" - } - ] - }, - "From": [ { "$instance": {} } ], - - "To": [ - { - "$instance": { - "Airport": [ - { - "startIndex": 19, - "endIndex": 22, - "text": "jfk", - "type": "Airport" - } - ] - }, - "Airport": [ - [ - "New York" - ] - ] - } - ] - }, - "luisResult": { - "query": "flight from cdg to jfk", - "topScoringIntent": { - "intent": "BookFlight", - "score": 0.712058365 - }, - "entities": [ - { - "entity": "cdg", - "type": "From", - "startIndex": 12, - "endIndex": 14, - "score": 0.9553623 - }, - { - "entity": "jfk", - "type": "To", - "startIndex": 19, - "endIndex": 21, - "score": 0.8905674 - }, - { - "entity": "jfk", - "type": "Airport", - "startIndex": 19, - "endIndex": 21, - "resolution": { - "values": [ - "New York" - ] - } - } - ], - "compositeEntities": [ - { - "parentType": "From", - "value": "cdg", - "children": [] - - }, - { - "parentType": "To", - "value": "jfk", - "children": [ - { - "type": "Airport", - "value": "jfk" - } - ] - } - ] - } -} \ No newline at end of file diff --git a/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/testData/FlightFromMadridToChicago.json b/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/testData/FlightFromMadridToChicago.json deleted file mode 100644 index a001904c8f..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/testData/FlightFromMadridToChicago.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "text": "flight from madrid to chicago", - "intents": { "BookFlight": { "score": 0.8837919 } }, - "entities": { - "$instance": { - "From": [ - { - "startIndex": 12, - "endIndex": 18, - "score": 0.888236344, - "text": "madrid", - "type": "From" - } - ], - "To": [ - { - "startIndex": 22, - "endIndex": 29, - "score": 0.640484631, - "text": "chicago", - "type": "To" - } - ] - }, - "From": [ { "$instance": {} } ], - "To": [ { "$instance": {} } ] - }, - "luisResult": { - "query": "flight from madrid to chicago", - "topScoringIntent": { - "intent": "BookFlight", - "score": 0.8837919 - }, - "entities": [ - { - "entity": "madrid", - "type": "From", - "startIndex": 12, - "endIndex": 17, - "score": 0.888236344 - }, - { - "entity": "chicago", - "type": "To", - "startIndex": 22, - "endIndex": 28, - "score": 0.640484631 - } - ], - "compositeEntities": [ - { - "parentType": "From", - "value": "madrid", - "children": [] - }, - { - "parentType": "To", - "value": "chicago", - "children": [] - } - ] - } -} \ No newline at end of file diff --git a/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/testData/FlightFromParisToNewYork.json b/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/testData/FlightFromParisToNewYork.json deleted file mode 100644 index be15e91eb6..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/testData/FlightFromParisToNewYork.json +++ /dev/null @@ -1,115 +0,0 @@ -{ - "text": "flight from paris to new york", - "intents": { "BookFlight": { "score": 0.9953049 } }, - "entities": { - "$instance": { - "From": [ - { - "startIndex": 12, - "endIndex": 17, - "score": 0.94712317, - "text": "paris", - "type": "From" - } - ], - "To": [ - { - "startIndex": 21, - "endIndex": 29, - "score": 0.8602996, - "text": "new york", - "type": "To" - } - ] - }, - "From": [ - { - "$instance": { - "Airport": [ - { - "startIndex": 12, - "endIndex": 17, - "text": "paris", - "type": "Airport" - } - ] - }, - "Airport": [ [ "Paris" ] ] - } - ], - "To": [ - { - "$instance": { - "Airport": [ - { - "startIndex": 21, - "endIndex": 29, - "text": "new york", - "type": "Airport" - } - ] - }, - "Airport": [ [ "New York" ] ] - } - ] - }, - "luisResult": { - "query": "flight from paris to new york", - "topScoringIntent": { - "intent": "BookFlight", - "score": 0.9953049 - }, - "entities": [ - { - "entity": "paris", - "type": "From", - "startIndex": 12, - "endIndex": 16, - "score": 0.94712317 - }, - { - "entity": "new york", - "type": "To", - "startIndex": 21, - "endIndex": 28, - "score": 0.8602996 - }, - { - "entity": "paris", - "type": "Airport", - "startIndex": 12, - "endIndex": 16, - "resolution": { "values": [ "Paris" ] } - }, - { - "entity": "new york", - "type": "Airport", - "startIndex": 21, - "endIndex": 28, - "resolution": { "values": [ "New York" ] } - } - ], - "compositeEntities": [ - { - "parentType": "From", - "value": "paris", - "children": [ - { - "type": "Airport", - "value": "paris" - } - ] - }, - { - "parentType": "To", - "value": "new york", - "children": [ - { - "type": "Airport", - "value": "new york" - } - ] - } - ] - } -} \ No newline at end of file diff --git a/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/testData/FlightToMadrid.json b/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/testData/FlightToMadrid.json deleted file mode 100644 index 992d8f1ed9..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/testData/FlightToMadrid.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "text": "flight to madrid", - "intents": { "BookFlight": { "score": 0.8476145 } }, - "entities": { - "$instance": { - "To": [ - { - "startIndex": 10, - "endIndex": 16, - "score": 0.7892059, - "text": "madrid", - "type": "To" - } - ] - }, - "To": [ { "$instance": {} } ] - }, - "luisResult": { - "query": "flight to madrid", - "topScoringIntent": { - "intent": "BookFlight", - "score": 0.8476145 - }, - "entities": [ - { - "entity": "madrid", - "type": "To", - "startIndex": 10, - "endIndex": 15, - "score": 0.7892059 - } - ], - "compositeEntities": [ - { - "parentType": "To", - "value": "madrid", - "children": [] - } - ] - } -} \ No newline at end of file diff --git a/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/testData/bookingDialogTestCases.js b/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/testData/bookingDialogTestCases.js deleted file mode 100644 index 4869b1e203..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/testData/bookingDialogTestCases.js +++ /dev/null @@ -1,152 +0,0 @@ -/** - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. - */ -const now = new Date(); -const today = formatDate(new Date()); -const tomorrow = formatDate(new Date().setDate(now.getDate() + 1)); - -function formatDate(date) { - const d = new Date(date); - let month = '' + (d.getMonth() + 1); - let day = '' + d.getDate(); - const year = d.getFullYear(); - - if (month.length < 2) month = '0' + month; - if (day.length < 2) day = '0' + day; - - return [year, month, day].join('-'); -} - -module.exports = [ - { - name: 'Full flow', - initialData: {}, - steps: [ - ['hi', 'To what city would you like to travel?'], - ['Seattle', 'From what city will you be travelling?'], - ['New York', 'On what date would you like to travel?'], - ['tomorrow', `Please confirm, I have you traveling to: Seattle from: New York on: ${ tomorrow }. Is this correct? (1) Yes or (2) No`], - ['yes', null] - ], - expectedStatus: 'complete', - expectedResult: { - destination: 'Seattle', - origin: 'New York', - travelDate: tomorrow - } - }, - { - name: 'Full flow with \'no\' at confirmation', - initialData: {}, - steps: [ - ['hi', 'To what city would you like to travel?'], - ['Seattle', 'From what city will you be travelling?'], - ['New York', 'On what date would you like to travel?'], - ['tomorrow', `Please confirm, I have you traveling to: Seattle from: New York on: ${ tomorrow }. Is this correct? (1) Yes or (2) No`], - ['no', null] - ], - expectedStatus: 'complete', - expectedResult: undefined - }, - { - name: 'Destination given', - initialData: { - destination: 'Bahamas' - }, - steps: [ - ['hi', 'From what city will you be travelling?'], - ['New York', 'On what date would you like to travel?'], - ['tomorrow', `Please confirm, I have you traveling to: Bahamas from: New York on: ${ tomorrow }. Is this correct? (1) Yes or (2) No`], - ['yes', null] - ], - expectedStatus: 'complete', - expectedResult: { - origin: 'New York', - destination: 'Bahamas', - travelDate: tomorrow - } - }, - { - name: 'Destination and origin given', - initialData: { - destination: 'Seattle', - origin: 'New York' - }, - steps: [ - ['hi', 'On what date would you like to travel?'], - ['tomorrow', `Please confirm, I have you traveling to: Seattle from: New York on: ${ tomorrow }. Is this correct? (1) Yes or (2) No`], - ['yes', null] - ], - expectedStatus: 'complete', - expectedResult: { - destination: 'Seattle', - origin: 'New York', - travelDate: tomorrow - } - }, - { - name: 'All booking details given for today', - initialData: { - destination: 'Seattle', - origin: 'Bahamas', - travelDate: today - }, - steps: [ - ['hi', `Please confirm, I have you traveling to: Seattle from: Bahamas on: ${ today }. Is this correct? (1) Yes or (2) No`], - ['yes', null] - ], - expectedStatus: 'complete', - expectedResult: { - destination: 'Seattle', - origin: 'Bahamas', - travelDate: today - } - }, - { - name: 'Cancel on origin prompt', - initialData: {}, - steps: [ - ['hi', 'To what city would you like to travel?'], - ['cancel', 'Cancelling...'] - ], - expectedStatus: 'complete', - expectedResult: undefined - }, - { - name: 'Cancel on destination prompt', - initialData: {}, - steps: [ - ['hi', 'To what city would you like to travel?'], - ['Seattle', 'From what city will you be travelling?'], - ['cancel', 'Cancelling...'] - ], - expectedStatus: 'complete', - expectedResult: undefined - }, - { - name: 'Cancel on date prompt', - initialData: {}, - steps: [ - ['hi', 'To what city would you like to travel?'], - ['Seattle', 'From what city will you be travelling?'], - ['New York', 'On what date would you like to travel?'], - ['cancel', 'Cancelling...'] - ], - expectedStatus: 'complete', - expectedResult: undefined - }, - { - name: 'Cancel on confirm prompt', - initialData: {}, - steps: [ - ['hi', 'To what city would you like to travel?'], - ['Seattle', 'From what city will you be travelling?'], - ['New York', 'On what date would you like to travel?'], - ['tomorrow', `Please confirm, I have you traveling to: Seattle from: New York on: ${ tomorrow }. Is this correct? (1) Yes or (2) No`], - ['cancel', 'Cancelling...'] - ], - expectedStatus: 'complete', - expectedResult: undefined - } -]; diff --git a/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/testData/bookingDialogTestCases.ts b/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/testData/bookingDialogTestCases.ts deleted file mode 100644 index a42457e6cc..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/testData/bookingDialogTestCases.ts +++ /dev/null @@ -1,152 +0,0 @@ -/** - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. - */ -const bookingDialogNow = new Date(); -const bookingDialogToday = formatBookingDialogDate(new Date()); -const bookingDialogTomorrow = formatBookingDialogDate(new Date().setDate(bookingDialogNow.getDate() + 1)); - -function formatBookingDialogDate(date) { - const d = new Date(date); - let month = '' + (d.getMonth() + 1); - let day = '' + d.getDate(); - const year = d.getFullYear(); - - if (month.length < 2) month = '0' + month; - if (day.length < 2) day = '0' + day; - - return [year, month, day].join('-'); -} - -module.exports = [ - { - expectedResult: { - destination: 'Seattle', - origin: 'New York', - travelDate: bookingDialogTomorrow - }, - expectedStatus: 'complete', - initialData: {}, - name: 'Full flow', - steps: [ - ['hi', 'To what city would you like to travel?'], - ['Seattle', 'From what city will you be travelling?'], - ['New York', 'On what date would you like to travel?'], - ['tomorrow', `Please confirm, I have you traveling to: Seattle from: New York on: ${ bookingDialogTomorrow }. Is this correct? (1) Yes or (2) No`], - ['yes', null] - ] - }, - { - expectedResult: undefined, - expectedStatus: 'complete', - initialData: {}, - name: 'Full flow with \'no\' at confirmation', - steps: [ - ['hi', 'To what city would you like to travel?'], - ['Seattle', 'From what city will you be travelling?'], - ['New York', 'On what date would you like to travel?'], - ['tomorrow', `Please confirm, I have you traveling to: Seattle from: New York on: ${ bookingDialogTomorrow }. Is this correct? (1) Yes or (2) No`], - ['no', null] - ] - }, - { - expectedResult: { - destination: 'Bahamas', - origin: 'New York', - travelDate: bookingDialogTomorrow - }, - expectedStatus: 'complete', - initialData: { - destination: 'Bahamas' - }, - name: 'Destination given', - steps: [ - ['hi', 'From what city will you be travelling?'], - ['New York', 'On what date would you like to travel?'], - ['tomorrow', `Please confirm, I have you traveling to: Bahamas from: New York on: ${ bookingDialogTomorrow }. Is this correct? (1) Yes or (2) No`], - ['yes', null] - ] - }, - { - expectedResult: { - destination: 'Seattle', - origin: 'New York', - travelDate: bookingDialogTomorrow - }, - expectedStatus: 'complete', - initialData: { - destination: 'Seattle', - origin: 'New York' - }, - name: 'Destination and origin given', - steps: [ - ['hi', 'On what date would you like to travel?'], - ['tomorrow', `Please confirm, I have you traveling to: Seattle from: New York on: ${ bookingDialogTomorrow }. Is this correct? (1) Yes or (2) No`], - ['yes', null] - ] - }, - { - expectedResult: { - destination: 'Seattle', - origin: 'Bahamas', - travelDate: bookingDialogToday - }, - expectedStatus: 'complete', - initialData: { - destination: 'Seattle', - origin: 'Bahamas', - travelDate: bookingDialogToday - }, - name: 'All booking details given for today', - steps: [ - ['hi', `Please confirm, I have you traveling to: Seattle from: Bahamas on: ${ bookingDialogToday }. Is this correct? (1) Yes or (2) No`], - ['yes', null] - ] - }, - { - expectedResult: undefined, - expectedStatus: 'complete', - initialData: {}, - name: 'Cancel on origin prompt', - steps: [ - ['hi', 'To what city would you like to travel?'], - ['cancel', 'Cancelling...'] - ] - }, - { - expectedResult: undefined, - expectedStatus: 'complete', - initialData: {}, - name: 'Cancel on destination prompt', - steps: [ - ['hi', 'To what city would you like to travel?'], - ['Seattle', 'From what city will you be travelling?'], - ['cancel', 'Cancelling...'] - ] - }, - { - expectedResult: undefined, - expectedStatus: 'complete', - initialData: {}, - name: 'Cancel on date prompt', - steps: [ - ['hi', 'To what city would you like to travel?'], - ['Seattle', 'From what city will you be travelling?'], - ['New York', 'On what date would you like to travel?'], - ['cancel', 'Cancelling...'] - ] - }, - { - expectedResult: undefined, - expectedStatus: 'complete', - initialData: {}, - name: 'Cancel on confirm prompt', - steps: [ - ['hi', 'To what city would you like to travel?'], - ['Seattle', 'From what city will you be travelling?'], - ['New York', 'On what date would you like to travel?'], - ['tomorrow', `Please confirm, I have you traveling to: Seattle from: New York on: ${ bookingDialogTomorrow }. Is this correct? (1) Yes or (2) No`], - ['cancel', 'Cancelling...'] - ] - } -]; diff --git a/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/testData/dateResolverTestCases.js b/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/testData/dateResolverTestCases.js deleted file mode 100644 index 66857238f8..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/testData/dateResolverTestCases.js +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. - */ -const now = new Date(); -const tomorrow = formatDate(new Date().setDate(now.getDate() + 1)); -const dayAfterTomorrow = formatDate(new Date().setDate(now.getDate() + 2)); - -function formatDate(date) { - const d = new Date(date); - let month = '' + (d.getMonth() + 1); - let day = '' + d.getDate(); - const year = d.getFullYear(); - - if (month.length < 2) month = '0' + month; - if (day.length < 2) day = '0' + day; - - return [year, month, day].join('-'); -} - -module.exports = [ - { - name: 'tomorrow', - initialData: null, - steps: [ - ['hi', 'On what date would you like to travel?'], - ['tomorrow', null] - ], - expectedResult: tomorrow - }, - { - name: 'the day after tomorrow', - initialData: null, - steps: [ - ['hi', 'On what date would you like to travel?'], - ['the day after tomorrow', null] - ], - expectedResult: dayAfterTomorrow - }, - { - name: 'two days from now', - initialData: null, - steps: [ - ['hi', 'On what date would you like to travel?'], - ['two days from now', null] - ], - expectedResult: dayAfterTomorrow - }, - { - name: 'valid input given (tomorrow)', - initialData: { date: tomorrow }, - steps: [ - ['hi', null] - ], - expectedResult: tomorrow - }, - { - name: 'retry prompt', - initialData: {}, - steps: [ - ['hi', 'On what date would you like to travel?'], - ['bananas', 'I\'m sorry, for best results, please enter your travel date including the month, day and year.'], - ['tomorrow', null] - ], - expectedResult: tomorrow - }, - { - name: 'fuzzy time', - initialData: {}, - steps: [ - ['hi', 'On what date would you like to travel?'], - ['may 5th', 'I\'m sorry, for best results, please enter your travel date including the month, day and year.'], - ['may 5th 2055', null] - ], - expectedResult: '2055-05-05' - } -]; diff --git a/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/testData/dateResolverTestCases.ts b/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/testData/dateResolverTestCases.ts deleted file mode 100644 index 241a430836..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/tests/dialogs/testData/dateResolverTestCases.ts +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. - */ -const now = new Date(); -const tomorrow = formatDate(new Date().setDate(now.getDate() + 1)); -const dayAfterTomorrow = formatDate(new Date().setDate(now.getDate() + 2)); - -function formatDate(date) { - const d = new Date(date); - let month = '' + (d.getMonth() + 1); - let day = '' + d.getDate(); - const year = d.getFullYear(); - - if (month.length < 2) month = '0' + month; - if (day.length < 2) day = '0' + day; - - return [year, month, day].join('-'); -} - -module.exports = [ - { - expectedResult: tomorrow, - initialData: null, - name: 'tomorrow', - steps: [ - ['hi', 'On what date would you like to travel?'], - ['tomorrow', null] - ] - }, - { - expectedResult: dayAfterTomorrow, - initialData: null, - name: 'the day after tomorrow', - steps: [ - ['hi', 'On what date would you like to travel?'], - ['the day after tomorrow', null] - ] - }, - { - expectedResult: dayAfterTomorrow, - initialData: null, - name: 'two days from now', - steps: [ - ['hi', 'On what date would you like to travel?'], - ['two days from now', null] - ] - }, - { - expectedResult: tomorrow, - initialData: { date: tomorrow }, - name: 'valid input given (tomorrow)', - steps: [ - ['hi', null] - ] - }, - { - expectedResult: tomorrow, - initialData: {}, - name: 'retry prompt', - steps: [ - ['hi', 'On what date would you like to travel?'], - ['bananas', 'I\'m sorry, for best results, please enter your travel date including the month, day and year.'], - ['tomorrow', null] - ] - }, - { - expectedResult: '2055-05-05', - initialData: {}, - name: 'fuzzy time', - steps: [ - ['hi', 'On what date would you like to travel?'], - ['may 5th', 'I\'m sorry, for best results, please enter your travel date including the month, day and year.'], - ['may 5th 2055', null] - ] - } -]; diff --git a/generators/generator-botbuilder/generators/app/templates/core/tsconfig.json b/generators/generator-botbuilder/generators/app/templates/core/tsconfig.json deleted file mode 100644 index 7c70e81a34..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/tsconfig.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "compilerOptions": { - "composite": true, - "declaration": true, - "target": "es2017", - "module": "NodeNext", - "moduleResolution": "NodeNext", - "outDir": "./lib", - "rootDir": "./src", - "resolveJsonModule": true, - "rootDirs": ["./src", "./resources"], - "sourceMap": true, - "incremental": true, - "tsBuildInfoFile": "./lib/.tsbuildinfo", - "esModuleInterop": true - } -} diff --git a/generators/generator-botbuilder/generators/app/templates/core/tslint.json b/generators/generator-botbuilder/generators/app/templates/core/tslint.json deleted file mode 100644 index ad00715f85..0000000000 --- a/generators/generator-botbuilder/generators/app/templates/core/tslint.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "defaultSeverity": "error", - "extends": [ - "tslint:recommended" - ], - "jsRules": {}, - "rules": { - "interface-name" : [true, "never-prefix"], - "max-line-length": [false], - "no-console": [false, "log", "error"], - "no-var-requires": false, - "quotemark": [true, "single"], - "one-variable-per-declaration": false, - "curly": [true, "ignore-same-line"], - "trailing-comma": [true, {"multiline": "never", "singleline": "never"}] - }, - "rulesDirectory": [] -} diff --git a/generators/generator-botbuilder/generators/app/templates/echo/bot.js b/generators/generator-botbuilder/generators/app/templates/echo/bot.js index 2c4c88e95c..14f16cd9ec 100644 --- a/generators/generator-botbuilder/generators/app/templates/echo/bot.js +++ b/generators/generator-botbuilder/generators/app/templates/echo/bot.js @@ -12,7 +12,7 @@ class EchoBot extends ActivityHandler { }); this.onMembersAdded(async (context, next) => { - const membersAdded = context.activity.membersAdded; + const membersAdded = context.activity.membersAdded ?? []; const welcomeText = 'Hello and welcome!'; for (let cnt = 0; cnt < membersAdded.length; ++cnt) { if (membersAdded[cnt].id !== context.activity.recipient.id) { diff --git a/generators/generator-botbuilder/generators/app/templates/echo/index.js b/generators/generator-botbuilder/generators/app/templates/echo/index.js index ebfeaeeb76..ea198f1f8c 100644 --- a/generators/generator-botbuilder/generators/app/templates/echo/index.js +++ b/generators/generator-botbuilder/generators/app/templates/echo/index.js @@ -11,8 +11,7 @@ const restify = require('restify'); // See https://aka.ms/bot-services to learn more about the different parts of a bot. const { CloudAdapter, - ConfigurationServiceClientCredentialFactory, - createBotFrameworkAuthenticationFromConfiguration + ConfigurationBotFrameworkAuthentication } = require('botbuilder'); // This bot's main dialog. @@ -28,14 +27,7 @@ server.listen(process.env.port || process.env.PORT || 3978, () => { console.log('\nTo talk to your bot, open the emulator select "Open Bot"'); }); -const credentialsFactory = new ConfigurationServiceClientCredentialFactory({ - MicrosoftAppId: process.env.MicrosoftAppId, - MicrosoftAppPassword: process.env.MicrosoftAppPassword, - MicrosoftAppType: process.env.MicrosoftAppType, - MicrosoftAppTenantId: process.env.MicrosoftAppTenantId -}); - -const botFrameworkAuthentication = createBotFrameworkAuthenticationFromConfiguration(null, credentialsFactory); +const botFrameworkAuthentication = new ConfigurationBotFrameworkAuthentication(process.env); // Create adapter. // See https://aka.ms/about-bot-adapter to learn more about adapters. @@ -45,7 +37,8 @@ const adapter = new CloudAdapter(botFrameworkAuthentication); const onTurnErrorHandler = async (context, error) => { // This check writes out errors to console log .vs. app insights. // NOTE: In production environment, you should consider logging this to Azure - // application insights. + // application insights. See https://aka.ms/bottelemetry for telemetry + // configuration instructions. console.error(`\n [onTurnError] unhandled error: ${ error }`); // Send a trace activity, which will be displayed in Bot Framework Emulator diff --git a/generators/generator-botbuilder/generators/app/templates/echo/index.ts b/generators/generator-botbuilder/generators/app/templates/echo/index.ts index fe89d48876..0717124cf5 100644 --- a/generators/generator-botbuilder/generators/app/templates/echo/index.ts +++ b/generators/generator-botbuilder/generators/app/templates/echo/index.ts @@ -12,14 +12,13 @@ import { INodeSocket } from 'botframework-streaming'; // See https://aka.ms/bot-services to learn more about the different parts of a bot. import { CloudAdapter, - ConfigurationServiceClientCredentialFactory, - createBotFrameworkAuthenticationFromConfiguration + ConfigurationBotFrameworkAuthentication, + ConfigurationBotFrameworkAuthenticationOptions } from 'botbuilder'; // This bot's main dialog. import { EchoBot } from './bot'; - // Create HTTP server. const server = restify.createServer(); server.use(restify.plugins.bodyParser()); @@ -30,14 +29,9 @@ server.listen(process.env.port || process.env.PORT || 3978, () => { console.log('\nTo talk to your bot, open the emulator select "Open Bot"'); }); -const credentialsFactory = new ConfigurationServiceClientCredentialFactory({ - MicrosoftAppId: process.env.MicrosoftAppId, - MicrosoftAppPassword: process.env.MicrosoftAppPassword, - MicrosoftAppType: process.env.MicrosoftAppType, - MicrosoftAppTenantId: process.env.MicrosoftAppTenantId -}); - -const botFrameworkAuthentication = createBotFrameworkAuthenticationFromConfiguration(null, credentialsFactory); +const botFrameworkAuthentication = new ConfigurationBotFrameworkAuthentication( + process.env as ConfigurationBotFrameworkAuthenticationOptions +); // Create adapter. // See https://aka.ms/about-bot-adapter to learn more about adapters. diff --git a/generators/generator-botbuilder/generators/app/templates/echo/package.json.js b/generators/generator-botbuilder/generators/app/templates/echo/package.json.js index 775968502c..d5b8d7f888 100644 --- a/generators/generator-botbuilder/generators/app/templates/echo/package.json.js +++ b/generators/generator-botbuilder/generators/app/templates/echo/package.json.js @@ -2,7 +2,7 @@ "name": "<%= botname %>", "version": "4.1.6", "description": "<%= botDescription %>", - "author": "Generated using Microsoft Bot Builder Yeoman generator v<%= version %>", + "author": "Generated using Microsoft Bot Builder Yeoman generator v5", "license": "MIT", "main": "<%= npmMain %>", "scripts": { diff --git a/generators/generator-botbuilder/generators/app/templates/echo/package.json.ts b/generators/generator-botbuilder/generators/app/templates/echo/package.json.ts index a585870422..4e570a77c0 100644 --- a/generators/generator-botbuilder/generators/app/templates/echo/package.json.ts +++ b/generators/generator-botbuilder/generators/app/templates/echo/package.json.ts @@ -2,7 +2,7 @@ "name": "<%= botname %>", "version": "4.1.6", "description": "<%= botDescription %>", - "author": "Generated using Microsoft Bot Builder Yeoman generator v<%= version %>", + "author": "Generated using Microsoft Bot Builder Yeoman generator v5", "license": "MIT", "main": "<%= npmMain %>", "scripts": { @@ -24,6 +24,7 @@ "restify": "~11.1.0" }, "devDependencies": { + "@types/dotenv": "6.1.1", "@types/node": "^18.19.47", "@types/restify": "8.4.2", "nodemon": "^2.0.4", diff --git a/generators/generator-botbuilder/generators/app/templates/empty/bot.js b/generators/generator-botbuilder/generators/app/templates/empty/bot.js index 13eccbf38d..3cd1da631e 100644 --- a/generators/generator-botbuilder/generators/app/templates/empty/bot.js +++ b/generators/generator-botbuilder/generators/app/templates/empty/bot.js @@ -4,7 +4,7 @@ class EmptyBot extends ActivityHandler { constructor() { super(); this.onMembersAdded(async (context, next) => { - const membersAdded = context.activity.membersAdded; + const membersAdded = context.activity.membersAdded ?? []; for (let cnt = 0; cnt < membersAdded.length; ++cnt) { if (membersAdded[cnt].id !== context.activity.recipient.id) { await context.sendActivity('Hello world!'); diff --git a/generators/generator-botbuilder/generators/app/templates/empty/index.js b/generators/generator-botbuilder/generators/app/templates/empty/index.js index 7d4c1bf143..95be34e3aa 100644 --- a/generators/generator-botbuilder/generators/app/templates/empty/index.js +++ b/generators/generator-botbuilder/generators/app/templates/empty/index.js @@ -1,11 +1,17 @@ +const path = require('path'); +const dotenv = require('dotenv'); + +// Import required bot configuration. +const ENV_FILE = path.join(__dirname, '.env'); +dotenv.config({ path: ENV_FILE }); + const restify = require('restify'); // Import required bot services. // See https://aka.ms/bot-services to learn more about the different parts of a bot. const { CloudAdapter, - ConfigurationServiceClientCredentialFactory, - createBotFrameworkAuthenticationFromConfiguration + ConfigurationBotFrameworkAuthentication } = require('botbuilder'); // This bot's main dialog. @@ -17,26 +23,22 @@ server.use(restify.plugins.bodyParser()); server.listen(process.env.port || process.env.PORT || 3978, () => { console.log(`\n${ server.name } listening to ${ server.url }`); + console.log('\nGet Bot Framework Emulator: https://aka.ms/botframework-emulator'); + console.log('\nTo talk to your bot, open the emulator select "Open Bot"'); }); -const credentialsFactory = new ConfigurationServiceClientCredentialFactory({ - MicrosoftAppId: process.env.MicrosoftAppId, - MicrosoftAppPassword: process.env.MicrosoftAppPassword, - MicrosoftAppType: process.env.MicrosoftAppType, - MicrosoftAppTenantId: process.env.MicrosoftAppTenantId -}); - -const botFrameworkAuthentication = createBotFrameworkAuthenticationFromConfiguration(null, credentialsFactory); +const botFrameworkAuthentication = new ConfigurationBotFrameworkAuthentication(process.env); // Create adapter. // See https://aka.ms/about-bot-adapter to learn more about adapters. const adapter = new CloudAdapter(botFrameworkAuthentication); // Catch-all for errors. -adapter.onTurnError = async (context, error) => { +const onTurnErrorHandler = async (context, error) => { // This check writes out errors to console log .vs. app insights. // NOTE: In production environment, you should consider logging this to Azure - // application insights. + // application insights. See https://aka.ms/bottelemetry for telemetry + // configuration instructions. console.error(`\n [onTurnError] unhandled error: ${ error }`); // Send a trace activity, which will be displayed in Bot Framework Emulator @@ -52,6 +54,9 @@ adapter.onTurnError = async (context, error) => { await context.sendActivity('To continue to run this bot, please fix the bot source code.'); }; +// Set the onTurnError for the singleton CloudAdapter. +adapter.onTurnError = onTurnErrorHandler; + // Create the main dialog. const myBot = new EmptyBot(); diff --git a/generators/generator-botbuilder/generators/app/templates/empty/index.ts b/generators/generator-botbuilder/generators/app/templates/empty/index.ts index ec8ab1e295..b72f18bdee 100644 --- a/generators/generator-botbuilder/generators/app/templates/empty/index.ts +++ b/generators/generator-botbuilder/generators/app/templates/empty/index.ts @@ -1,39 +1,42 @@ +import * as path from 'path'; import * as restify from 'restify'; // Import required bot services. // See https://aka.ms/bot-services to learn more about the different parts of a bot. import { CloudAdapter, - ConfigurationServiceClientCredentialFactory, - createBotFrameworkAuthenticationFromConfiguration + ConfigurationBotFrameworkAuthentication, + ConfigurationBotFrameworkAuthenticationOptions } from 'botbuilder'; // This bot's main dialog. import { EmptyBot } from './bot'; +import { config } from 'dotenv'; + +const ENV_FILE = path.join(__dirname, '..', '.env'); +config({ path: ENV_FILE }); + // Create HTTP server. const server = restify.createServer(); server.use(restify.plugins.bodyParser()); server.listen(process.env.port || process.env.PORT || 3978, () => { console.log(`\n${ server.name } listening to ${ server.url }`); + console.log('\nGet Bot Framework Emulator: https://aka.ms/botframework-emulator'); + console.log('\nTo talk to your bot, open the emulator select "Open Bot"'); }); -const credentialsFactory = new ConfigurationServiceClientCredentialFactory({ - MicrosoftAppId: process.env.MicrosoftAppId, - MicrosoftAppPassword: process.env.MicrosoftAppPassword, - MicrosoftAppType: process.env.MicrosoftAppType, - MicrosoftAppTenantId: process.env.MicrosoftAppTenantId -}); - -const botFrameworkAuthentication = createBotFrameworkAuthenticationFromConfiguration(null, credentialsFactory); +const botFrameworkAuthentication = new ConfigurationBotFrameworkAuthentication( + process.env as ConfigurationBotFrameworkAuthenticationOptions +); // Create adapter. // See https://aka.ms/about-bot-adapter to learn more about adapters. const adapter = new CloudAdapter(botFrameworkAuthentication); // Catch-all for errors. -adapter.onTurnError = async (context, error) => { +const onTurnErrorHandler = async (context, error) => { // This check writes out errors to console log .vs. app insights. // NOTE: In production environment, you should consider logging this to Azure // application insights. @@ -52,6 +55,9 @@ adapter.onTurnError = async (context, error) => { await context.sendActivity('To continue to run this bot, please fix the bot source code.'); }; +// Set the onTurnError for the singleton CloudAdapter. +adapter.onTurnError = onTurnErrorHandler; + // Create the main dialog. const myBot = new EmptyBot(); diff --git a/generators/generator-botbuilder/generators/app/templates/empty/package.json.js b/generators/generator-botbuilder/generators/app/templates/empty/package.json.js index 1789e68fcd..d5b8d7f888 100644 --- a/generators/generator-botbuilder/generators/app/templates/empty/package.json.js +++ b/generators/generator-botbuilder/generators/app/templates/empty/package.json.js @@ -2,7 +2,7 @@ "name": "<%= botname %>", "version": "4.1.6", "description": "<%= botDescription %>", - "author": "Generated using Microsoft Bot Builder Yeoman generator v<%= version %>", + "author": "Generated using Microsoft Bot Builder Yeoman generator v5", "license": "MIT", "main": "<%= npmMain %>", "scripts": { @@ -17,6 +17,7 @@ }, "dependencies": { "botbuilder": "4.1.6", + "dotenv": "~8.2.0", "restify": "~11.1.0" }, "devDependencies": { diff --git a/generators/generator-botbuilder/generators/app/templates/empty/package.json.ts b/generators/generator-botbuilder/generators/app/templates/empty/package.json.ts index 37fd94e37c..54f4a390d9 100644 --- a/generators/generator-botbuilder/generators/app/templates/empty/package.json.ts +++ b/generators/generator-botbuilder/generators/app/templates/empty/package.json.ts @@ -2,7 +2,7 @@ "name": "<%= botname %>", "version": "4.1.6", "description": "<%= botDescription %>", - "author": "Generated using Microsoft Bot Builder Yeoman generator v<%= version %>", + "author": "Generated using Microsoft Bot Builder Yeoman generator v5", "license": "MIT", "main": "<%= npmMain %>", "scripts": { @@ -20,9 +20,11 @@ "dependencies": { "botbuilder": "4.1.6", "replace": "~1.2.0", + "dotenv": "~8.2.0", "restify": "~11.1.0" }, "devDependencies": { + "@types/dotenv": "6.1.1", "@types/node": "^18.19.47", "@types/restify": "8.4.2", "nodemon": "^2.0.4", diff --git a/generators/generator-botbuilder/testGen.cmd b/generators/generator-botbuilder/testGen.cmd index af744cf507..38afc03acb 100644 --- a/generators/generator-botbuilder/testGen.cmd +++ b/generators/generator-botbuilder/testGen.cmd @@ -58,53 +58,6 @@ npm run lint cd .. -# -# Core bot in TypeScript -# -echo Generating my-core-bot-ts -yo botbuilder -N "my-core-bot-ts" -D "A core bot in ts" -L "TypeScript" -T "core" --noprompt -cd ./my-core-bot-ts -echo building and linting my-core-bot-ts -npm run build -npm run lint -cd .. - - -# -# Core bot with tests in TypeScript -# -echo Generating my-core-bot-with-tests-ts -yo botbuilder -N "my-core-bot-with-tests-ts" -D "A core bot with tests in ts" -L "TypeScript" -T "core" --addtests --noprompt -cd ./my-core-bot-with-tests-ts -echo building and linting my-core-bot-with-tests-ts -npm run build -npm run lint -npm test -cd .. - - -# -# Core bot in JavaScript -# -echo Generating my-core-bot-js -yo botbuilder -N "my-core-bot-js" -D "A core bot in js" -L "JavaScript" -T "core" --noprompt -cd ./my-core-bot-js -echo linting my-core-bot-js -npm run lint -cd .. - - -# -# Core bot with tests in JavaScript -# -echo Generating my-core-bot-with-tests-js -yo botbuilder -N "my-core-bot-with-tests-js" -D "A core bot with tests in js" -L "JavaScript" -T "core" --addtests --noprompt -cd ./my-core-bot-with-tests-js -echo linting my-core-bot-with-tests-js -npm run lint -npm test -cd .. - if [ "$1" = nocleanup ] || [ "$1" = noclean ] then echo "*****************************************************************************" @@ -135,28 +88,4 @@ else # Echo bot in JavaScript # rm -rf ./my-echo-bot-js - - - # - # Core bot in TypeScript - # - rm -rf ./my-core-bot-ts - - - # - # Core bot with tests in TypeScript - # - rm -rf ./my-core-bot-with-tests-ts - - - # - # Core bot in JavaScript - # - rm -rf ./my-core-bot-js - - - # - # Core bot with tests in JavaScript - # - rm -rf ./my-core-bot-with-tests-js fi diff --git a/libraries/botbuilder-repo-utils/src/workspace.ts b/libraries/botbuilder-repo-utils/src/workspace.ts index b2b43641a4..663df282e5 100644 --- a/libraries/botbuilder-repo-utils/src/workspace.ts +++ b/libraries/botbuilder-repo-utils/src/workspace.ts @@ -42,9 +42,7 @@ export async function collectWorkspacePackages( ): Promise> { // Note: posix is required, this emits absolute paths that are platform specific const paths = await glob( - workspaces.map((workspace) => - path.posix.join(repoRoot, workspace, '{package.json,package-with-tests.json}{,.js,.ts}'), - ), + workspaces.map((workspace) => path.posix.join(repoRoot, workspace, 'package.json{,.js,.ts}')), ); const maybeWorkspaces = await Promise.all( diff --git a/libraries/botbuilder-repo-utils/tests/updateVersions.test.ts b/libraries/botbuilder-repo-utils/tests/updateVersions.test.ts index 70c5927067..76a7f2b84f 100644 --- a/libraries/botbuilder-repo-utils/tests/updateVersions.test.ts +++ b/libraries/botbuilder-repo-utils/tests/updateVersions.test.ts @@ -190,8 +190,8 @@ describe('updateVersions', function () { return { ...workspace, relPath, - absPath: path.join(root, ...relPath, '{package.json,package-with-tests.json}{,.js,.ts}'), - posixPath: path.posix.join(root, ...relPath, '{package.json,package-with-tests.json}{,.js,.ts}'), + absPath: path.join(root, ...relPath, 'package.json{,.js,.ts}'), + posixPath: path.posix.join(root, ...relPath, 'package.json{,.js,.ts}'), }; });