diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 000000000..ad136dfa8 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,15 @@ +# Lines starting with '#' are comments. +# Each line is a file pattern followed by one or more owners. + +# More details are here: https://help.github.com/articles/about-codeowners/ + +# The '*' pattern is global owners. + +# Order is important. The last matching pattern has the most precedence. +# The folders are ordered as follows: + +# In each subsection folders are ordered first by depth, then alphabetically. +# This should make it easy to add new rules without breaking existing ones. + +# Global rule: +* @microsoft/botframework-sdk \ No newline at end of file diff --git a/.gitignore b/.gitignore index 660bbe29e..d80f28b79 100644 --- a/.gitignore +++ b/.gitignore @@ -66,3 +66,6 @@ libraries/swagger/generated /.vs/slnx.sqlite /.vs/botbuilder-java/v16/.suo /.vs/ProjectSettings.json +/.vs/botbuilder-java/v16/TestStore/0/000.testlog +/.vs/botbuilder-java/v16/TestStore/0/testlog.manifest +/.vs/VSWorkspaceState.json diff --git a/README.md b/README.md index 084ece132..7c90ffb18 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,16 @@ # ![Bot Framework for Java](./docs/media/BotFrameworkJava_header.png) +**The Bot Framework Java SDK is being retired with final long-term support ending in November 2023, after which this repository will be archived. There will be no further feature development, with only critical security and bug fixes within this repository being undertaken. Existing bots built with this SDK will continue to function. For all new bot development we recommend that you adopt [Power Virtual Agents](https://powervirtualagents.microsoft.com/en-us/blog/the-future-of-bot-building/) or use the [Bot Framework C#](https://github.com/microsoft/botbuilder-dotnet) or [Bot Framework JavaScript](https://github.com/microsoft/botbuilder-js) SDKs.** + This repository contains code for the Java version of the [Microsoft Bot Framework SDK](https://github.com/Microsoft/botframework-sdk), which is part of the Microsoft Bot Framework - a comprehensive framework for building enterprise-grade conversational AI experiences. -This SDK enables developers to model conversation and build sophisticated bot applications using JavaScript. SDKs for [.NET](https://github.com/Microsoft/botbuilder-dotnet), [Python](https://github.com/Microsoft/botbuilder-python) and [JavaScript](https://github.com/Microsoft/botbuilder-js) are also available. +This SDK enables developers to model conversation and build sophisticated bot applications using Java. SDKs for [.NET](https://github.com/Microsoft/botbuilder-dotnet), [Python](https://github.com/Microsoft/botbuilder-python) and [JavaScript](https://github.com/Microsoft/botbuilder-js) are also available. To get started building bots using the SDK, see the [Azure Bot Service Documentation](https://docs.microsoft.com/en-us/azure/bot-service/?view=azure-bot-service-4.0). If you are an existing user, then you can also [find out what's new with Bot Framework](https://docs.microsoft.com/en-us/azure/bot-service/what-is-new?view=azure-bot-service-4.0). For more information jump to a section below. -- [!Bot Framework for Java](#) +- [Bot Framework for Java](#) - [Build Status](#build-status) - [Getting Started](#getting-started) - [Prerequisites](#prerequisites) @@ -28,7 +30,7 @@ For more information jump to a section below. | Branch | Description | Build Status | Coverage Status | |--------|-------------|--------------|-----------------| - |Main | 4.14.* Preview Builds | [![Build Status](https://fuselabs.visualstudio.com/SDK_v4/_apis/build/status/Java/BotBuilder-Java-4.0-daily?branchName=main)](https://fuselabs.visualstudio.com/SDK_v4/_build/latest?definitionId=1202&branchName=main) | [![Coverage Status](https://coveralls.io/repos/github/microsoft/botbuilder-java/badge.svg?branch=823847c676b7dbb0fa348a308297ae375f5141ef)](https://coveralls.io/github/microsoft/botbuilder-java?branch=823847c676b7dbb0fa348a308297ae375f5141ef) | + |Main | 4.15.* Builds | [![Build Status](https://fuselabs.visualstudio.com/SDK_v4/_apis/build/status/Java/BotBuilder-Java-4.0-daily?branchName=main)](https://fuselabs.visualstudio.com/SDK_v4/_build/latest?definitionId=1202&branchName=main) | [![Coverage Status](https://coveralls.io/repos/github/microsoft/botbuilder-java/badge.svg?branch=823847c676b7dbb0fa348a308297ae375f5141ef)](https://coveralls.io/github/microsoft/botbuilder-java?branch=823847c676b7dbb0fa348a308297ae375f5141ef) | ## Getting Started To get started building bots using the SDK, see the [Azure Bot Service Documentation](https://docs.microsoft.com/en-us/azure/bot-service/?view=azure-bot-service-4.0). @@ -106,7 +108,7 @@ We use the [@botframework](https://twitter.com/botframework) account on twitter The [Gitter Channel](https://gitter.im/Microsoft/BotBuilder) provides a place where the Community can get together and collaborate. ## Contributing and our code of conduct -We welcome contributions and suggestions. Please see our [contributing guidelines](./contributing.md) for more information. +We welcome contributions and suggestions. Please see our [contributing guidelines](./Contributing.md) for more information. This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..e138ec5d6 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,41 @@ + + +## Security + +Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). + +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). + +If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + + * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). + + diff --git a/generators/.gitignore b/generators/.gitignore new file mode 100644 index 000000000..ba2a97b57 --- /dev/null +++ b/generators/.gitignore @@ -0,0 +1,2 @@ +node_modules +coverage diff --git a/generators/LICENSE.md b/generators/LICENSE.md new file mode 100644 index 000000000..506ab97e5 --- /dev/null +++ b/generators/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Microsoft Corporation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/generators/README.md b/generators/README.md new file mode 100644 index 000000000..c86d8685f --- /dev/null +++ b/generators/README.md @@ -0,0 +1,126 @@ +# generator-botbuilder-java + +Yeoman generator for [Bot Framework v4](https://dev.botframework.com). Will let you quickly set up a conversational AI bot +using core AI capabilities. + +## About + +`generator-botbuilder-java` will help you build new conversational AI bots using the [Bot Framework v4](https://dev.botframework.com). + +## Templates + +The generator supports three different template options. The table below can help guide which template is right for you. + +| 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. | +| 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. | +| Core Bot | A good template if you want to create advanced bots, as it uses multi-turn dialogs and [LUIS](https://www.luis.ai), an AI based cognitive service, to implement language understanding. This template creates a bot that can extract places and dates to book a flight. | + +### How to Choose a Template + +| 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. | +| Empty Bot | You are a seasoned Bot Framework v4 developer. You've built bots before, and want the minimum skeleton of a bot. | +| Core Bot | You are a medium to advanced user of Bot Framework v4 and want to start integrating language understanding as well as multi-turn dialogs in your bots. | + +### Template Overview + +#### Echo Bot Template + +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 receives 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. + +#### 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. + +#### Core Bot Template + +The Core Bot template uses [LUIS](https://www.luis.ai) to implement core AI capabilities, a multi-turn conversation using Dialogs, handles user interruptions, and prompts for and validate requests for information from the user. This template implements a basic three-step waterfall dialog, where the first step asks the user for an input to book a flight, then asks the user if the information is correct, and finally confirms the booking with the user. Choose this template if want to create an advanced bot that can extract information from the user's input. + +## Installation + +1. Install [Yeoman](http://yeoman.io) using [npm](https://www.npmjs.com) (we assume you have pre-installed [node.js](https://nodejs.org/)). + + ```bash + # Make sure both are installed globally + npm install -g yo + ``` + +2. Install generator-botbuilder-java by typing the following in your console: + + ```bash + # Make sure both are installed globally + npm install -g generator-botbuilder-java + ``` + +3. Verify that Yeoman and generator-botbuilder-java have been installed correctly by typing the following into your console: + + ```bash + yo botbuilder-java --help + ``` + +## Usage + +### Creating a New Bot Project + +When the generator is launched, it will prompt for the information required to create a new bot. + +```bash +# Run the generator in interactive mode +yo botbuilder-java +``` + +### Generator Command Line Options + +The generator supports a number of command line options that can be used to change the generator's default options or to pre-seed a prompt. + +| Command line Option | Description | +| ------------------- | ----------- | +| --help, -h | List help text for all supported command-line options | +| --botName, -N | The name given to the bot project | +| --packageName, -P | The Java package name to use for the bot | +| --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. + +```bash +# Run the generator defaulting the pacakge name and the template +yo botbuilder-java --P "com.mycompany.bot" --T "echo" +``` + +### Generating a Bot Using --noprompt + +The generator can be run in `--noprompt` mode, which can be used for automated bot creation. When run in `--noprompt` mode, the generator can be configured using command line options as documented above. If a command line option is ommitted a reasonable default will be used. In addition, passing the `--noprompt` option will cause the generator to create a new bot project without prompting for confirmation before generating the bot. + +#### Default Options + +| Command line Option | Default Value | +| ------------------- | ----------- | +| --botname, -N | `echo` | +| --packageName, -p | `echo` | +| --template, -T | `echo` | + +#### Examples Using --noprompt + +This example shows how to run the generator in --noprompt mode, setting all required options on the command line. + +```bash +# Run the generator, setting all command line options +yo botbuilder-java --noprompt -N "MyEchoBot" -P "com.mycompany.bot.echo" -T "echo" +``` + +This example shows how to run the generator in --noprompt mode, using all the default command line options. The generator will create a bot project using all the default values specified in the **Default Options** table above. + +```bash +# Run the generator using all default options +yo botbuilder-java --noprompt +``` + +## Logging Issues and Providing Feedback + +Issues and feedback about the botbuilder generator can be submitted through the project's [GitHub Issues](https://github.com/Microsoft/botbuilder-java/issues) page. diff --git a/generators/generators/app/index.js b/generators/generators/app/index.js new file mode 100644 index 000000000..13df547aa --- /dev/null +++ b/generators/generators/app/index.js @@ -0,0 +1,276 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; +const pkg = require('../../package.json'); +const Generator = require('yeoman-generator'); +const path = require('path'); +const chalk = require('chalk'); +const mkdirp = require('mkdirp'); +const _ = require('lodash'); + +const BOT_TEMPLATE_NAME_EMPTY = 'Empty Bot'; +const BOT_TEMPLATE_NAME_SIMPLE = 'Echo Bot'; +const BOT_TEMPLATE_NAME_CORE = 'Core Bot'; + +const BOT_TEMPLATE_NOPROMPT_EMPTY = 'empty'; +const BOT_TEMPLATE_NOPROMPT_SIMPLE = 'echo'; +const BOT_TEMPLATE_NOPROMPT_CORE = 'core'; + +const bigBot = + ` ╭─────────────────────────────╮\n` + + ` ` + + chalk.blue.bold(`//`) + + ` ` + + chalk.blue.bold(`\\\\`) + + ` │ Welcome to the │\n` + + ` ` + + chalk.blue.bold(`//`) + + ` () () ` + + chalk.blue.bold(`\\\\`) + + ` │ Microsoft Java Bot Builder │\n` + + ` ` + + chalk.blue.bold(`\\\\`) + + ` ` + + chalk.blue.bold(`//`) + + ` /│ generator! │\n` + + ` ` + + chalk.blue.bold(`\\\\`) + + ` ` + + chalk.blue.bold(`//`) + + ` ╰─────────────────────────────╯\n` + + ` v${pkg.version}`; + +const tinyBot = + ` ` + chalk.blue.bold(`<`) + ` ** ` + chalk.blue.bold(`>`) + ` `; + +module.exports = class extends Generator { + constructor(args, opts) { + super(args, opts); + + // allocate an object that we can use to store our user prompt values from our askFor* functions + this.templateConfig = {}; + + // configure the commandline options + this._configureCommandlineOptions(); + } + + initializing() { + // give the user some data before we start asking them questions + this.log(bigBot); + } + + prompting() { + // if we're told to not prompt, then pick what we need and return + if (this.options.noprompt) { + // this function will throw if it encounters errors/invalid options + return this._verifyNoPromptOptions(); + } + + const userPrompts = this._getPrompts(); + async function executePrompts([prompt, ...rest]) { + if (prompt) { + await prompt(); + return executePrompts(rest); + } + } + + return executePrompts(userPrompts); + } + + writing() { + // if the user confirmed their settings, then lets go ahead + // an install module dependencies + if (this.templateConfig.finalConfirmation === true) { + const botName = this.templateConfig.botName; + const packageName = this.templateConfig.packageName.replace(/-/g, '_').toLowerCase(); + const packageTree = packageName.replace(/\./g, '/'); + const artifact = _.kebabCase(this.templateConfig.botName).replace(/([^a-z0-9-]+)/gi, ``); + const directoryName = _.camelCase(this.templateConfig.botName); + const template = this.templateConfig.template.toLowerCase(); + + if (path.basename(this.destinationPath()) !== directoryName) { + mkdirp.sync(directoryName); + this.destinationRoot(this.destinationPath(directoryName)); + } + + // Copy the project tree + this.fs.copyTpl( + this.templatePath(path.join(template, 'project', '**')), + this.destinationPath(), + { + botName, + packageName, + artifact + } + ); + + // Copy main source + this.fs.copyTpl( + this.templatePath(path.join(template, 'src/main/java/**')), + this.destinationPath(path.join('src/main/java', packageTree)), + { + packageName + } + ); + + // Copy test source + this.fs.copyTpl( + this.templatePath(path.join(template, 'src/test/java/**')), + this.destinationPath(path.join('src/test/java', packageTree)), + { + packageName + } + ); + } + } + + end() { + if (this.templateConfig.finalConfirmation === true) { + this.log(chalk.green('------------------------ ')); + this.log(chalk.green(' Your new bot is ready! ')); + this.log(chalk.green('------------------------ ')); + this.log(`Your bot should be in a directory named "${_.camelCase(this.templateConfig.botName)}"`); + this.log('Open the ' + chalk.green.bold('README.md') + ' to learn how to run your bot. '); + this.log('Thank you for using the Microsoft Bot Framework. '); + this.log(`\n${tinyBot} The Bot Framework Team`); + } else { + this.log(chalk.red.bold('-------------------------------- ')); + this.log(chalk.red.bold(' New bot creation was canceled. ')); + this.log(chalk.red.bold('-------------------------------- ')); + this.log('Thank you for using the Microsoft Bot Framework. '); + this.log(`\n${tinyBot} The Bot Framework Team`); + } + } + + _configureCommandlineOptions() { + this.option('botName', { + desc: 'The name you want to give to your bot', + type: String, + default: 'echo', + alias: 'N' + }); + + this.option('packageName', { + desc: `What's the fully qualified package name of your bot?`, + type: String, + default: 'com.mycompany.echo', + alias: 'P' + }); + + const templateDesc = `The initial bot capabilities. (${BOT_TEMPLATE_NAME_EMPTY} | ${BOT_TEMPLATE_NAME_SIMPLE} | ${BOT_TEMPLATE_NAME_CORE})`; + this.option('template', { + desc: templateDesc, + type: String, + default: BOT_TEMPLATE_NAME_SIMPLE, + alias: 'T' + }); + + this.argument('noprompt', { + desc: 'Do not prompt for any information or confirmation', + type: Boolean, + required: false, + default: false + }); + } + + _getPrompts() { + return [ + // ask the user to name their bot + async () => { + return this.prompt({ + type: 'input', + name: 'botName', + message: `What's the name of your bot?`, + default: (this.options.botName ? this.options.botName : 'echo') + }).then(answer => { + // store the botname description answer + this.templateConfig.botName = answer.botName; + }); + }, + + // ask for package name + async () => { + return this.prompt({ + type: 'input', + name: 'packageName', + message: `What's the fully qualified package name of your bot?`, + default: (this.options.packageName ? this.options.packageName : 'com.mycompany.echo') + }).then(answer => { + // store the package name description answer + this.templateConfig.packageName = answer.packageName; + }); + }, + + + // ask the user which bot template we should use + async () => { + return this.prompt({ + type: 'list', + name: 'template', + message: 'Which template would you like to start with?', + choices: [ + { + name: BOT_TEMPLATE_NAME_SIMPLE, + value: BOT_TEMPLATE_NOPROMPT_SIMPLE + }, + { + name: BOT_TEMPLATE_NAME_EMPTY, + value: BOT_TEMPLATE_NOPROMPT_EMPTY + }, + { + name: BOT_TEMPLATE_NAME_CORE, + value: BOT_TEMPLATE_NOPROMPT_CORE + } + ], + default: (this.options.template ? _.toLower(this.options.template) : BOT_TEMPLATE_NOPROMPT_SIMPLE) + }).then(answer => { + // store the template prompt answer + this.templateConfig.template = answer.template; + }); + }, + + // ask the user for final confirmation before we generate their bot + async () => { + return this.prompt({ + type: 'confirm', + name: 'finalConfirmation', + message: 'Looking good. Shall I go ahead and create your new bot?', + default: true + }).then(answer => { + // store the finalConfirmation prompt answer + this.templateConfig.finalConfirmation = answer.finalConfirmation; + }); + } + ]; + } + + // if we're run with the --noprompt option, verify that all required options were supplied. + // throw for missing options, or a resolved Promise + _verifyNoPromptOptions() { + this.templateConfig = _.pick(this.options, ['botName', 'packageName', 'template']) + + // validate we have what we need, or we'll need to throw + if (!this.templateConfig.botName) { + throw new Error('Must specify a name for your bot when using --noprompt argument. Use --botName or -N'); + } + if (!this.templateConfig.packageName) { + throw new Error('Must specify a package name for your bot when using --noprompt argument. Use --packageName or -P'); + } + + // make sure we have a supported template + 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)) { + throw new Error('Must specify a template when using --noprompt argument. Use --template or -T'); + } + + // 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; + + return Promise.resolve(); + } +}; diff --git a/generators/generators/app/templates/core/project/README-LUIS.md b/generators/generators/app/templates/core/project/README-LUIS.md new file mode 100644 index 000000000..12bc78ed0 --- /dev/null +++ b/generators/generators/app/templates/core/project/README-LUIS.md @@ -0,0 +1,216 @@ +# Setting up LUIS via CLI: + +This README contains information on how to create and deploy a LUIS application. When the bot is ready to be deployed to production, we recommend creating a LUIS Endpoint Resource for usage with your LUIS App. + +> _For instructions on how to create a LUIS Application via the LUIS portal, see these Quickstart steps:_ +> 1. _[Quickstart: Create a new app in the LUIS portal][Quickstart-create]_ +> 2. _[Quickstart: Deploy an app in the LUIS portal][Quickstart-deploy]_ + + [Quickstart-create]: https://docs.microsoft.com/azure/cognitive-services/luis/get-started-portal-build-app + [Quickstart-deploy]:https://docs.microsoft.com/azure/cognitive-services/luis/get-started-portal-deploy-app + +## Table of Contents: + +- [Prerequisites](#Prerequisites) +- [Import a new LUIS Application using a local LUIS application](#Import-a-new-LUIS-Application-using-a-local-LUIS-application) +- [How to create a LUIS Endpoint resource in Azure and pair it with a LUIS Application](#How-to-create-a-LUIS-Endpoint-resource-in-Azure-and-pair-it-with-a-LUIS-Application) + +___ + +## [Prerequisites](#Table-of-Contents): + +#### Install Azure CLI >=2.0.61: + +Visit the following page to find the correct installer for your OS: +- https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest + +#### Install LUIS CLI >=2.4.0: + +Open a CLI of your choice and type the following: + +```bash +npm i -g luis-apis@^2.4.0 +``` + +#### LUIS portal account: + +You should already have a LUIS account with either https://luis.ai, https://eu.luis.ai, or https://au.luis.ai. To determine where to create a LUIS account, consider where you will deploy your LUIS applications, and then place them in [the corresponding region][LUIS-Authoring-Regions]. + +After you've created your account, you need your [Authoring Key][LUIS-AKey] and a LUIS application ID. + + [LUIS-Authoring-Regions]: https://docs.microsoft.com/azure/cognitive-services/luis/luis-reference-regions#luis-authoring-regions] + [LUIS-AKey]: https://docs.microsoft.com/azure/cognitive-services/luis/luis-concept-keys#authoring-key + +___ + +## [Import a new LUIS Application using a local LUIS application](#Table-of-Contents) + +### 1. Import the local LUIS application to luis.ai + +```bash +luis import application --region "LuisAppAuthoringRegion" --authoringKey "LuisAuthoringKey" --appName "FlightBooking" --in "./cognitiveModels/FlightBooking.json" +``` + +Outputs the following JSON: + +```json +{ + "id": "########-####-####-####-############", + "name": "FlightBooking", + "description": "A LUIS model that uses intent and entities.", + "culture": "en-us", + "usageScenario": "", + "domain": "", + "versionsCount": 1, + "createdDateTime": "2019-03-29T18:32:02Z", + "endpoints": {}, + "endpointHitsCount": 0, + "activeVersion": "0.1", + "ownerEmail": "bot@contoso.com", + "tokenizerVersion": "1.0.0" +} +``` + +For the next step, you'll need the `"id"` value for `--appId` and the `"activeVersion"` value for `--versionId`. + +### 2. Train the LUIS Application + +```bash +luis train version --region "LuisAppAuthoringRegion" --authoringKey "LuisAuthoringKey" --appId "LuisAppId" --versionId "LuisAppversion" --wait +``` + +### 3. Publish the LUIS Application + +```bash +luis publish version --region "LuisAppAuthoringRegion" --authoringKey "LuisAuthoringKey" --appId "LuisAppId" --versionId "LuisAppversion" --publishRegion "LuisAppPublishRegion" +``` + +> `--region` corresponds to the region you _author_ your application in. The regions available for this are "westus", "westeurope" and "australiaeast".
+> These regions correspond to the three available portals, https://luis.ai, https://eu.luis.ai, or https://au.luis.ai.
+> `--publishRegion` corresponds to the region of the endpoint you're publishing to, (e.g. "westus", "southeastasia", "westeurope", "brazilsouth").
+> See the [reference docs][Endpoint-API] for a list of available publish/endpoint regions. + + [Endpoint-API]: https://westus.dev.cognitive.microsoft.com/docs/services/5819c76f40a6350ce09de1ac/operations/5819c77140a63516d81aee78 + +Outputs the following: + +```json + { + "versionId": "0.1", + "isStaging": false, + "endpointUrl": "https://westus.api.cognitive.microsoft.com/luis/v2.0/apps/########-####-####-####-############", + "region": "westus", + "assignedEndpointKey": null, + "endpointRegion": "westus", + "failedRegions": "", + "publishedDateTime": "2019-03-29T18:40:32Z", + "directVersionPublish": false +} +``` + +To see how to create an LUIS Cognitive Service Resource in Azure, please see [the next README][README-LUIS]. This Resource should be used when you want to move your bot to production. The instructions will show you how to create and pair the resource with a LUIS Application. + + [README-LUIS]: ./README-LUIS.md + +___ + +## [How to create a LUIS Endpoint resource in Azure and pair it with a LUIS Application](#Table-of-Contents) + +### 1. Create a new LUIS Cognitive Services resource on Azure via Azure CLI + +> _Note:_
+> _If you don't have a Resource Group in your Azure subscription, you can create one through the Azure portal or through using:_ +> ```bash +> az group create --subscription "AzureSubscriptionGuid" --location "westus" --name "ResourceGroupName" +> ``` +> _To see a list of valid locations, use `az account list-locations`_ + + +```bash +# Use Azure CLI to create the LUIS Key resource on Azure +az cognitiveservices account create --kind "luis" --name "NewLuisResourceName" --sku "S0" --location "westus" --subscription "AzureSubscriptionGuid" -g "ResourceGroupName" +``` + +The command will output a response similar to the JSON below: + +```json +{ + "endpoint": "https://westus.api.cognitive.microsoft.com/luis/v2.0", + "etag": "\"########-####-####-####-############\"", + "id": "/subscriptions/########-####-####-####-############/resourceGroups/ResourceGroupName/providers/Microsoft.CognitiveServices/accounts/NewLuisResourceName", + "internalId": "################################", + "kind": "luis", + "location": "westus", + "name": "NewLuisResourceName", + "provisioningState": "Succeeded", + "resourceGroup": "ResourceGroupName", + "sku": { + "name": "S0", + "tier": null + }, + "tags": null, + "type": "Microsoft.CognitiveServices/accounts" +} +``` + + + +Take the output from the previous command and create a JSON file in the following format: + +```json +{ + "azureSubscriptionId": "00000000-0000-0000-0000-000000000000", + "resourceGroup": "ResourceGroupName", + "accountName": "NewLuisResourceName" +} +``` + +### 2. Retrieve ARM access token via Azure CLI + +```bash +az account get-access-token --subscription "AzureSubscriptionGuid" +``` + +This will return an object that looks like this: + +```json +{ + "accessToken": "eyJ0eXAiOiJKVtokentokentokentokentokeng1dCI6Ik4tbEMwbi05REFMcXdodUhZbkhRNjNHZUNYYyIsItokenI6Ik4tbEMwbi05REFMcXdodUhZbkhRNjNHZUNYYyJ9.eyJhdWQiOiJodHRwczovL21hbmFnZW1lbnQuY29yZS53aW5kb3dzLm5ldC8iLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC83MmY5ODhiZi04NmYxLTQxYWYtOTFhYi0yZDdjZDAxMWRiNDcvIiwiaWF0IjoxNTUzODc3MTUwLCJuYmYiOjE1NTM4NzcxNTAsImV4cCI6MTU1Mzg4MTA1MCwiX2NsYWltX25hbWVzIjp7Imdyb3VwcyI6InNyYzEifSwiX2NsYWltX3NvdXJjZXMiOnsic3JjMSI6eyJlbmRwb2ludCI6Imh0dHBzOi8vZ3JhcGgud2luZG93cy5uZXQvNzJmOTg4YmYtODZmMS00MWFmLTkxYWItMmQ3Y2QwMTFkYjQ3L3VzZXJzL2ZmZTQyM2RkLWJhM2YtNDg0Ny04NjgyLWExNTI5MDA4MjM4Ny9nZXRNZW1iZXJPYmplY3RzIn19LCJhY3IiOiIxIiwiYWlvIjoiQVZRQXEvOEtBQUFBeGVUc201NDlhVHg4RE1mMFlRVnhGZmxxOE9RSC9PODR3QktuSmRqV1FqTkkwbmxLYzB0bHJEZzMyMFZ5bWZGaVVBSFBvNUFFUTNHL0FZNDRjdk01T3M0SEt0OVJkcE5JZW9WU0dzd0kvSkk9IiwiYW1yIjpbIndpYSIsIm1mYSJdLCJhcHBpZCI6IjA0YjA3Nzk1LThkZGItNDYxYS1iYmVlLTAyZjllMWJmN2I0NiIsImFwcGlkYWNyIjoiMCIsImRldmljZWlkIjoiNDhmNDVjNjEtMTg3Zi00MjUxLTlmZWItMTllZGFkZmMwMmE3IiwiZmFtaWx5X25hbWUiOiJHdW0iLCJnaXZlbl9uYW1lIjoiU3RldmVuIiwiaXBhZGRyIjoiMTY3LjIyMC4yLjU1IiwibmFtZSI6IlN0ZXZlbiBHdW0iLCJvaWQiOiJmZmU0MjNkZC1iYTNmLTQ4NDctODY4Mi1hMTUyOTAwODIzODciLCJvbnByZW1fc2lkIjoiUy0xLTUtMjEtMjEyNzUyMTE4NC0xNjA0MDEyOTIwLTE4ODc5Mjc1MjctMjYwOTgyODUiLCJwdWlkIjoiMTAwMzdGRkVBMDQ4NjlBNyIsInJoIjoiSSIsInNjcCI6InVzZXJfaW1wZXJzb25hdGlvbiIsInN1YiI6Ik1rMGRNMWszN0U5ckJyMjhieUhZYjZLSU85LXVFQVVkZFVhNWpkSUd1Nk0iLCJ0aWQiOiI3MmY5ODhiZi04NmYxLTQxYWYtOTFhYi0yZDdjZDAxMWRiNDciLCJ1bmlxdWVfbmFtZSI6InN0Z3VtQG1pY3Jvc29mdC5jb20iLCJ1cG4iOiJzdGd1bUBtaWNyb3NvZnQuY29tIiwidXRpIjoiT2w2NGN0TXY4RVNEQzZZQWRqRUFtokenInZlciI6IjEuMCJ9.kFAsEilE0mlS1pcpqxf4rEnRKeYsehyk-gz-zJHUrE__oad3QjgDSBDPrR_ikLdweynxbj86pgG4QFaHURNCeE6SzrbaIrNKw-n9jrEtokenlosOxg_0l2g1LeEUOi5Q4gQREAU_zvSbl-RY6sAadpOgNHtGvz3Rc6FZRITfkckSLmsKAOFoh-aWC6tFKG8P52rtB0qVVRz9tovBeNqkMYL49s9ypduygbXNVwSQhm5JszeWDgrFuVFHBUP_iENCQYGQpEZf_KvjmX1Ur1F9Eh9nb4yI2gFlKncKNsQl-tokenK7-tokentokentokentokentokentokenatoken", + "expiresOn": "2200-12-31 23:59:59.999999", + "subscription": "AzureSubscriptionGuid", + "tenant": "tenant-guid", + "tokenType": "Bearer" +} +``` + +The value needed for the next step is the `"accessToken"`. + +### 3. Use `luis add appazureaccount` to pair your LUIS resource with a LUIS Application + +```bash +luis add appazureaccount --in "path/to/created/requestBody.json" --appId "LuisAppId" --authoringKey "LuisAuthoringKey" --armToken "accessToken" +``` + +If successful, it should yield a response like this: + +```json +{ + "code": "Success", + "message": "Operation Successful" +} +``` + +### 4. See the LUIS Cognitive Services' keys + +```bash +az cognitiveservices account keys list --name "NewLuisResourceName" --subscription "AzureSubscriptionGuid" -g "ResourceGroupName" +``` + +This will return an object that looks like this: + +```json +{ + "key1": "9a69####dc8f####8eb4####399f####", + "key2": "####f99e####4b1a####fb3b####6b9f" +} +``` diff --git a/generators/generators/app/templates/core/project/README.md b/generators/generators/app/templates/core/project/README.md new file mode 100644 index 000000000..5428395fc --- /dev/null +++ b/generators/generators/app/templates/core/project/README.md @@ -0,0 +1,67 @@ +# <%= botName %> + +Bot Framework v4 core bot sample. + +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. + +### 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=cs). + +Once you created the LUIS model, update `application.properties` with your `LuisAppId`, `LuisAPIKey` and `LuisAPIHostName`. + +``` + 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 try this sample + +- From the root of this project folder: + - Build the sample using `mvn package` + - Run it by using `java -jar .\target\<%= artifact %>-1.0.0.jar` + +## 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 latest Bot Framework Emulator 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&tabs=csharp) +- [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) +- [Spring Boot](https://spring.io/projects/spring-boot) diff --git a/generators/generators/app/templates/core/project/cognitiveModels/FlightBooking.json b/generators/generators/app/templates/core/project/cognitiveModels/FlightBooking.json new file mode 100644 index 000000000..603dd06b2 --- /dev/null +++ b/generators/generators/app/templates/core/project/cognitiveModels/FlightBooking.json @@ -0,0 +1,339 @@ +{ + "luis_schema_version": "3.2.0", + "versionId": "0.1", + "name": "FlightBooking", + "desc": "Luis Model for <%= botName %>", + "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/generators/app/templates/core/project/deploymentTemplates/template-with-new-rg.json b/generators/generators/app/templates/core/project/deploymentTemplates/template-with-new-rg.json new file mode 100644 index 000000000..196cfb933 --- /dev/null +++ b/generators/generators/app/templates/core/project/deploymentTemplates/template-with-new-rg.json @@ -0,0 +1,292 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "groupLocation": { + "defaultValue": "", + "type": "string", + "metadata": { + "description": "Specifies the location of the Resource Group." + } + }, + "groupName": { + "type": "string", + "metadata": { + "description": "Specifies the name of the Resource Group." + } + }, + "appId": { + "type": "string", + "metadata": { + "description": "Active Directory App 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." + } + }, + "botId": { + "type": "string", + "metadata": { + "description": "The globally unique and immutable bot ID. Also used to configure the displayName of the bot, which is mutable." + } + }, + "botSku": { + "defaultValue": "S1", + "type": "string", + "metadata": { + "description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1." + } + }, + "newAppServicePlanName": { + "defaultValue": "", + "type": "string", + "metadata": { + "description": "The name of the App Service Plan." + } + }, + "newAppServicePlanSku": { + "type": "object", + "defaultValue": { + "name": "P1v2", + "tier": "PremiumV2", + "size": "P1v2", + "family": "Pv2", + "capacity": 1 + }, + "metadata": { + "description": "The SKU of the App Service Plan. Defaults to Standard values." + } + }, + "newAppServicePlanLocation": { + "defaultValue": "", + "type": "string", + "metadata": { + "description": "The location of the App Service Plan. Defaults to \"westus\"." + } + }, + "newWebAppName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The globally unique name of the Web App. Defaults to the value passed in for \"botId\"." + } + } + }, + "variables": { + "appServicePlanName": "[parameters('newAppServicePlanName')]", + "resourcesLocation": "[parameters('newAppServicePlanLocation')]", + "webAppName": "[if(empty(parameters('newWebAppName')), parameters('botId'), parameters('newWebAppName'))]", + "siteHost": "[concat(variables('webAppName'), '.azurewebsites.net')]", + "botEndpoint": "[concat('https://', variables('siteHost'), '/api/messages')]", + "publishingUsername": "[concat('$', parameters('newWebAppName'))]", + "resourceGroupId": "[concat(subscription().id, '/resourceGroups/', parameters('groupName'))]" + }, + "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/2015-01-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": {}, + "variables": {}, + "resources": [ + { + "comments": "Create a new Linux App Service Plan if no existing App Service Plan name was passed in.", + "type": "Microsoft.Web/serverfarms", + "name": "[variables('appServicePlanName')]", + "apiVersion": "2018-02-01", + "location": "[variables('resourcesLocation')]", + "sku": "[parameters('newAppServicePlanSku')]", + "kind": "linux", + "properties": { + "perSiteScaling": false, + "maximumElasticWorkerCount": 1, + "isSpot": false, + "reserved": true, + "isXenon": false, + "hyperV": false, + "targetWorkerCount": 0, + "targetWorkerSizeId": 0 + } + }, + { + "comments": "Create a Web App using a Linux App Service Plan", + "type": "Microsoft.Web/sites", + "apiVersion": "2018-11-01", + "location": "[variables('resourcesLocation')]", + "kind": "app,linux", + "dependsOn": [ + "[concat(variables('resourceGroupId'), '/providers/Microsoft.Web/serverfarms/', variables('appServicePlanName'))]" + ], + "name": "[variables('webAppName')]", + "properties": { + "name": "[variables('webAppName')]", + "hostNameSslStates": [ + { + "name": "[concat(parameters('newWebAppName'), '.azurewebsites.net')]", + "sslState": "Disabled", + "hostType": "Standard" + }, + { + "name": "[concat(parameters('newWebAppName'), '.scm.azurewebsites.net')]", + "sslState": "Disabled", + "hostType": "Repository" + } + ], + "serverFarmId": "[variables('appServicePlanName')]", + "reserved": true, + "isXenon": false, + "hyperV": false, + "scmSiteAlsoStopped": false, + "clientAffinityEnabled": true, + "clientCertEnabled": false, + "hostNamesDisabled": false, + "containerSize": 0, + "dailyMemoryTimeQuota": 0, + "httpsOnly": false, + "redundancyMode": "None", + "siteConfig": { + "appSettings": [ + { + "name": "JAVA_OPTS", + "value": "-Dserver.port=80" + }, + { + "name": "MicrosoftAppId", + "value": "[parameters('appId')]" + }, + { + "name": "MicrosoftAppPassword", + "value": "[parameters('appSecret')]" + } + ], + "cors": { + "allowedOrigins": [ + "https://botservice.hosting.portal.azure.net", + "https://hosting.onecloud.azure-test.net/" + ] + } + } + } + }, + { + "type": "Microsoft.Web/sites/config", + "apiVersion": "2018-11-01", + "name": "[concat(variables('webAppName'), '/web')]", + "location": "[variables('resourcesLocation')]", + "dependsOn": [ + "[concat(variables('resourceGroupId'), '/providers/Microsoft.Web/sites/', variables('webAppName'))]" + ], + "properties": { + "numberOfWorkers": 1, + "defaultDocuments": [ + "Default.htm", + "Default.html", + "Default.asp", + "index.htm", + "index.html", + "iisstart.htm", + "default.aspx", + "index.php", + "hostingstart.html" + ], + "netFrameworkVersion": "v4.0", + "linuxFxVersion": "JAVA|8-jre8", + "requestTracingEnabled": false, + "remoteDebuggingEnabled": false, + "httpLoggingEnabled": false, + "logsDirectorySizeLimit": 35, + "detailedErrorLoggingEnabled": false, + "publishingUsername": "[variables('publishingUsername')]", + "scmType": "None", + "use32BitWorkerProcess": true, + "webSocketsEnabled": false, + "alwaysOn": true, + "managedPipelineMode": "Integrated", + "virtualApplications": [ + { + "virtualPath": "/", + "physicalPath": "site\\wwwroot", + "preloadEnabled": true + } + ], + "loadBalancing": "LeastRequests", + "experiments": { + "rampUpRules": [] + }, + "autoHealEnabled": false, + "localMySqlEnabled": false, + "ipSecurityRestrictions": [ + { + "ipAddress": "Any", + "action": "Allow", + "priority": 1, + "name": "Allow all", + "description": "Allow all access" + } + ], + "scmIpSecurityRestrictions": [ + { + "ipAddress": "Any", + "action": "Allow", + "priority": 1, + "name": "Allow all", + "description": "Allow all access" + } + ], + "scmIpSecurityRestrictionsUseMain": false, + "http20Enabled": false, + "minTlsVersion": "1.2", + "ftpsState": "AllAllowed", + "reservedInstanceCount": 0 + } + }, + { + "apiVersion": "2021-03-01", + "type": "Microsoft.BotService/botServices", + "name": "[parameters('botId')]", + "location": "global", + "kind": "azurebot", + "sku": { + "name": "[parameters('botSku')]" + }, + "properties": { + "name": "[parameters('botId')]", + "displayName": "[parameters('botId')]", + "iconUrl": "https://docs.botframework.com/static/devportal/client/images/bot-framework-default.png", + "endpoint": "[variables('botEndpoint')]", + "msaAppId": "[parameters('appId')]", + "luisAppIds": [], + "schemaTransformationVersion": "1.3", + "isCmekEnabled": false, + "isIsolated": false + }, + "dependsOn": [ + "[concat(variables('resourceGroupId'), '/providers/Microsoft.Web/sites/', variables('webAppName'))]" + ] + } + ], + "outputs": {} + } + } + } + ] +} diff --git a/generators/generators/app/templates/core/project/deploymentTemplates/template-with-preexisting-rg.json b/generators/generators/app/templates/core/project/deploymentTemplates/template-with-preexisting-rg.json new file mode 100644 index 000000000..d6feb0a0f --- /dev/null +++ b/generators/generators/app/templates/core/project/deploymentTemplates/template-with-preexisting-rg.json @@ -0,0 +1,260 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "appId": { + "type": "string", + "metadata": { + "description": "Active Directory App 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. Defaults to \"\"." + } + }, + "botId": { + "type": "string", + "metadata": { + "description": "The globally unique and immutable bot ID. Also used to configure the displayName of the bot, which is mutable." + } + }, + "botSku": { + "defaultValue": "S1", + "type": "string", + "metadata": { + "description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1." + } + }, + "newAppServicePlanName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the new App Service Plan." + } + }, + "newAppServicePlanSku": { + "type": "object", + "defaultValue": { + "name": "P1v2", + "tier": "PremiumV2", + "size": "P1v2", + "family": "Pv2", + "capacity": 1 + }, + "metadata": { + "description": "The SKU of the App Service Plan. Defaults to Standard values." + } + }, + "appServicePlanLocation": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The location of the App Service Plan." + } + }, + "existingAppServicePlan": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Name of the existing App Service Plan used to create the Web App for the bot." + } + }, + "newWebAppName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The globally unique name of the Web App. Defaults to the value passed in for \"botId\"." + } + } + }, + "variables": { + "defaultAppServicePlanName": "[if(empty(parameters('existingAppServicePlan')), 'createNewAppServicePlan', parameters('existingAppServicePlan'))]", + "useExistingAppServicePlan": "[not(equals(variables('defaultAppServicePlanName'), 'createNewAppServicePlan'))]", + "servicePlanName": "[if(variables('useExistingAppServicePlan'), parameters('existingAppServicePlan'), parameters('newAppServicePlanName'))]", + "publishingUsername": "[concat('$', parameters('newWebAppName'))]", + "resourcesLocation": "[parameters('appServicePlanLocation')]", + "webAppName": "[if(empty(parameters('newWebAppName')), parameters('botId'), parameters('newWebAppName'))]", + "siteHost": "[concat(variables('webAppName'), '.azurewebsites.net')]", + "botEndpoint": "[concat('https://', variables('siteHost'), '/api/messages')]" + }, + "resources": [ + { + "comments": "Create a new Linux App Service Plan if no existing App Service Plan name was passed in.", + "type": "Microsoft.Web/serverfarms", + "condition": "[not(variables('useExistingAppServicePlan'))]", + "name": "[variables('servicePlanName')]", + "apiVersion": "2018-02-01", + "location": "[variables('resourcesLocation')]", + "sku": "[parameters('newAppServicePlanSku')]", + "kind": "linux", + "properties": { + "perSiteScaling": false, + "maximumElasticWorkerCount": 1, + "isSpot": false, + "reserved": true, + "isXenon": false, + "hyperV": false, + "targetWorkerCount": 0, + "targetWorkerSizeId": 0 + } + }, + { + "comments": "Create a Web App using a Linux App Service Plan", + "type": "Microsoft.Web/sites", + "apiVersion": "2018-11-01", + "location": "[variables('resourcesLocation')]", + "kind": "app,linux", + "dependsOn": [ + "[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]" + ], + "name": "[variables('webAppName')]", + "properties": { + "name": "[variables('webAppName')]", + "hostNameSslStates": [ + { + "name": "[concat(parameters('newWebAppName'), '.azurewebsites.net')]", + "sslState": "Disabled", + "hostType": "Standard" + }, + { + "name": "[concat(parameters('newWebAppName'), '.scm.azurewebsites.net')]", + "sslState": "Disabled", + "hostType": "Repository" + } + ], + "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]", + "reserved": true, + "isXenon": false, + "hyperV": false, + "scmSiteAlsoStopped": false, + "clientAffinityEnabled": true, + "clientCertEnabled": false, + "hostNamesDisabled": false, + "containerSize": 0, + "dailyMemoryTimeQuota": 0, + "httpsOnly": false, + "redundancyMode": "None", + "siteConfig": { + "appSettings": [ + { + "name": "JAVA_OPTS", + "value": "-Dserver.port=80" + }, + { + "name": "MicrosoftAppId", + "value": "[parameters('appId')]" + }, + { + "name": "MicrosoftAppPassword", + "value": "[parameters('appSecret')]" + } + ], + "cors": { + "allowedOrigins": [ + "https://botservice.hosting.portal.azure.net", + "https://hosting.onecloud.azure-test.net/" + ] + } + } + } + }, + { + "type": "Microsoft.Web/sites/config", + "apiVersion": "2018-11-01", + "name": "[concat(variables('webAppName'), '/web')]", + "location": "[variables('resourcesLocation')]", + "dependsOn": [ + "[resourceId('Microsoft.Web/sites/', variables('webAppName'))]" + ], + "properties": { + "numberOfWorkers": 1, + "defaultDocuments": [ + "Default.htm", + "Default.html", + "Default.asp", + "index.htm", + "index.html", + "iisstart.htm", + "default.aspx", + "index.php", + "hostingstart.html" + ], + "netFrameworkVersion": "v4.0", + "linuxFxVersion": "JAVA|8-jre8", + "requestTracingEnabled": false, + "remoteDebuggingEnabled": false, + "httpLoggingEnabled": false, + "logsDirectorySizeLimit": 35, + "detailedErrorLoggingEnabled": false, + "publishingUsername": "[variables('publishingUsername')]", + "scmType": "None", + "use32BitWorkerProcess": true, + "webSocketsEnabled": false, + "alwaysOn": true, + "managedPipelineMode": "Integrated", + "virtualApplications": [ + { + "virtualPath": "/", + "physicalPath": "site\\wwwroot", + "preloadEnabled": true + } + ], + "loadBalancing": "LeastRequests", + "experiments": { + "rampUpRules": [] + }, + "autoHealEnabled": false, + "localMySqlEnabled": false, + "ipSecurityRestrictions": [ + { + "ipAddress": "Any", + "action": "Allow", + "priority": 1, + "name": "Allow all", + "description": "Allow all access" + } + ], + "scmIpSecurityRestrictions": [ + { + "ipAddress": "Any", + "action": "Allow", + "priority": 1, + "name": "Allow all", + "description": "Allow all access" + } + ], + "scmIpSecurityRestrictionsUseMain": false, + "http20Enabled": false, + "minTlsVersion": "1.2", + "ftpsState": "AllAllowed", + "reservedInstanceCount": 0 + } + }, + { + "apiVersion": "2021-03-01", + "type": "Microsoft.BotService/botServices", + "name": "[parameters('botId')]", + "location": "global", + "kind": "azurebot", + "sku": { + "name": "[parameters('botSku')]" + }, + "properties": { + "name": "[parameters('botId')]", + "displayName": "[parameters('botId')]", + "iconUrl": "https://docs.botframework.com/static/devportal/client/images/bot-framework-default.png", + "endpoint": "[variables('botEndpoint')]", + "msaAppId": "[parameters('appId')]", + "luisAppIds": [], + "schemaTransformationVersion": "1.3", + "isCmekEnabled": false, + "isIsolated": false + }, + "dependsOn": [ + "[resourceId('Microsoft.Web/sites/', variables('webAppName'))]" + ] + } + ] +} diff --git a/generators/generators/app/templates/core/project/pom.xml b/generators/generators/app/templates/core/project/pom.xml new file mode 100644 index 000000000..8306e22f8 --- /dev/null +++ b/generators/generators/app/templates/core/project/pom.xml @@ -0,0 +1,254 @@ + + + + 4.0.0 + + <%= packageName %> + <%= artifact %> + 1.0.0 + jar + + ${project.groupId}:${project.artifactId} + This package contains a Java Core Bot sample using Spring Boot. + http://maven.apache.org + + + org.springframework.boot + spring-boot-starter-parent + 2.4.0 + + + + + + MIT License + http://www.opensource.org/licenses/mit-license.php + + + + + + Bot Framework Development + + Microsoft + https://dev.botframework.com/ + + + + + 1.8 + 1.8 + 1.8 + <%= packageName %>.Application + + + + + org.springframework.boot + spring-boot-starter-test + 2.4.0 + test + + + junit + junit + 4.13.1 + test + + + org.junit.vintage + junit-vintage-engine + test + + + + org.slf4j + slf4j-api + + + org.apache.logging.log4j + log4j-api + 2.17.1 + + + org.apache.logging.log4j + log4j-core + 2.17.1 + + + org.apache.logging.log4j + log4j-to-slf4j + 2.15.0 + test + + + + com.microsoft.bot + bot-integration-spring + 4.14.1 + compile + + + com.microsoft.bot + bot-dialogs + 4.14.1 + + + com.microsoft.bot + bot-ai-luis-v3 + 4.14.1 + + + + + + build + + true + + + + + src/main/resources + false + + + + + maven-compiler-plugin + 3.8.1 + + + maven-war-plugin + 3.2.3 + + src/main/webapp + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + <%= packageName %>.Application + + + + + + com.microsoft.azure + azure-webapp-maven-plugin + 1.12.0 + + V2 + ${groupname} + ${botname} + + + JAVA_OPTS + -Dserver.port=80 + + + + linux + Java 8 + Java SE + + + + + ${project.basedir}/target + + *.jar + + + + + + + + + + + + publish + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + maven-war-plugin + 3.2.3 + + src/main/webapp + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.8 + true + + true + ossrh + https://oss.sonatype.org/ + true + + + + + org.apache.maven.plugins + maven-gpg-plugin + + + sign-artifacts + verify + + sign + + + + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + + jar + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + 8 + false + + + + attach-javadocs + + jar + + + + + + + + + diff --git a/generators/generators/app/templates/core/project/src/main/resources/application.properties b/generators/generators/app/templates/core/project/src/main/resources/application.properties new file mode 100644 index 000000000..255d7cd56 --- /dev/null +++ b/generators/generators/app/templates/core/project/src/main/resources/application.properties @@ -0,0 +1,6 @@ +MicrosoftAppId= +MicrosoftAppPassword= +LuisAppId= +LuisAPIKey= +LuisAPIHostName= +server.port=3978 diff --git a/generators/generators/app/templates/core/project/src/main/resources/cards/welcomeCard.json b/generators/generators/app/templates/core/project/src/main/resources/cards/welcomeCard.json new file mode 100644 index 000000000..9b6389e39 --- /dev/null +++ b/generators/generators/app/templates/core/project/src/main/resources/cards/welcomeCard.json @@ -0,0 +1,46 @@ +{ + "$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/generators/app/templates/core/project/src/main/resources/log4j2.json b/generators/generators/app/templates/core/project/src/main/resources/log4j2.json new file mode 100644 index 000000000..67c0ad530 --- /dev/null +++ b/generators/generators/app/templates/core/project/src/main/resources/log4j2.json @@ -0,0 +1,18 @@ +{ + "configuration": { + "name": "Default", + "appenders": { + "Console": { + "name": "Console-Appender", + "target": "SYSTEM_OUT", + "PatternLayout": {"pattern": "[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n"} + } + }, + "loggers": { + "root": { + "level": "debug", + "appender-ref": {"ref": "Console-Appender","level": "debug"} + } + } + } +} diff --git a/generators/generators/app/templates/core/project/src/main/webapp/META-INF/MANIFEST.MF b/generators/generators/app/templates/core/project/src/main/webapp/META-INF/MANIFEST.MF new file mode 100644 index 000000000..254272e1c --- /dev/null +++ b/generators/generators/app/templates/core/project/src/main/webapp/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Class-Path: + diff --git a/generators/generators/app/templates/core/project/src/main/webapp/WEB-INF/web.xml b/generators/generators/app/templates/core/project/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..383c19004 --- /dev/null +++ b/generators/generators/app/templates/core/project/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,12 @@ + + + dispatcher + + org.springframework.web.servlet.DispatcherServlet + + + contextConfigLocation + /WEB-INF/spring/dispatcher-config.xml + + 1 + \ No newline at end of file diff --git a/generators/generators/app/templates/core/project/src/main/webapp/index.html b/generators/generators/app/templates/core/project/src/main/webapp/index.html new file mode 100644 index 000000000..593ac520c --- /dev/null +++ b/generators/generators/app/templates/core/project/src/main/webapp/index.html @@ -0,0 +1,417 @@ + + + + + + + <%= botName %> + + + + + +
+
+
+
<%= botName %>
+
+
+
+
+
Your bot is ready!
+
You can test your bot in the Bot Framework Emulator
+ by connecting to http://localhost:3978/api/messages.
+ +
Visit Azure + Bot Service to register your bot and add it to
+ various channels. The bot's endpoint URL typically looks + like this:
+
https://your_bots_hostname/api/messages
+
+
+
+
+ +
+ + diff --git a/generators/generators/app/templates/core/src/main/java/Application.java b/generators/generators/app/templates/core/src/main/java/Application.java new file mode 100644 index 000000000..7f99e415d --- /dev/null +++ b/generators/generators/app/templates/core/src/main/java/Application.java @@ -0,0 +1,79 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package <%= packageName %>; + +import com.microsoft.bot.builder.Bot; +import com.microsoft.bot.builder.ConversationState; +import com.microsoft.bot.builder.UserState; +import com.microsoft.bot.integration.AdapterWithErrorHandler; +import com.microsoft.bot.integration.BotFrameworkHttpAdapter; +import com.microsoft.bot.integration.Configuration; +import com.microsoft.bot.integration.spring.BotController; +import com.microsoft.bot.integration.spring.BotDependencyConfiguration; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; + +/** + * This is the starting point of the Sprint Boot Bot application. + */ +@SpringBootApplication + +// Use the default BotController to receive incoming Channel messages. A custom +// controller could be used by eliminating this import and creating a new +// org.springframework.web.bind.annotation.RestController. +// The default controller is created by the Spring Boot container using +// dependency injection. The default route is /api/messages. +@Import({BotController.class}) + +/** + * This class extends the BotDependencyConfiguration which provides the default + * implementations for a Bot application. The Application class should + * override methods in order to provide custom implementations. + */ +public class Application extends BotDependencyConfiguration { + + /** + * The start method. + * + * @param args The args. + */ + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + + /** + * Returns the Bot for this application. + * + *

+ * The @Component annotation could be used on the Bot class instead of this method with the + * @Bean annotation. + *

+ * + * @return The Bot implementation for this application. + */ + @Bean + public Bot getBot( + Configuration configuration, + UserState userState, + ConversationState conversationState + ) { + FlightBookingRecognizer recognizer = new FlightBookingRecognizer(configuration); + MainDialog dialog = new MainDialog(recognizer, new BookingDialog()); + return new DialogAndWelcomeBot<>(conversationState, userState, dialog); + } + + /** + * Returns a custom Adapter that provides error handling. + * + * @param configuration The Configuration object to use. + * @return An error handling BotFrameworkHttpAdapter. + */ + @Override + public BotFrameworkHttpAdapter getBotFrameworkHttpAdaptor(Configuration configuration) { + return new AdapterWithErrorHandler(configuration); + } +} + diff --git a/generators/generators/app/templates/core/src/main/java/BookingDetails.java b/generators/generators/app/templates/core/src/main/java/BookingDetails.java new file mode 100644 index 000000000..160242106 --- /dev/null +++ b/generators/generators/app/templates/core/src/main/java/BookingDetails.java @@ -0,0 +1,68 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package <%= packageName %>; + +/** + * The model class to retrieve the information of the booking. + */ +public class BookingDetails { + + private String destination; + private String origin; + private String travelDate; + + /** + * Gets the destination of the booking. + * + * @return The destination. + */ + public String getDestination() { + return destination; + } + + /** + * Sets the destination of the booking. + * + * @param withDestination The new destination. + */ + public void setDestination(String withDestination) { + this.destination = withDestination; + } + + /** + * Gets the origin of the booking. + * + * @return The origin. + */ + public String getOrigin() { + return origin; + } + + /** + * Sets the origin of the booking. + * + * @param withOrigin The new origin. + */ + public void setOrigin(String withOrigin) { + this.origin = withOrigin; + } + + /** + * Gets the travel date of the booking. + * + * @return The travel date. + */ + public String getTravelDate() { + return travelDate; + } + + /** + * Sets the travel date of the booking. + * + * @param withTravelDate The new travel date. + */ + public void setTravelDate(String withTravelDate) { + this.travelDate = withTravelDate; + } +} diff --git a/generators/generators/app/templates/core/src/main/java/BookingDialog.java b/generators/generators/app/templates/core/src/main/java/BookingDialog.java new file mode 100644 index 000000000..d5e7322b3 --- /dev/null +++ b/generators/generators/app/templates/core/src/main/java/BookingDialog.java @@ -0,0 +1,127 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package <%= packageName %>; + +import java.util.Arrays; +import java.util.concurrent.CompletableFuture; + +import com.microsoft.bot.builder.MessageFactory; +import com.microsoft.bot.dialogs.DialogTurnResult; +import com.microsoft.bot.dialogs.WaterfallDialog; +import com.microsoft.bot.dialogs.WaterfallStep; +import com.microsoft.bot.dialogs.WaterfallStepContext; +import com.microsoft.bot.dialogs.prompts.ConfirmPrompt; +import com.microsoft.bot.dialogs.prompts.PromptOptions; +import com.microsoft.bot.dialogs.prompts.TextPrompt; +import com.microsoft.bot.schema.Activity; +import com.microsoft.bot.schema.InputHints; +import com.microsoft.recognizers.datatypes.timex.expression.Constants; +import com.microsoft.recognizers.datatypes.timex.expression.TimexProperty; + +/** + * The class containing the booking dialogs. + */ +public class BookingDialog extends CancelAndHelpDialog { + + private final String destinationStepMsgText = "Where would you like to travel to?"; + private final String originStepMsgText = "Where are you traveling from?"; + + /** + * The constructor of the Booking Dialog class. + */ + public BookingDialog() { + super("BookingDialog"); + + addDialog(new TextPrompt("TextPrompt")); + addDialog(new ConfirmPrompt("ConfirmPrompt")); + addDialog(new DateResolverDialog(null)); + WaterfallStep[] waterfallSteps = { + this::destinationStep, + this::originStep, + this::travelDateStep, + this::confirmStep, + this::finalStep + }; + addDialog(new WaterfallDialog("WaterfallDialog", Arrays.asList(waterfallSteps))); + + // The initial child Dialog to run. + setInitialDialogId("WaterfallDialog"); + } + + private CompletableFuture destinationStep(WaterfallStepContext stepContext) { + BookingDetails bookingDetails = (BookingDetails) stepContext.getOptions(); + + if (bookingDetails.getDestination() == null || bookingDetails.getDestination().trim().isEmpty()) { + Activity promptMessage = + MessageFactory.text(destinationStepMsgText, destinationStepMsgText, + InputHints.EXPECTING_INPUT + ); + PromptOptions promptOptions = new PromptOptions(); + promptOptions.setPrompt(promptMessage); + return stepContext.prompt("TextPrompt", promptOptions); + } + + return stepContext.next(bookingDetails.getDestination()); + } + + private CompletableFuture originStep(WaterfallStepContext stepContext) { + BookingDetails bookingDetails = (BookingDetails) stepContext.getOptions(); + + bookingDetails.setDestination((String) stepContext.getResult()); + + if (bookingDetails.getOrigin() == null || bookingDetails.getOrigin().trim().isEmpty()) { + Activity promptMessage = + MessageFactory + .text(originStepMsgText, originStepMsgText, InputHints.EXPECTING_INPUT); + PromptOptions promptOptions = new PromptOptions(); + promptOptions.setPrompt(promptMessage); + return stepContext.prompt("TextPrompt", promptOptions); + } + + return stepContext.next(bookingDetails.getOrigin()); + } + + private CompletableFuture travelDateStep(WaterfallStepContext stepContext) { + BookingDetails bookingDetails = (BookingDetails) stepContext.getOptions(); + + bookingDetails.setOrigin((String) stepContext.getResult()); + + if (bookingDetails.getTravelDate() == null || isAmbiguous(bookingDetails.getTravelDate())) { + return stepContext.beginDialog("DateResolverDialog", bookingDetails.getTravelDate()); + } + + return stepContext.next(bookingDetails.getTravelDate()); + } + + private CompletableFuture confirmStep(WaterfallStepContext stepContext) { + BookingDetails bookingDetails = (BookingDetails) stepContext.getOptions(); + + bookingDetails.setTravelDate((String) stepContext.getResult()); + + String messageText = String.format( + "Please confirm, I have you traveling to: %s from: %s on: %s. Is this correct?", + bookingDetails.getDestination(), bookingDetails.getOrigin(), bookingDetails.getTravelDate()); + Activity promptMessage = MessageFactory.text(messageText, messageText, InputHints.EXPECTING_INPUT); + + PromptOptions promptOptions = new PromptOptions(); + promptOptions.setPrompt(promptMessage); + + return stepContext.prompt("ConfirmPrompt", promptOptions); + } + + + private CompletableFuture finalStep(WaterfallStepContext stepContext) { + if ((Boolean) stepContext.getResult()) { + BookingDetails bookingDetails = (BookingDetails) stepContext.getOptions(); + return stepContext.endDialog(bookingDetails); + } + + return stepContext.endDialog(null); + } + + private static boolean isAmbiguous(String timex) { + TimexProperty timexProperty = new TimexProperty(timex); + return !timexProperty.getTypes().contains(Constants.TimexTypes.DEFINITE); + } +} diff --git a/generators/generators/app/templates/core/src/main/java/CancelAndHelpDialog.java b/generators/generators/app/templates/core/src/main/java/CancelAndHelpDialog.java new file mode 100644 index 000000000..209b9b3b0 --- /dev/null +++ b/generators/generators/app/templates/core/src/main/java/CancelAndHelpDialog.java @@ -0,0 +1,80 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package <%= packageName %>; + +import com.microsoft.bot.builder.MessageFactory; +import com.microsoft.bot.dialogs.ComponentDialog; +import com.microsoft.bot.dialogs.DialogContext; +import com.microsoft.bot.dialogs.DialogTurnResult; +import com.microsoft.bot.dialogs.DialogTurnStatus; +import com.microsoft.bot.schema.Activity; +import com.microsoft.bot.schema.ActivityTypes; +import com.microsoft.bot.schema.InputHints; + +import java.util.concurrent.CompletableFuture; + +/** + * The class in charge of the dialog interruptions. + */ +public class CancelAndHelpDialog extends ComponentDialog { + + private final String helpMsgText = "Show help here"; + private final String cancelMsgText = "Cancelling..."; + + /** + * The constructor of the CancelAndHelpDialog class. + * + * @param id The dialog's Id. + */ + public CancelAndHelpDialog(String id) { + super(id); + } + + /** + * Called when the dialog is _continued_, where it is the active dialog and the user replies + * with a new activity. + * + * @param innerDc innerDc The inner {@link DialogContext} for the current turn of conversation. + * @return A {@link CompletableFuture} representing the asynchronous operation. If the task is + * successful, the result indicates whether the dialog is still active after the turn has been + * processed by the dialog. The result may also contain a return value. + */ + @Override + protected CompletableFuture onContinueDialog(DialogContext innerDc) { + return interrupt(innerDc).thenCompose(result -> { + if (result != null) { + return CompletableFuture.completedFuture(result); + } + return super.onContinueDialog(innerDc); + }); + } + + private CompletableFuture interrupt(DialogContext innerDc) { + if (innerDc.getContext().getActivity().isType(ActivityTypes.MESSAGE)) { + String text = innerDc.getContext().getActivity().getText().toLowerCase(); + + switch (text) { + case "help": + case "?": + Activity helpMessage = MessageFactory + .text(helpMsgText, helpMsgText, InputHints.EXPECTING_INPUT); + return innerDc.getContext().sendActivity(helpMessage) + .thenCompose(sendResult -> + CompletableFuture + .completedFuture(new DialogTurnResult(DialogTurnStatus.WAITING))); + case "cancel": + case "quit": + Activity cancelMessage = MessageFactory + .text(cancelMsgText, cancelMsgText, InputHints.IGNORING_INPUT); + return innerDc.getContext() + .sendActivity(cancelMessage) + .thenCompose(sendResult -> innerDc.cancelAllDialogs()); + default: + break; + } + } + + return CompletableFuture.completedFuture(null); + } +} diff --git a/generators/generators/app/templates/core/src/main/java/DateResolverDialog.java b/generators/generators/app/templates/core/src/main/java/DateResolverDialog.java new file mode 100644 index 000000000..687eb45ac --- /dev/null +++ b/generators/generators/app/templates/core/src/main/java/DateResolverDialog.java @@ -0,0 +1,109 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package <%= packageName %>; + +import com.microsoft.bot.builder.MessageFactory; +import com.microsoft.bot.dialogs.DialogTurnResult; +import com.microsoft.bot.dialogs.WaterfallDialog; +import com.microsoft.bot.dialogs.WaterfallStep; +import com.microsoft.bot.dialogs.WaterfallStepContext; +import com.microsoft.bot.dialogs.prompts.DateTimePrompt; +import com.microsoft.bot.dialogs.prompts.DateTimeResolution; +import com.microsoft.bot.dialogs.prompts.PromptOptions; +import com.microsoft.bot.dialogs.prompts.PromptValidatorContext; +import com.microsoft.bot.schema.Activity; +import com.microsoft.bot.schema.InputHints; +import com.microsoft.recognizers.datatypes.timex.expression.Constants; +import com.microsoft.recognizers.datatypes.timex.expression.TimexProperty; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +/** + * The class containing the date resolver dialogs. + */ +public class DateResolverDialog extends CancelAndHelpDialog { + private final String promptMsgText = "When would you like to travel?"; + private final String repromptMsgText = + "I'm sorry, to make your booking please enter a full travel date including Day Month and Year."; + + + /** + * The constructor of the DateResolverDialog class. + * @param id The dialog's id. + */ + public DateResolverDialog(@Nullable String id) { + super(id != null ? id : "DateResolverDialog"); + + + addDialog(new DateTimePrompt("DateTimePrompt", + DateResolverDialog::dateTimePromptValidator, null)); + WaterfallStep[] waterfallSteps = { + this::initialStep, + this::finalStep + }; + addDialog(new WaterfallDialog("WaterfallDialog", Arrays.asList(waterfallSteps))); + + // The initial child Dialog to run. + setInitialDialogId("WaterfallDialog"); + } + + private CompletableFuture initialStep(WaterfallStepContext stepContext) { + String timex = (String) stepContext.getOptions(); + + Activity promptMessage = MessageFactory.text(promptMsgText, promptMsgText, InputHints.EXPECTING_INPUT); + Activity repromptMessage = MessageFactory.text(repromptMsgText, repromptMsgText, InputHints.EXPECTING_INPUT); + + if (timex == null) { + // We were not given any date at all so prompt the user. + PromptOptions promptOptions = new PromptOptions(); + promptOptions.setPrompt(promptMessage); + promptOptions.setRetryPrompt(repromptMessage); + return stepContext.prompt("DateTimePrompt", promptOptions); + } + + // We have a Date we just need to check it is unambiguous. + TimexProperty timexProperty = new TimexProperty(timex); + if (!timexProperty.getTypes().contains(Constants.TimexTypes.DEFINITE)) { + // This is essentially a "reprompt" of the data we were given up front. + PromptOptions promptOptions = new PromptOptions(); + promptOptions.setPrompt(repromptMessage); + return stepContext.prompt("DateTimePrompt", promptOptions); + } + + DateTimeResolution dateTimeResolution = new DateTimeResolution(); + dateTimeResolution.setTimex(timex); + List dateTimeResolutions = new ArrayList(); + dateTimeResolutions.add(dateTimeResolution); + return stepContext.next(dateTimeResolutions); + } + + private CompletableFuture finalStep(WaterfallStepContext stepContext) { + String timex = ((ArrayList) stepContext.getResult()).get(0).getTimex(); + return stepContext.endDialog(timex); + } + + private static CompletableFuture dateTimePromptValidator( + PromptValidatorContext> promptContext + ) { + if (promptContext.getRecognized().getSucceeded()) { + // 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. + String timex = ((List) promptContext.getRecognized().getValue()) + .get(0).getTimex().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. + Boolean isDefinite = new TimexProperty(timex).getTypes().contains(Constants.TimexTypes.DEFINITE); + + return CompletableFuture.completedFuture(isDefinite); + } + + return CompletableFuture.completedFuture(false); + } +} diff --git a/generators/generators/app/templates/core/src/main/java/DialogAndWelcomeBot.java b/generators/generators/app/templates/core/src/main/java/DialogAndWelcomeBot.java new file mode 100644 index 000000000..13143ced2 --- /dev/null +++ b/generators/generators/app/templates/core/src/main/java/DialogAndWelcomeBot.java @@ -0,0 +1,99 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package <%= packageName %>; + +import com.codepoetics.protonpack.collectors.CompletableFutures; +import com.microsoft.bot.builder.ConversationState; +import com.microsoft.bot.builder.MessageFactory; +import com.microsoft.bot.builder.TurnContext; +import com.microsoft.bot.builder.UserState; +import com.microsoft.bot.dialogs.Dialog; +import com.microsoft.bot.schema.Activity; +import com.microsoft.bot.schema.Attachment; +import com.microsoft.bot.schema.ChannelAccount; +import com.microsoft.bot.schema.Serialization; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; + +/** + * The class containing the welcome dialog. + * + * @param is a Dialog. + */ +public class DialogAndWelcomeBot extends DialogBot { + + /** + * Creates a DialogBot. + * + * @param withConversationState ConversationState to use in the bot + * @param withUserState UserState to use + * @param withDialog Param inheriting from Dialog class + */ + public DialogAndWelcomeBot( + ConversationState withConversationState, UserState withUserState, T withDialog + ) { + super(withConversationState, withUserState, withDialog); + } + + /** + * When the {@link #onConversationUpdateActivity(TurnContext)} method receives a conversation + * update activity that indicates one or more users other than the bot are joining the + * conversation, it calls this method. + * + * @param membersAdded A list of all the members added to the conversation, as described by the + * conversation update activity + * @param turnContext The context object for this turn. + * @return A task that represents the work queued to execute. + */ + @Override + protected CompletableFuture onMembersAdded( + List membersAdded, TurnContext turnContext + ) { + return turnContext.getActivity().getMembersAdded().stream() + .filter(member -> !StringUtils + .equals(member.getId(), turnContext.getActivity().getRecipient().getId())) + .map(channel -> { + // Greet anyone that was not the target (recipient) of this message. + // To learn more about Adaptive Cards, see https://aka.ms/msbot-adaptivecards for more details. + Attachment welcomeCard = createAdaptiveCardAttachment(); + Activity response = MessageFactory + .attachment(welcomeCard, null, "Welcome to Bot Framework!", null); + + return turnContext.sendActivity(response).thenApply(sendResult -> { + return Dialog.run(getDialog(), turnContext, + getConversationState().createProperty("DialogState") + ); + }); + }) + .collect(CompletableFutures.toFutureList()) + .thenApply(resourceResponse -> null); + } + + // Load attachment from embedded resource. + private Attachment createAdaptiveCardAttachment() { + try ( + InputStream inputStream = Thread.currentThread(). + getContextClassLoader().getResourceAsStream("cards/welcomeCard.json") + ) { + String adaptiveCardJson = IOUtils + .toString(inputStream, StandardCharsets.UTF_8.toString()); + + Attachment attachment = new Attachment(); + attachment.setContentType("application/vnd.microsoft.card.adaptive"); + attachment.setContent(Serialization.jsonToTree(adaptiveCardJson)); + return attachment; + + } catch (IOException e) { + e.printStackTrace(); + return new Attachment(); + } + } +} diff --git a/generators/generators/app/templates/core/src/main/java/DialogBot.java b/generators/generators/app/templates/core/src/main/java/DialogBot.java new file mode 100644 index 000000000..02b74135c --- /dev/null +++ b/generators/generators/app/templates/core/src/main/java/DialogBot.java @@ -0,0 +1,127 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package <%= packageName %>; + +import com.microsoft.bot.builder.ActivityHandler; +import com.microsoft.bot.builder.BotState; +import com.microsoft.bot.builder.ConversationState; +import com.microsoft.bot.builder.TurnContext; +import com.microsoft.bot.builder.UserState; +import com.microsoft.bot.dialogs.Dialog; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.CompletableFuture; + +/** + * This Bot implementation can run any type of Dialog. The use of type parameterization is to allow + * multiple different bots to be run at different endpoints within the same project. This can be + * achieved by defining distinct Controller types each with dependency on distinct Bot types. The + * ConversationState is used by the Dialog system. The UserState isn't, however, it might have been + * used in a Dialog implementation, and the requirement is that all BotState objects are saved at + * the end of a turn. + * + * @param parameter of a type inheriting from Dialog + */ +public class DialogBot extends ActivityHandler { + + private Dialog dialog; + private BotState conversationState; + private BotState userState; + + /** + * Gets the dialog in use. + * + * @return instance of dialog + */ + protected Dialog getDialog() { + return dialog; + } + + /** + * Gets the conversation state. + * + * @return instance of conversationState + */ + protected BotState getConversationState() { + return conversationState; + } + + /** + * Gets the user state. + * + * @return instance of userState + */ + protected BotState getUserState() { + return userState; + } + + /** + * Sets the dialog in use. + * + * @param withDialog the dialog (of Dialog type) to be set + */ + protected void setDialog(Dialog withDialog) { + dialog = withDialog; + } + + /** + * Sets the conversation state. + * + * @param withConversationState the conversationState (of BotState type) to be set + */ + protected void setConversationState(BotState withConversationState) { + conversationState = withConversationState; + } + + /** + * Sets the user state. + * + * @param withUserState the userState (of BotState type) to be set + */ + protected void setUserState(BotState withUserState) { + userState = withUserState; + } + + /** + * Creates a DialogBot. + * + * @param withConversationState ConversationState to use in the bot + * @param withUserState UserState to use + * @param withDialog Param inheriting from Dialog class + */ + public DialogBot( + ConversationState withConversationState, UserState withUserState, T withDialog + ) { + this.conversationState = withConversationState; + this.userState = withUserState; + this.dialog = withDialog; + } + + /** + * Saves the BotState objects at the end of each turn. + * + * @param turnContext + * @return + */ + @Override + public CompletableFuture onTurn(TurnContext turnContext) { + return super.onTurn(turnContext) + .thenCompose(turnResult -> conversationState.saveChanges(turnContext, false)) + .thenCompose(saveResult -> userState.saveChanges(turnContext, false)); + } + + /** + * This method is executed when the turnContext receives a message activity. + * + * @param turnContext + * @return + */ + @Override + protected CompletableFuture onMessageActivity(TurnContext turnContext) { + LoggerFactory.getLogger(DialogBot.class).info("Running dialog with Message Activity."); + + // Run the Dialog with the new message Activity. + return Dialog.run(dialog, turnContext, conversationState.createProperty("DialogState")); + } +} diff --git a/generators/generators/app/templates/core/src/main/java/FlightBookingRecognizer.java b/generators/generators/app/templates/core/src/main/java/FlightBookingRecognizer.java new file mode 100644 index 000000000..9d328e1b0 --- /dev/null +++ b/generators/generators/app/templates/core/src/main/java/FlightBookingRecognizer.java @@ -0,0 +1,153 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package <%= packageName %>; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.microsoft.bot.ai.luis.LuisApplication; +import com.microsoft.bot.ai.luis.LuisRecognizer; +import com.microsoft.bot.ai.luis.LuisRecognizerOptionsV3; +import com.microsoft.bot.builder.Recognizer; +import com.microsoft.bot.builder.RecognizerResult; +import com.microsoft.bot.builder.TurnContext; +import com.microsoft.bot.integration.Configuration; +import org.apache.commons.lang3.StringUtils; + +import java.util.concurrent.CompletableFuture; + +/** + * The class in charge of recognizing the booking information. + */ +public class FlightBookingRecognizer implements Recognizer { + + private LuisRecognizer recognizer; + + /** + * The constructor of the FlightBookingRecognizer class. + * + * @param configuration The Configuration object to use. + */ + public FlightBookingRecognizer(Configuration configuration) { + Boolean luisIsConfigured = StringUtils.isNotBlank(configuration.getProperty("LuisAppId")) + && StringUtils.isNotBlank(configuration.getProperty("LuisAPIKey")) + && StringUtils.isNotBlank(configuration.getProperty("LuisAPIHostName")); + if (luisIsConfigured) { + LuisApplication luisApplication = new LuisApplication( + configuration.getProperty("LuisAppId"), + configuration.getProperty("LuisAPIKey"), + String.format("https://%s", configuration.getProperty("LuisAPIHostName")) + ); + // Set the recognizer options depending on which endpoint version you want to use. + // More details can be found in + // https://docs.microsoft.com/en-gb/azure/cognitive-services/luis/luis-migration-api-v3 + LuisRecognizerOptionsV3 recognizerOptions = new LuisRecognizerOptionsV3(luisApplication); + recognizerOptions.setIncludeInstanceData(true); + + this.recognizer = new LuisRecognizer(recognizerOptions); + } + } + + /** + * Verify if the recognizer is configured. + * + * @return True if it's configured, False if it's not. + */ + public Boolean isConfigured() { + return this.recognizer != null; + } + + /** + * Return an object with preformatted LUIS results for the bot's dialogs to consume. + * + * @param context A {link TurnContext} + * @return A {link RecognizerResult} + */ + public CompletableFuture executeLuisQuery(TurnContext context) { + // Returns true if luis is configured in the application.properties and initialized. + return this.recognizer.recognize(context); + } + + /** + * Gets the From data from the entities which is part of the result. + * + * @param result The recognizer result. + * @return The object node representing the From data. + */ + public ObjectNode getFromEntities(RecognizerResult result) { + String fromValue = "", fromAirportValue = ""; + if (result.getEntities().get("$instance").get("From") != null) { + fromValue = result.getEntities().get("$instance").get("From").get(0).get("text") + .asText(); + } + if (!fromValue.isEmpty() + && result.getEntities().get("From").get(0).get("Airport") != null) { + fromAirportValue = result.getEntities().get("From").get(0).get("Airport").get(0).get(0) + .asText(); + } + + ObjectMapper mapper = new ObjectMapper().findAndRegisterModules(); + ObjectNode entitiesNode = mapper.createObjectNode(); + entitiesNode.put("from", fromValue); + entitiesNode.put("airport", fromAirportValue); + return entitiesNode; + } + + /** + * Gets the To data from the entities which is part of the result. + * + * @param result The recognizer result. + * @return The object node representing the To data. + */ + public ObjectNode getToEntities(RecognizerResult result) { + String toValue = "", toAirportValue = ""; + if (result.getEntities().get("$instance").get("To") != null) { + toValue = result.getEntities().get("$instance").get("To").get(0).get("text").asText(); + } + if (!toValue.isEmpty() && result.getEntities().get("To").get(0).get("Airport") != null) { + toAirportValue = result.getEntities().get("To").get(0).get("Airport").get(0).get(0) + .asText(); + } + + ObjectMapper mapper = new ObjectMapper().findAndRegisterModules(); + ObjectNode entitiesNode = mapper.createObjectNode(); + entitiesNode.put("to", toValue); + entitiesNode.put("airport", toAirportValue); + return entitiesNode; + } + + /** + * 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. + * + * @param result A {link RecognizerResult} + * @return The Timex value without the Time model + */ + public String getTravelDate(RecognizerResult result) { + JsonNode datetimeEntity = result.getEntities().get("datetime"); + if (datetimeEntity == null || datetimeEntity.get(0) == null) { + return null; + } + + JsonNode timex = datetimeEntity.get(0).get("timex"); + if (timex == null || timex.get(0) == null) { + return null; + } + + String datetime = timex.get(0).asText().split("T")[0]; + return datetime; + } + + /** + * Runs an utterance through a recognizer and returns a generic recognizer result. + * + * @param turnContext Turn context. + * @return Analysis of utterance. + */ + @Override + public CompletableFuture recognize(TurnContext turnContext) { + return this.recognizer.recognize(turnContext); + } +} diff --git a/generators/generators/app/templates/core/src/main/java/MainDialog.java b/generators/generators/app/templates/core/src/main/java/MainDialog.java new file mode 100644 index 000000000..7b6cd1d8c --- /dev/null +++ b/generators/generators/app/templates/core/src/main/java/MainDialog.java @@ -0,0 +1,232 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package <%= packageName %>; + +import com.microsoft.bot.builder.MessageFactory; +import com.microsoft.bot.builder.TurnContext; +import com.microsoft.bot.dialogs.ComponentDialog; +import com.microsoft.bot.dialogs.DialogTurnResult; +import com.microsoft.bot.dialogs.WaterfallDialog; +import com.microsoft.bot.dialogs.WaterfallStep; +import com.microsoft.bot.dialogs.WaterfallStepContext; +import com.microsoft.bot.dialogs.prompts.PromptOptions; +import com.microsoft.bot.dialogs.prompts.TextPrompt; +import com.microsoft.bot.schema.Activity; +import com.microsoft.bot.schema.InputHints; +import com.microsoft.recognizers.datatypes.timex.expression.TimexProperty; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.apache.commons.lang3.StringUtils; + +/** + * The class containing the main dialog for the sample. + */ +public class MainDialog extends ComponentDialog { + + private final FlightBookingRecognizer luisRecognizer; + private final Integer plusDayValue = 7; + + /** + * The constructor of the Main Dialog class. + * + * @param withLuisRecognizer The FlightBookingRecognizer object. + * @param bookingDialog The BookingDialog object with booking dialogs. + */ + public MainDialog(FlightBookingRecognizer withLuisRecognizer, BookingDialog bookingDialog) { + super("MainDialog"); + + luisRecognizer = withLuisRecognizer; + + addDialog(new TextPrompt("TextPrompt")); + addDialog(bookingDialog); + WaterfallStep[] waterfallSteps = { + this::introStep, + this::actStep, + this::finalStep + }; + addDialog(new WaterfallDialog("WaterfallDialog", Arrays.asList(waterfallSteps))); + + // The initial child Dialog to run. + setInitialDialogId("WaterfallDialog"); + } + + /** + * 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. + * + * @param stepContext A {@link WaterfallStepContext} + * @return A {@link DialogTurnResult} + */ + private CompletableFuture introStep(WaterfallStepContext stepContext) { + if (!luisRecognizer.isConfigured()) { + Activity text = MessageFactory.text("NOTE: LUIS is not configured. " + + "To enable all capabilities, add 'LuisAppId', 'LuisAPIKey' and 'LuisAPIHostName' " + + "to the appsettings.json file.", null, InputHints.IGNORING_INPUT); + return stepContext.getContext().sendActivity(text) + .thenCompose(sendResult -> stepContext.next(null)); + } + + // Use the text provided in FinalStepAsync or the default if it is the first time. + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMMM d, yyyy"); + String weekLaterDate = LocalDateTime.now().plusDays(plusDayValue).format(formatter); + String messageText = stepContext.getOptions() != null + ? stepContext.getOptions().toString() + : String.format("What can I help you with today?\n" + + "Say something like \"Book a flight from Paris to Berlin on %s\"", weekLaterDate); + Activity promptMessage = MessageFactory + .text(messageText, messageText, InputHints.EXPECTING_INPUT); + PromptOptions promptOptions = new PromptOptions(); + promptOptions.setPrompt(promptMessage); + return stepContext.prompt("TextPrompt", promptOptions); + } + + /** + * 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. + * + * @param stepContext A {@link WaterfallStepContext} + * @return A {@link DialogTurnResult} + */ + private CompletableFuture actStep(WaterfallStepContext stepContext) { + if (!luisRecognizer.isConfigured()) { + // LUIS is not configured, we just run the BookingDialog path with an empty BookingDetailsInstance. + return stepContext.beginDialog("BookingDialog", new BookingDetails()); + } + + // Call LUIS and gather any potential booking details. (Note the TurnContext has the response to the prompt.) + return luisRecognizer.recognize(stepContext.getContext()).thenCompose(luisResult -> { + switch (luisResult.getTopScoringIntent().intent) { + case "BookFlight": + // Extract the values for the composite entities from the LUIS result. + ObjectNode fromEntities = luisRecognizer.getFromEntities(luisResult); + ObjectNode toEntities = luisRecognizer.getToEntities(luisResult); + + // Show a warning for Origin and Destination if we can't resolve them. + return showWarningForUnsupportedCities( + stepContext.getContext(), fromEntities, toEntities) + .thenCompose(showResult -> { + // Initialize BookingDetails with any entities we may have found in the response. + + BookingDetails bookingDetails = new BookingDetails(); + bookingDetails.setDestination(toEntities.get("airport").asText()); + bookingDetails.setOrigin(fromEntities.get("airport").asText()); + bookingDetails.setTravelDate(luisRecognizer.getTravelDate(luisResult)); + // Run the BookingDialog giving it whatever details we have from the LUIS call, + // it will fill out the remainder. + return stepContext.beginDialog("BookingDialog", bookingDetails); + } + ); + case "GetWeather": + // We haven't implemented the GetWeatherDialog so we just display a TODO message. + String getWeatherMessageText = "TODO: get weather flow here"; + Activity getWeatherMessage = MessageFactory + .text( + getWeatherMessageText, getWeatherMessageText, + InputHints.IGNORING_INPUT + ); + return stepContext.getContext().sendActivity(getWeatherMessage) + .thenCompose(resourceResponse -> stepContext.next(null)); + + default: + // Catch all for unhandled intents + String didntUnderstandMessageText = String.format( + "Sorry, I didn't get that. Please " + + " try asking in a different way (intent was %s)", + luisResult.getTopScoringIntent().intent + ); + Activity didntUnderstandMessage = MessageFactory + .text( + didntUnderstandMessageText, didntUnderstandMessageText, + InputHints.IGNORING_INPUT + ); + return stepContext.getContext().sendActivity(didntUnderstandMessage) + .thenCompose(resourceResponse -> stepContext.next(null)); + } + }); + } + + /** + * 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. + * + * @param turnContext A {@link WaterfallStepContext} + * @param fromEntities An ObjectNode with the entities of From object + * @param toEntities An ObjectNode with the entities of To object + * @return A task + */ + private static CompletableFuture showWarningForUnsupportedCities( + TurnContext turnContext, + ObjectNode fromEntities, + ObjectNode toEntities + ) { + List unsupportedCities = new ArrayList(); + + if (StringUtils.isNotBlank(fromEntities.get("from").asText()) + && StringUtils.isBlank(fromEntities.get("airport").asText())) { + unsupportedCities.add(fromEntities.get("from").asText()); + } + + if (StringUtils.isNotBlank(toEntities.get("to").asText()) + && StringUtils.isBlank(toEntities.get("airport").asText())) { + unsupportedCities.add(toEntities.get("to").asText()); + } + + if (!unsupportedCities.isEmpty()) { + String messageText = String.format( + "Sorry but the following airports are not supported: %s", + String.join(", ", unsupportedCities) + ); + Activity message = MessageFactory + .text(messageText, messageText, InputHints.IGNORING_INPUT); + return turnContext.sendActivity(message) + .thenApply(sendResult -> null); + } + + return CompletableFuture.completedFuture(null); + } + + /** + * This is the final step in the main waterfall dialog. It wraps up the sample "book a flight" + * interaction with a simple confirmation. + * + * @param stepContext A {@link WaterfallStepContext} + * @return A {@link DialogTurnResult} + */ + private CompletableFuture finalStep(WaterfallStepContext stepContext) { + CompletableFuture stepResult = CompletableFuture.completedFuture(null); + + // If the child dialog ("BookingDialog") was cancelled, + // the user failed to confirm or if the intent wasn't BookFlight + // the Result here will be null. + if (stepContext.getResult() instanceof BookingDetails) { + // Now we have all the booking details call the booking service. + // If the call to the booking service was successful tell the user. + BookingDetails result = (BookingDetails) stepContext.getResult(); + TimexProperty timeProperty = new TimexProperty(result.getTravelDate()); + String travelDateMsg = timeProperty.toNaturalLanguage(LocalDateTime.now()); + String messageText = String.format("I have you booked to %s from %s on %s", + result.getDestination(), result.getOrigin(), travelDateMsg + ); + Activity message = MessageFactory + .text(messageText, messageText, InputHints.IGNORING_INPUT); + stepResult = stepContext.getContext().sendActivity(message).thenApply(sendResult -> null); + } + + // Restart the main dialog with a different message the second time around + String promptMessage = "What else can I do for you?"; + return stepResult + .thenCompose(result -> stepContext.replaceDialog(getInitialDialogId(), promptMessage)); + } +} diff --git a/generators/generators/app/templates/core/src/main/java/package-info.java b/generators/generators/app/templates/core/src/main/java/package-info.java new file mode 100644 index 000000000..1a5f44b0c --- /dev/null +++ b/generators/generators/app/templates/core/src/main/java/package-info.java @@ -0,0 +1,8 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for +// license information. + +/** + * This package contains the classes for the core-bot sample. + */ +package <%= packageName %>; diff --git a/generators/generators/app/templates/core/src/test/java/ApplicationTest.java b/generators/generators/app/templates/core/src/test/java/ApplicationTest.java new file mode 100644 index 000000000..9b84031d4 --- /dev/null +++ b/generators/generators/app/templates/core/src/test/java/ApplicationTest.java @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package <%= packageName %>; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class ApplicationTest { + + @Test + public void contextLoads() { + } + +} diff --git a/generators/generators/app/templates/echo/project/README.md b/generators/generators/app/templates/echo/project/README.md new file mode 100644 index 000000000..0d514610a --- /dev/null +++ b/generators/generators/app/templates/echo/project/README.md @@ -0,0 +1,85 @@ +# <%= botName %> + +This bot has been created using [Bot Framework](https://dev.botframework.com), it shows how to create a simple bot that accepts input from the user and echoes it back. + +This sample is a Spring Boot app and uses the Azure CLI and azure-webapp Maven plugin to deploy to Azure. + +## Prerequisites + +- Java 1.8+ +- Install [Maven](https://maven.apache.org/) +- An account on [Azure](https://azure.microsoft.com) if you want to deploy to Azure. + +## To try this sample locally +- From the root of this project folder: + - Build the sample using `mvn package` + - Run it by using `java -jar .\target\<%= artifact %>-1.0.0.jar` + +- Test 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.3.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 + +As described on [Deploy your bot](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-deploy-az-cli), you will perform the first 4 steps to setup the Azure app, then deploy the code using the azure-webapp Maven plugin. + +### 1. Login to Azure +From a command (or PowerShell) prompt in the root of the bot folder, execute: +`az login` + +### 2. Set the subscription +`az account set --subscription ""` + +If you aren't sure which subscription to use for deploying the bot, you can view the list of subscriptions for your account by using `az account list` command. + +### 3. Create an App registration +`az ad app create --display-name "" --password "" --available-to-other-tenants` + +Replace `` and `` with your own values. + +`` is the unique name of your bot. +`` is a minimum 16 character password for your bot. + +Record the `appid` from the returned JSON + +### 4. Create the Azure resources +Replace the values for ``, ``, ``, and `` in the following commands: + +#### To a new Resource Group +`az deployment sub create --name "echoBotDeploy" --location "westus" --template-file ".\deploymentTemplates\template-with-new-rg.json" --parameters appId="" appSecret="" botId="" botSku=S1 newAppServicePlanName="echoBotPlan" newWebAppName="echoBot" groupLocation="westus" newAppServicePlanLocation="westus"` + +#### To an existing Resource Group +`az deployment group create --resource-group "" --template-file ".\deploymentTemplates\template-with-preexisting-rg.json" --parameters appId="" appSecret="" botId="" newWebAppName="echoBot" newAppServicePlanName="echoBotPlan" appServicePlanLocation="westus" --name "echoBot"` + +### 5. Update app id and password +In src/main/resources/application.properties update +- `MicrosoftAppPassword` with the botsecret value +- `MicrosoftAppId` with the appid from the first step + +### 6. Deploy the code +- Execute `mvn clean package` +- Execute `mvn azure-webapp:deploy -Dgroupname="" -Dbotname=""` + +If the deployment is successful, you will be able to test it via "Test in Web Chat" from the Azure Portal using the "Bot Channel Registration" for the bot. + +After the bot is deployed, you only need to execute #6 if you make changes to the bot. + + +## Further reading + +- [Maven Plugin for Azure App Service](https://docs.microsoft.com/en-us/java/api/overview/azure/maven/azure-webapp-maven-plugin/readme?view=azure-java-stable) +- [Spring Boot](https://spring.io/projects/spring-boot) +- [Azure for Java cloud developers](https://docs.microsoft.com/en-us/azure/java/?view=azure-java-stable) +- [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) +- [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) diff --git a/generators/generators/app/templates/echo/project/deploymentTemplates/template-with-new-rg.json b/generators/generators/app/templates/echo/project/deploymentTemplates/template-with-new-rg.json new file mode 100644 index 000000000..196cfb933 --- /dev/null +++ b/generators/generators/app/templates/echo/project/deploymentTemplates/template-with-new-rg.json @@ -0,0 +1,292 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "groupLocation": { + "defaultValue": "", + "type": "string", + "metadata": { + "description": "Specifies the location of the Resource Group." + } + }, + "groupName": { + "type": "string", + "metadata": { + "description": "Specifies the name of the Resource Group." + } + }, + "appId": { + "type": "string", + "metadata": { + "description": "Active Directory App 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." + } + }, + "botId": { + "type": "string", + "metadata": { + "description": "The globally unique and immutable bot ID. Also used to configure the displayName of the bot, which is mutable." + } + }, + "botSku": { + "defaultValue": "S1", + "type": "string", + "metadata": { + "description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1." + } + }, + "newAppServicePlanName": { + "defaultValue": "", + "type": "string", + "metadata": { + "description": "The name of the App Service Plan." + } + }, + "newAppServicePlanSku": { + "type": "object", + "defaultValue": { + "name": "P1v2", + "tier": "PremiumV2", + "size": "P1v2", + "family": "Pv2", + "capacity": 1 + }, + "metadata": { + "description": "The SKU of the App Service Plan. Defaults to Standard values." + } + }, + "newAppServicePlanLocation": { + "defaultValue": "", + "type": "string", + "metadata": { + "description": "The location of the App Service Plan. Defaults to \"westus\"." + } + }, + "newWebAppName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The globally unique name of the Web App. Defaults to the value passed in for \"botId\"." + } + } + }, + "variables": { + "appServicePlanName": "[parameters('newAppServicePlanName')]", + "resourcesLocation": "[parameters('newAppServicePlanLocation')]", + "webAppName": "[if(empty(parameters('newWebAppName')), parameters('botId'), parameters('newWebAppName'))]", + "siteHost": "[concat(variables('webAppName'), '.azurewebsites.net')]", + "botEndpoint": "[concat('https://', variables('siteHost'), '/api/messages')]", + "publishingUsername": "[concat('$', parameters('newWebAppName'))]", + "resourceGroupId": "[concat(subscription().id, '/resourceGroups/', parameters('groupName'))]" + }, + "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/2015-01-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": {}, + "variables": {}, + "resources": [ + { + "comments": "Create a new Linux App Service Plan if no existing App Service Plan name was passed in.", + "type": "Microsoft.Web/serverfarms", + "name": "[variables('appServicePlanName')]", + "apiVersion": "2018-02-01", + "location": "[variables('resourcesLocation')]", + "sku": "[parameters('newAppServicePlanSku')]", + "kind": "linux", + "properties": { + "perSiteScaling": false, + "maximumElasticWorkerCount": 1, + "isSpot": false, + "reserved": true, + "isXenon": false, + "hyperV": false, + "targetWorkerCount": 0, + "targetWorkerSizeId": 0 + } + }, + { + "comments": "Create a Web App using a Linux App Service Plan", + "type": "Microsoft.Web/sites", + "apiVersion": "2018-11-01", + "location": "[variables('resourcesLocation')]", + "kind": "app,linux", + "dependsOn": [ + "[concat(variables('resourceGroupId'), '/providers/Microsoft.Web/serverfarms/', variables('appServicePlanName'))]" + ], + "name": "[variables('webAppName')]", + "properties": { + "name": "[variables('webAppName')]", + "hostNameSslStates": [ + { + "name": "[concat(parameters('newWebAppName'), '.azurewebsites.net')]", + "sslState": "Disabled", + "hostType": "Standard" + }, + { + "name": "[concat(parameters('newWebAppName'), '.scm.azurewebsites.net')]", + "sslState": "Disabled", + "hostType": "Repository" + } + ], + "serverFarmId": "[variables('appServicePlanName')]", + "reserved": true, + "isXenon": false, + "hyperV": false, + "scmSiteAlsoStopped": false, + "clientAffinityEnabled": true, + "clientCertEnabled": false, + "hostNamesDisabled": false, + "containerSize": 0, + "dailyMemoryTimeQuota": 0, + "httpsOnly": false, + "redundancyMode": "None", + "siteConfig": { + "appSettings": [ + { + "name": "JAVA_OPTS", + "value": "-Dserver.port=80" + }, + { + "name": "MicrosoftAppId", + "value": "[parameters('appId')]" + }, + { + "name": "MicrosoftAppPassword", + "value": "[parameters('appSecret')]" + } + ], + "cors": { + "allowedOrigins": [ + "https://botservice.hosting.portal.azure.net", + "https://hosting.onecloud.azure-test.net/" + ] + } + } + } + }, + { + "type": "Microsoft.Web/sites/config", + "apiVersion": "2018-11-01", + "name": "[concat(variables('webAppName'), '/web')]", + "location": "[variables('resourcesLocation')]", + "dependsOn": [ + "[concat(variables('resourceGroupId'), '/providers/Microsoft.Web/sites/', variables('webAppName'))]" + ], + "properties": { + "numberOfWorkers": 1, + "defaultDocuments": [ + "Default.htm", + "Default.html", + "Default.asp", + "index.htm", + "index.html", + "iisstart.htm", + "default.aspx", + "index.php", + "hostingstart.html" + ], + "netFrameworkVersion": "v4.0", + "linuxFxVersion": "JAVA|8-jre8", + "requestTracingEnabled": false, + "remoteDebuggingEnabled": false, + "httpLoggingEnabled": false, + "logsDirectorySizeLimit": 35, + "detailedErrorLoggingEnabled": false, + "publishingUsername": "[variables('publishingUsername')]", + "scmType": "None", + "use32BitWorkerProcess": true, + "webSocketsEnabled": false, + "alwaysOn": true, + "managedPipelineMode": "Integrated", + "virtualApplications": [ + { + "virtualPath": "/", + "physicalPath": "site\\wwwroot", + "preloadEnabled": true + } + ], + "loadBalancing": "LeastRequests", + "experiments": { + "rampUpRules": [] + }, + "autoHealEnabled": false, + "localMySqlEnabled": false, + "ipSecurityRestrictions": [ + { + "ipAddress": "Any", + "action": "Allow", + "priority": 1, + "name": "Allow all", + "description": "Allow all access" + } + ], + "scmIpSecurityRestrictions": [ + { + "ipAddress": "Any", + "action": "Allow", + "priority": 1, + "name": "Allow all", + "description": "Allow all access" + } + ], + "scmIpSecurityRestrictionsUseMain": false, + "http20Enabled": false, + "minTlsVersion": "1.2", + "ftpsState": "AllAllowed", + "reservedInstanceCount": 0 + } + }, + { + "apiVersion": "2021-03-01", + "type": "Microsoft.BotService/botServices", + "name": "[parameters('botId')]", + "location": "global", + "kind": "azurebot", + "sku": { + "name": "[parameters('botSku')]" + }, + "properties": { + "name": "[parameters('botId')]", + "displayName": "[parameters('botId')]", + "iconUrl": "https://docs.botframework.com/static/devportal/client/images/bot-framework-default.png", + "endpoint": "[variables('botEndpoint')]", + "msaAppId": "[parameters('appId')]", + "luisAppIds": [], + "schemaTransformationVersion": "1.3", + "isCmekEnabled": false, + "isIsolated": false + }, + "dependsOn": [ + "[concat(variables('resourceGroupId'), '/providers/Microsoft.Web/sites/', variables('webAppName'))]" + ] + } + ], + "outputs": {} + } + } + } + ] +} diff --git a/generators/generators/app/templates/echo/project/deploymentTemplates/template-with-preexisting-rg.json b/generators/generators/app/templates/echo/project/deploymentTemplates/template-with-preexisting-rg.json new file mode 100644 index 000000000..d6feb0a0f --- /dev/null +++ b/generators/generators/app/templates/echo/project/deploymentTemplates/template-with-preexisting-rg.json @@ -0,0 +1,260 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "appId": { + "type": "string", + "metadata": { + "description": "Active Directory App 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. Defaults to \"\"." + } + }, + "botId": { + "type": "string", + "metadata": { + "description": "The globally unique and immutable bot ID. Also used to configure the displayName of the bot, which is mutable." + } + }, + "botSku": { + "defaultValue": "S1", + "type": "string", + "metadata": { + "description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1." + } + }, + "newAppServicePlanName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the new App Service Plan." + } + }, + "newAppServicePlanSku": { + "type": "object", + "defaultValue": { + "name": "P1v2", + "tier": "PremiumV2", + "size": "P1v2", + "family": "Pv2", + "capacity": 1 + }, + "metadata": { + "description": "The SKU of the App Service Plan. Defaults to Standard values." + } + }, + "appServicePlanLocation": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The location of the App Service Plan." + } + }, + "existingAppServicePlan": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Name of the existing App Service Plan used to create the Web App for the bot." + } + }, + "newWebAppName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The globally unique name of the Web App. Defaults to the value passed in for \"botId\"." + } + } + }, + "variables": { + "defaultAppServicePlanName": "[if(empty(parameters('existingAppServicePlan')), 'createNewAppServicePlan', parameters('existingAppServicePlan'))]", + "useExistingAppServicePlan": "[not(equals(variables('defaultAppServicePlanName'), 'createNewAppServicePlan'))]", + "servicePlanName": "[if(variables('useExistingAppServicePlan'), parameters('existingAppServicePlan'), parameters('newAppServicePlanName'))]", + "publishingUsername": "[concat('$', parameters('newWebAppName'))]", + "resourcesLocation": "[parameters('appServicePlanLocation')]", + "webAppName": "[if(empty(parameters('newWebAppName')), parameters('botId'), parameters('newWebAppName'))]", + "siteHost": "[concat(variables('webAppName'), '.azurewebsites.net')]", + "botEndpoint": "[concat('https://', variables('siteHost'), '/api/messages')]" + }, + "resources": [ + { + "comments": "Create a new Linux App Service Plan if no existing App Service Plan name was passed in.", + "type": "Microsoft.Web/serverfarms", + "condition": "[not(variables('useExistingAppServicePlan'))]", + "name": "[variables('servicePlanName')]", + "apiVersion": "2018-02-01", + "location": "[variables('resourcesLocation')]", + "sku": "[parameters('newAppServicePlanSku')]", + "kind": "linux", + "properties": { + "perSiteScaling": false, + "maximumElasticWorkerCount": 1, + "isSpot": false, + "reserved": true, + "isXenon": false, + "hyperV": false, + "targetWorkerCount": 0, + "targetWorkerSizeId": 0 + } + }, + { + "comments": "Create a Web App using a Linux App Service Plan", + "type": "Microsoft.Web/sites", + "apiVersion": "2018-11-01", + "location": "[variables('resourcesLocation')]", + "kind": "app,linux", + "dependsOn": [ + "[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]" + ], + "name": "[variables('webAppName')]", + "properties": { + "name": "[variables('webAppName')]", + "hostNameSslStates": [ + { + "name": "[concat(parameters('newWebAppName'), '.azurewebsites.net')]", + "sslState": "Disabled", + "hostType": "Standard" + }, + { + "name": "[concat(parameters('newWebAppName'), '.scm.azurewebsites.net')]", + "sslState": "Disabled", + "hostType": "Repository" + } + ], + "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]", + "reserved": true, + "isXenon": false, + "hyperV": false, + "scmSiteAlsoStopped": false, + "clientAffinityEnabled": true, + "clientCertEnabled": false, + "hostNamesDisabled": false, + "containerSize": 0, + "dailyMemoryTimeQuota": 0, + "httpsOnly": false, + "redundancyMode": "None", + "siteConfig": { + "appSettings": [ + { + "name": "JAVA_OPTS", + "value": "-Dserver.port=80" + }, + { + "name": "MicrosoftAppId", + "value": "[parameters('appId')]" + }, + { + "name": "MicrosoftAppPassword", + "value": "[parameters('appSecret')]" + } + ], + "cors": { + "allowedOrigins": [ + "https://botservice.hosting.portal.azure.net", + "https://hosting.onecloud.azure-test.net/" + ] + } + } + } + }, + { + "type": "Microsoft.Web/sites/config", + "apiVersion": "2018-11-01", + "name": "[concat(variables('webAppName'), '/web')]", + "location": "[variables('resourcesLocation')]", + "dependsOn": [ + "[resourceId('Microsoft.Web/sites/', variables('webAppName'))]" + ], + "properties": { + "numberOfWorkers": 1, + "defaultDocuments": [ + "Default.htm", + "Default.html", + "Default.asp", + "index.htm", + "index.html", + "iisstart.htm", + "default.aspx", + "index.php", + "hostingstart.html" + ], + "netFrameworkVersion": "v4.0", + "linuxFxVersion": "JAVA|8-jre8", + "requestTracingEnabled": false, + "remoteDebuggingEnabled": false, + "httpLoggingEnabled": false, + "logsDirectorySizeLimit": 35, + "detailedErrorLoggingEnabled": false, + "publishingUsername": "[variables('publishingUsername')]", + "scmType": "None", + "use32BitWorkerProcess": true, + "webSocketsEnabled": false, + "alwaysOn": true, + "managedPipelineMode": "Integrated", + "virtualApplications": [ + { + "virtualPath": "/", + "physicalPath": "site\\wwwroot", + "preloadEnabled": true + } + ], + "loadBalancing": "LeastRequests", + "experiments": { + "rampUpRules": [] + }, + "autoHealEnabled": false, + "localMySqlEnabled": false, + "ipSecurityRestrictions": [ + { + "ipAddress": "Any", + "action": "Allow", + "priority": 1, + "name": "Allow all", + "description": "Allow all access" + } + ], + "scmIpSecurityRestrictions": [ + { + "ipAddress": "Any", + "action": "Allow", + "priority": 1, + "name": "Allow all", + "description": "Allow all access" + } + ], + "scmIpSecurityRestrictionsUseMain": false, + "http20Enabled": false, + "minTlsVersion": "1.2", + "ftpsState": "AllAllowed", + "reservedInstanceCount": 0 + } + }, + { + "apiVersion": "2021-03-01", + "type": "Microsoft.BotService/botServices", + "name": "[parameters('botId')]", + "location": "global", + "kind": "azurebot", + "sku": { + "name": "[parameters('botSku')]" + }, + "properties": { + "name": "[parameters('botId')]", + "displayName": "[parameters('botId')]", + "iconUrl": "https://docs.botframework.com/static/devportal/client/images/bot-framework-default.png", + "endpoint": "[variables('botEndpoint')]", + "msaAppId": "[parameters('appId')]", + "luisAppIds": [], + "schemaTransformationVersion": "1.3", + "isCmekEnabled": false, + "isIsolated": false + }, + "dependsOn": [ + "[resourceId('Microsoft.Web/sites/', variables('webAppName'))]" + ] + } + ] +} diff --git a/generators/generators/app/templates/echo/project/pom.xml b/generators/generators/app/templates/echo/project/pom.xml new file mode 100644 index 000000000..2e9fe1986 --- /dev/null +++ b/generators/generators/app/templates/echo/project/pom.xml @@ -0,0 +1,259 @@ + + + + 4.0.0 + + <%= packageName %> + <%= artifact %> + 1.0.0 + jar + + ${project.groupId}:${project.artifactId} + This package contains a Java Bot Echo. + http://maven.apache.org + + + org.springframework.boot + spring-boot-starter-parent + 2.4.0 + + + + + 1.8 + 1.8 + 1.8 + <%= packageName %>.Application + https://botbuilder.myget.org/F/botbuilder-v4-java-daily/maven/ + + + + + junit + junit + 4.13.1 + test + + + org.springframework.boot + spring-boot-starter-test + 2.4.0 + test + + + org.junit.vintage + junit-vintage-engine + test + + + + org.slf4j + slf4j-api + + + org.apache.logging.log4j + log4j-api + 2.17.1 + + + org.apache.logging.log4j + log4j-core + 2.17.1 + + + org.apache.logging.log4j + log4j-to-slf4j + 2.15.0 + test + + + + com.microsoft.bot + bot-integration-spring + 4.14.1 + compile + + + + + + MyGet + ${repo.url} + + + + + + ossrh + + https://oss.sonatype.org/ + + + + + + + build + + true + + + + + src/main/resources + false + + + + + + maven-compiler-plugin + 3.8.1 + + 1.8 + 1.8 + + + + maven-war-plugin + 3.2.3 + + src/main/webapp + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + <%= packageName %>.Application + + + + + + com.microsoft.azure + azure-webapp-maven-plugin + 1.7.0 + + V2 + ${groupname} + ${botname} + + + JAVA_OPTS + -Dserver.port=80 + + + + linux + jre8 + jre8 + + + + + ${project.basedir}/target + + *.jar + + + + + + + + + org.apache.maven.plugins + maven-site-plugin + 3.7.1 + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 3.0.0 + + + + + + + + publish + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.apache.maven.plugins + maven-jar-plugin + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.8 + true + + true + ossrh + https://oss.sonatype.org/ + true + + + + + org.apache.maven.plugins + maven-gpg-plugin + + + sign-artifacts + verify + + sign + + + + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + + jar + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + 8 + false + + + + attach-javadocs + + jar + + + + + + + + + diff --git a/generators/generators/app/templates/echo/project/src/main/resources/application.properties b/generators/generators/app/templates/echo/project/src/main/resources/application.properties new file mode 100644 index 000000000..d7d0ee864 --- /dev/null +++ b/generators/generators/app/templates/echo/project/src/main/resources/application.properties @@ -0,0 +1,3 @@ +MicrosoftAppId= +MicrosoftAppPassword= +server.port=3978 diff --git a/generators/generators/app/templates/echo/project/src/main/resources/log4j2.json b/generators/generators/app/templates/echo/project/src/main/resources/log4j2.json new file mode 100644 index 000000000..ad838e77f --- /dev/null +++ b/generators/generators/app/templates/echo/project/src/main/resources/log4j2.json @@ -0,0 +1,21 @@ +{ + "configuration": { + "name": "Default", + "appenders": { + "Console": { + "name": "Console-Appender", + "target": "SYSTEM_OUT", + "PatternLayout": { + "pattern": + "[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n" + } + } + }, + "loggers": { + "root": { + "level": "debug", + "appender-ref": { "ref": "Console-Appender", "level": "debug" } + } + } + } +} diff --git a/generators/generators/app/templates/echo/project/src/main/webapp/META-INF/MANIFEST.MF b/generators/generators/app/templates/echo/project/src/main/webapp/META-INF/MANIFEST.MF new file mode 100644 index 000000000..254272e1c --- /dev/null +++ b/generators/generators/app/templates/echo/project/src/main/webapp/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Class-Path: + diff --git a/generators/generators/app/templates/echo/project/src/main/webapp/WEB-INF/web.xml b/generators/generators/app/templates/echo/project/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..383c19004 --- /dev/null +++ b/generators/generators/app/templates/echo/project/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,12 @@ + + + dispatcher + + org.springframework.web.servlet.DispatcherServlet + + + contextConfigLocation + /WEB-INF/spring/dispatcher-config.xml + + 1 + \ No newline at end of file diff --git a/generators/generators/app/templates/echo/project/src/main/webapp/index.html b/generators/generators/app/templates/echo/project/src/main/webapp/index.html new file mode 100644 index 000000000..1b10a9051 --- /dev/null +++ b/generators/generators/app/templates/echo/project/src/main/webapp/index.html @@ -0,0 +1,418 @@ + + + + + + + <%= botName %> + + + + + +
+
+
+
<%= botName %>
+
+
+
+
+
Your bot is ready!
+
You can test your bot in the Bot Framework Emulator
+ by connecting to http://localhost:3978/api/messages.
+ +
Visit Azure + Bot Service to register your bot and add it to
+ various channels. The bot's endpoint URL typically looks + like this:
+
https://your_bots_hostname/api/messages
+
+
+
+
+ +
+ + + diff --git a/generators/generators/app/templates/echo/src/main/java/Application.java b/generators/generators/app/templates/echo/src/main/java/Application.java new file mode 100644 index 000000000..001ead194 --- /dev/null +++ b/generators/generators/app/templates/echo/src/main/java/Application.java @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package <%= packageName %>; + +import com.microsoft.bot.builder.Bot; +import com.microsoft.bot.integration.AdapterWithErrorHandler; +import com.microsoft.bot.integration.BotFrameworkHttpAdapter; +import com.microsoft.bot.integration.Configuration; +import com.microsoft.bot.integration.spring.BotController; +import com.microsoft.bot.integration.spring.BotDependencyConfiguration; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; + +// +// This is the starting point of the Sprint Boot Bot application. +// +@SpringBootApplication + +// Use the default BotController to receive incoming Channel messages. A custom +// controller could be used by eliminating this import and creating a new +// org.springframework.web.bind.annotation.RestController. +// The default controller is created by the Spring Boot container using +// dependency injection. The default route is /api/messages. +@Import({BotController.class}) + +/** + * This class extends the BotDependencyConfiguration which provides the default + * implementations for a Bot application. The Application class should + * override methods in order to provide custom implementations. + */ +public class Application extends BotDependencyConfiguration { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + + /** + * Returns the Bot for this application. + * + *

+ * The @Component annotation could be used on the Bot class instead of this method + * with the @Bean annotation. + *

+ * + * @return The Bot implementation for this application. + */ + @Bean + public Bot getBot() { + return new EchoBot(); + } + + /** + * Returns a custom Adapter that provides error handling. + * + * @param configuration The Configuration object to use. + * @return An error handling BotFrameworkHttpAdapter. + */ + @Override + public BotFrameworkHttpAdapter getBotFrameworkHttpAdaptor(Configuration configuration) { + return new AdapterWithErrorHandler(configuration); + } +} diff --git a/generators/generators/app/templates/echo/src/main/java/EchoBot.java b/generators/generators/app/templates/echo/src/main/java/EchoBot.java new file mode 100644 index 000000000..9a474ab44 --- /dev/null +++ b/generators/generators/app/templates/echo/src/main/java/EchoBot.java @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package <%= packageName %>; + +import com.codepoetics.protonpack.collectors.CompletableFutures; +import com.microsoft.bot.builder.ActivityHandler; +import com.microsoft.bot.builder.MessageFactory; +import com.microsoft.bot.builder.TurnContext; +import com.microsoft.bot.schema.ChannelAccount; +import org.apache.commons.lang3.StringUtils; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +/** + * This class implements the functionality of the Bot. + * + *

+ * This is where application specific logic for interacting with the users would be added. For this + * sample, the {@link #onMessageActivity(TurnContext)} echos the text back to the user. The {@link + * #onMembersAdded(List, TurnContext)} will send a greeting to new conversation participants. + *

+ */ +public class EchoBot extends ActivityHandler { + + @Override + protected CompletableFuture onMessageActivity(TurnContext turnContext) { + return turnContext.sendActivity( + MessageFactory.text("Echo: " + turnContext.getActivity().getText()) + ).thenApply(sendResult -> null); + } + + @Override + protected CompletableFuture onMembersAdded( + List membersAdded, + TurnContext turnContext + ) { + return membersAdded.stream() + .filter( + member -> !StringUtils + .equals(member.getId(), turnContext.getActivity().getRecipient().getId()) + ).map(channel -> turnContext.sendActivity(MessageFactory.text("Hello and welcome!"))) + .collect(CompletableFutures.toFutureList()).thenApply(resourceResponses -> null); + } +} diff --git a/generators/generators/app/templates/echo/src/test/java/ApplicationTests.java b/generators/generators/app/templates/echo/src/test/java/ApplicationTests.java new file mode 100644 index 000000000..37084390c --- /dev/null +++ b/generators/generators/app/templates/echo/src/test/java/ApplicationTests.java @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package <%= packageName %>; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class ApplicationTests { + + @Test + public void contextLoads() { + } + +} diff --git a/generators/generators/app/templates/empty/project/README.md b/generators/generators/app/templates/empty/project/README.md new file mode 100644 index 000000000..0d514610a --- /dev/null +++ b/generators/generators/app/templates/empty/project/README.md @@ -0,0 +1,85 @@ +# <%= botName %> + +This bot has been created using [Bot Framework](https://dev.botframework.com), it shows how to create a simple bot that accepts input from the user and echoes it back. + +This sample is a Spring Boot app and uses the Azure CLI and azure-webapp Maven plugin to deploy to Azure. + +## Prerequisites + +- Java 1.8+ +- Install [Maven](https://maven.apache.org/) +- An account on [Azure](https://azure.microsoft.com) if you want to deploy to Azure. + +## To try this sample locally +- From the root of this project folder: + - Build the sample using `mvn package` + - Run it by using `java -jar .\target\<%= artifact %>-1.0.0.jar` + +- Test 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.3.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 + +As described on [Deploy your bot](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-deploy-az-cli), you will perform the first 4 steps to setup the Azure app, then deploy the code using the azure-webapp Maven plugin. + +### 1. Login to Azure +From a command (or PowerShell) prompt in the root of the bot folder, execute: +`az login` + +### 2. Set the subscription +`az account set --subscription ""` + +If you aren't sure which subscription to use for deploying the bot, you can view the list of subscriptions for your account by using `az account list` command. + +### 3. Create an App registration +`az ad app create --display-name "" --password "" --available-to-other-tenants` + +Replace `` and `` with your own values. + +`` is the unique name of your bot. +`` is a minimum 16 character password for your bot. + +Record the `appid` from the returned JSON + +### 4. Create the Azure resources +Replace the values for ``, ``, ``, and `` in the following commands: + +#### To a new Resource Group +`az deployment sub create --name "echoBotDeploy" --location "westus" --template-file ".\deploymentTemplates\template-with-new-rg.json" --parameters appId="" appSecret="" botId="" botSku=S1 newAppServicePlanName="echoBotPlan" newWebAppName="echoBot" groupLocation="westus" newAppServicePlanLocation="westus"` + +#### To an existing Resource Group +`az deployment group create --resource-group "" --template-file ".\deploymentTemplates\template-with-preexisting-rg.json" --parameters appId="" appSecret="" botId="" newWebAppName="echoBot" newAppServicePlanName="echoBotPlan" appServicePlanLocation="westus" --name "echoBot"` + +### 5. Update app id and password +In src/main/resources/application.properties update +- `MicrosoftAppPassword` with the botsecret value +- `MicrosoftAppId` with the appid from the first step + +### 6. Deploy the code +- Execute `mvn clean package` +- Execute `mvn azure-webapp:deploy -Dgroupname="" -Dbotname=""` + +If the deployment is successful, you will be able to test it via "Test in Web Chat" from the Azure Portal using the "Bot Channel Registration" for the bot. + +After the bot is deployed, you only need to execute #6 if you make changes to the bot. + + +## Further reading + +- [Maven Plugin for Azure App Service](https://docs.microsoft.com/en-us/java/api/overview/azure/maven/azure-webapp-maven-plugin/readme?view=azure-java-stable) +- [Spring Boot](https://spring.io/projects/spring-boot) +- [Azure for Java cloud developers](https://docs.microsoft.com/en-us/azure/java/?view=azure-java-stable) +- [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) +- [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) diff --git a/generators/generators/app/templates/empty/project/deploymentTemplates/template-with-new-rg.json b/generators/generators/app/templates/empty/project/deploymentTemplates/template-with-new-rg.json new file mode 100644 index 000000000..196cfb933 --- /dev/null +++ b/generators/generators/app/templates/empty/project/deploymentTemplates/template-with-new-rg.json @@ -0,0 +1,292 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "groupLocation": { + "defaultValue": "", + "type": "string", + "metadata": { + "description": "Specifies the location of the Resource Group." + } + }, + "groupName": { + "type": "string", + "metadata": { + "description": "Specifies the name of the Resource Group." + } + }, + "appId": { + "type": "string", + "metadata": { + "description": "Active Directory App 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." + } + }, + "botId": { + "type": "string", + "metadata": { + "description": "The globally unique and immutable bot ID. Also used to configure the displayName of the bot, which is mutable." + } + }, + "botSku": { + "defaultValue": "S1", + "type": "string", + "metadata": { + "description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1." + } + }, + "newAppServicePlanName": { + "defaultValue": "", + "type": "string", + "metadata": { + "description": "The name of the App Service Plan." + } + }, + "newAppServicePlanSku": { + "type": "object", + "defaultValue": { + "name": "P1v2", + "tier": "PremiumV2", + "size": "P1v2", + "family": "Pv2", + "capacity": 1 + }, + "metadata": { + "description": "The SKU of the App Service Plan. Defaults to Standard values." + } + }, + "newAppServicePlanLocation": { + "defaultValue": "", + "type": "string", + "metadata": { + "description": "The location of the App Service Plan. Defaults to \"westus\"." + } + }, + "newWebAppName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The globally unique name of the Web App. Defaults to the value passed in for \"botId\"." + } + } + }, + "variables": { + "appServicePlanName": "[parameters('newAppServicePlanName')]", + "resourcesLocation": "[parameters('newAppServicePlanLocation')]", + "webAppName": "[if(empty(parameters('newWebAppName')), parameters('botId'), parameters('newWebAppName'))]", + "siteHost": "[concat(variables('webAppName'), '.azurewebsites.net')]", + "botEndpoint": "[concat('https://', variables('siteHost'), '/api/messages')]", + "publishingUsername": "[concat('$', parameters('newWebAppName'))]", + "resourceGroupId": "[concat(subscription().id, '/resourceGroups/', parameters('groupName'))]" + }, + "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/2015-01-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": {}, + "variables": {}, + "resources": [ + { + "comments": "Create a new Linux App Service Plan if no existing App Service Plan name was passed in.", + "type": "Microsoft.Web/serverfarms", + "name": "[variables('appServicePlanName')]", + "apiVersion": "2018-02-01", + "location": "[variables('resourcesLocation')]", + "sku": "[parameters('newAppServicePlanSku')]", + "kind": "linux", + "properties": { + "perSiteScaling": false, + "maximumElasticWorkerCount": 1, + "isSpot": false, + "reserved": true, + "isXenon": false, + "hyperV": false, + "targetWorkerCount": 0, + "targetWorkerSizeId": 0 + } + }, + { + "comments": "Create a Web App using a Linux App Service Plan", + "type": "Microsoft.Web/sites", + "apiVersion": "2018-11-01", + "location": "[variables('resourcesLocation')]", + "kind": "app,linux", + "dependsOn": [ + "[concat(variables('resourceGroupId'), '/providers/Microsoft.Web/serverfarms/', variables('appServicePlanName'))]" + ], + "name": "[variables('webAppName')]", + "properties": { + "name": "[variables('webAppName')]", + "hostNameSslStates": [ + { + "name": "[concat(parameters('newWebAppName'), '.azurewebsites.net')]", + "sslState": "Disabled", + "hostType": "Standard" + }, + { + "name": "[concat(parameters('newWebAppName'), '.scm.azurewebsites.net')]", + "sslState": "Disabled", + "hostType": "Repository" + } + ], + "serverFarmId": "[variables('appServicePlanName')]", + "reserved": true, + "isXenon": false, + "hyperV": false, + "scmSiteAlsoStopped": false, + "clientAffinityEnabled": true, + "clientCertEnabled": false, + "hostNamesDisabled": false, + "containerSize": 0, + "dailyMemoryTimeQuota": 0, + "httpsOnly": false, + "redundancyMode": "None", + "siteConfig": { + "appSettings": [ + { + "name": "JAVA_OPTS", + "value": "-Dserver.port=80" + }, + { + "name": "MicrosoftAppId", + "value": "[parameters('appId')]" + }, + { + "name": "MicrosoftAppPassword", + "value": "[parameters('appSecret')]" + } + ], + "cors": { + "allowedOrigins": [ + "https://botservice.hosting.portal.azure.net", + "https://hosting.onecloud.azure-test.net/" + ] + } + } + } + }, + { + "type": "Microsoft.Web/sites/config", + "apiVersion": "2018-11-01", + "name": "[concat(variables('webAppName'), '/web')]", + "location": "[variables('resourcesLocation')]", + "dependsOn": [ + "[concat(variables('resourceGroupId'), '/providers/Microsoft.Web/sites/', variables('webAppName'))]" + ], + "properties": { + "numberOfWorkers": 1, + "defaultDocuments": [ + "Default.htm", + "Default.html", + "Default.asp", + "index.htm", + "index.html", + "iisstart.htm", + "default.aspx", + "index.php", + "hostingstart.html" + ], + "netFrameworkVersion": "v4.0", + "linuxFxVersion": "JAVA|8-jre8", + "requestTracingEnabled": false, + "remoteDebuggingEnabled": false, + "httpLoggingEnabled": false, + "logsDirectorySizeLimit": 35, + "detailedErrorLoggingEnabled": false, + "publishingUsername": "[variables('publishingUsername')]", + "scmType": "None", + "use32BitWorkerProcess": true, + "webSocketsEnabled": false, + "alwaysOn": true, + "managedPipelineMode": "Integrated", + "virtualApplications": [ + { + "virtualPath": "/", + "physicalPath": "site\\wwwroot", + "preloadEnabled": true + } + ], + "loadBalancing": "LeastRequests", + "experiments": { + "rampUpRules": [] + }, + "autoHealEnabled": false, + "localMySqlEnabled": false, + "ipSecurityRestrictions": [ + { + "ipAddress": "Any", + "action": "Allow", + "priority": 1, + "name": "Allow all", + "description": "Allow all access" + } + ], + "scmIpSecurityRestrictions": [ + { + "ipAddress": "Any", + "action": "Allow", + "priority": 1, + "name": "Allow all", + "description": "Allow all access" + } + ], + "scmIpSecurityRestrictionsUseMain": false, + "http20Enabled": false, + "minTlsVersion": "1.2", + "ftpsState": "AllAllowed", + "reservedInstanceCount": 0 + } + }, + { + "apiVersion": "2021-03-01", + "type": "Microsoft.BotService/botServices", + "name": "[parameters('botId')]", + "location": "global", + "kind": "azurebot", + "sku": { + "name": "[parameters('botSku')]" + }, + "properties": { + "name": "[parameters('botId')]", + "displayName": "[parameters('botId')]", + "iconUrl": "https://docs.botframework.com/static/devportal/client/images/bot-framework-default.png", + "endpoint": "[variables('botEndpoint')]", + "msaAppId": "[parameters('appId')]", + "luisAppIds": [], + "schemaTransformationVersion": "1.3", + "isCmekEnabled": false, + "isIsolated": false + }, + "dependsOn": [ + "[concat(variables('resourceGroupId'), '/providers/Microsoft.Web/sites/', variables('webAppName'))]" + ] + } + ], + "outputs": {} + } + } + } + ] +} diff --git a/generators/generators/app/templates/empty/project/deploymentTemplates/template-with-preexisting-rg.json b/generators/generators/app/templates/empty/project/deploymentTemplates/template-with-preexisting-rg.json new file mode 100644 index 000000000..d6feb0a0f --- /dev/null +++ b/generators/generators/app/templates/empty/project/deploymentTemplates/template-with-preexisting-rg.json @@ -0,0 +1,260 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "appId": { + "type": "string", + "metadata": { + "description": "Active Directory App 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. Defaults to \"\"." + } + }, + "botId": { + "type": "string", + "metadata": { + "description": "The globally unique and immutable bot ID. Also used to configure the displayName of the bot, which is mutable." + } + }, + "botSku": { + "defaultValue": "S1", + "type": "string", + "metadata": { + "description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1." + } + }, + "newAppServicePlanName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the new App Service Plan." + } + }, + "newAppServicePlanSku": { + "type": "object", + "defaultValue": { + "name": "P1v2", + "tier": "PremiumV2", + "size": "P1v2", + "family": "Pv2", + "capacity": 1 + }, + "metadata": { + "description": "The SKU of the App Service Plan. Defaults to Standard values." + } + }, + "appServicePlanLocation": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The location of the App Service Plan." + } + }, + "existingAppServicePlan": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Name of the existing App Service Plan used to create the Web App for the bot." + } + }, + "newWebAppName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The globally unique name of the Web App. Defaults to the value passed in for \"botId\"." + } + } + }, + "variables": { + "defaultAppServicePlanName": "[if(empty(parameters('existingAppServicePlan')), 'createNewAppServicePlan', parameters('existingAppServicePlan'))]", + "useExistingAppServicePlan": "[not(equals(variables('defaultAppServicePlanName'), 'createNewAppServicePlan'))]", + "servicePlanName": "[if(variables('useExistingAppServicePlan'), parameters('existingAppServicePlan'), parameters('newAppServicePlanName'))]", + "publishingUsername": "[concat('$', parameters('newWebAppName'))]", + "resourcesLocation": "[parameters('appServicePlanLocation')]", + "webAppName": "[if(empty(parameters('newWebAppName')), parameters('botId'), parameters('newWebAppName'))]", + "siteHost": "[concat(variables('webAppName'), '.azurewebsites.net')]", + "botEndpoint": "[concat('https://', variables('siteHost'), '/api/messages')]" + }, + "resources": [ + { + "comments": "Create a new Linux App Service Plan if no existing App Service Plan name was passed in.", + "type": "Microsoft.Web/serverfarms", + "condition": "[not(variables('useExistingAppServicePlan'))]", + "name": "[variables('servicePlanName')]", + "apiVersion": "2018-02-01", + "location": "[variables('resourcesLocation')]", + "sku": "[parameters('newAppServicePlanSku')]", + "kind": "linux", + "properties": { + "perSiteScaling": false, + "maximumElasticWorkerCount": 1, + "isSpot": false, + "reserved": true, + "isXenon": false, + "hyperV": false, + "targetWorkerCount": 0, + "targetWorkerSizeId": 0 + } + }, + { + "comments": "Create a Web App using a Linux App Service Plan", + "type": "Microsoft.Web/sites", + "apiVersion": "2018-11-01", + "location": "[variables('resourcesLocation')]", + "kind": "app,linux", + "dependsOn": [ + "[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]" + ], + "name": "[variables('webAppName')]", + "properties": { + "name": "[variables('webAppName')]", + "hostNameSslStates": [ + { + "name": "[concat(parameters('newWebAppName'), '.azurewebsites.net')]", + "sslState": "Disabled", + "hostType": "Standard" + }, + { + "name": "[concat(parameters('newWebAppName'), '.scm.azurewebsites.net')]", + "sslState": "Disabled", + "hostType": "Repository" + } + ], + "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]", + "reserved": true, + "isXenon": false, + "hyperV": false, + "scmSiteAlsoStopped": false, + "clientAffinityEnabled": true, + "clientCertEnabled": false, + "hostNamesDisabled": false, + "containerSize": 0, + "dailyMemoryTimeQuota": 0, + "httpsOnly": false, + "redundancyMode": "None", + "siteConfig": { + "appSettings": [ + { + "name": "JAVA_OPTS", + "value": "-Dserver.port=80" + }, + { + "name": "MicrosoftAppId", + "value": "[parameters('appId')]" + }, + { + "name": "MicrosoftAppPassword", + "value": "[parameters('appSecret')]" + } + ], + "cors": { + "allowedOrigins": [ + "https://botservice.hosting.portal.azure.net", + "https://hosting.onecloud.azure-test.net/" + ] + } + } + } + }, + { + "type": "Microsoft.Web/sites/config", + "apiVersion": "2018-11-01", + "name": "[concat(variables('webAppName'), '/web')]", + "location": "[variables('resourcesLocation')]", + "dependsOn": [ + "[resourceId('Microsoft.Web/sites/', variables('webAppName'))]" + ], + "properties": { + "numberOfWorkers": 1, + "defaultDocuments": [ + "Default.htm", + "Default.html", + "Default.asp", + "index.htm", + "index.html", + "iisstart.htm", + "default.aspx", + "index.php", + "hostingstart.html" + ], + "netFrameworkVersion": "v4.0", + "linuxFxVersion": "JAVA|8-jre8", + "requestTracingEnabled": false, + "remoteDebuggingEnabled": false, + "httpLoggingEnabled": false, + "logsDirectorySizeLimit": 35, + "detailedErrorLoggingEnabled": false, + "publishingUsername": "[variables('publishingUsername')]", + "scmType": "None", + "use32BitWorkerProcess": true, + "webSocketsEnabled": false, + "alwaysOn": true, + "managedPipelineMode": "Integrated", + "virtualApplications": [ + { + "virtualPath": "/", + "physicalPath": "site\\wwwroot", + "preloadEnabled": true + } + ], + "loadBalancing": "LeastRequests", + "experiments": { + "rampUpRules": [] + }, + "autoHealEnabled": false, + "localMySqlEnabled": false, + "ipSecurityRestrictions": [ + { + "ipAddress": "Any", + "action": "Allow", + "priority": 1, + "name": "Allow all", + "description": "Allow all access" + } + ], + "scmIpSecurityRestrictions": [ + { + "ipAddress": "Any", + "action": "Allow", + "priority": 1, + "name": "Allow all", + "description": "Allow all access" + } + ], + "scmIpSecurityRestrictionsUseMain": false, + "http20Enabled": false, + "minTlsVersion": "1.2", + "ftpsState": "AllAllowed", + "reservedInstanceCount": 0 + } + }, + { + "apiVersion": "2021-03-01", + "type": "Microsoft.BotService/botServices", + "name": "[parameters('botId')]", + "location": "global", + "kind": "azurebot", + "sku": { + "name": "[parameters('botSku')]" + }, + "properties": { + "name": "[parameters('botId')]", + "displayName": "[parameters('botId')]", + "iconUrl": "https://docs.botframework.com/static/devportal/client/images/bot-framework-default.png", + "endpoint": "[variables('botEndpoint')]", + "msaAppId": "[parameters('appId')]", + "luisAppIds": [], + "schemaTransformationVersion": "1.3", + "isCmekEnabled": false, + "isIsolated": false + }, + "dependsOn": [ + "[resourceId('Microsoft.Web/sites/', variables('webAppName'))]" + ] + } + ] +} diff --git a/generators/generators/app/templates/empty/project/pom.xml b/generators/generators/app/templates/empty/project/pom.xml new file mode 100644 index 000000000..2e9fe1986 --- /dev/null +++ b/generators/generators/app/templates/empty/project/pom.xml @@ -0,0 +1,259 @@ + + + + 4.0.0 + + <%= packageName %> + <%= artifact %> + 1.0.0 + jar + + ${project.groupId}:${project.artifactId} + This package contains a Java Bot Echo. + http://maven.apache.org + + + org.springframework.boot + spring-boot-starter-parent + 2.4.0 + + + + + 1.8 + 1.8 + 1.8 + <%= packageName %>.Application + https://botbuilder.myget.org/F/botbuilder-v4-java-daily/maven/ + + + + + junit + junit + 4.13.1 + test + + + org.springframework.boot + spring-boot-starter-test + 2.4.0 + test + + + org.junit.vintage + junit-vintage-engine + test + + + + org.slf4j + slf4j-api + + + org.apache.logging.log4j + log4j-api + 2.17.1 + + + org.apache.logging.log4j + log4j-core + 2.17.1 + + + org.apache.logging.log4j + log4j-to-slf4j + 2.15.0 + test + + + + com.microsoft.bot + bot-integration-spring + 4.14.1 + compile + + + + + + MyGet + ${repo.url} + + + + + + ossrh + + https://oss.sonatype.org/ + + + + + + + build + + true + + + + + src/main/resources + false + + + + + + maven-compiler-plugin + 3.8.1 + + 1.8 + 1.8 + + + + maven-war-plugin + 3.2.3 + + src/main/webapp + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + <%= packageName %>.Application + + + + + + com.microsoft.azure + azure-webapp-maven-plugin + 1.7.0 + + V2 + ${groupname} + ${botname} + + + JAVA_OPTS + -Dserver.port=80 + + + + linux + jre8 + jre8 + + + + + ${project.basedir}/target + + *.jar + + + + + + + + + org.apache.maven.plugins + maven-site-plugin + 3.7.1 + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 3.0.0 + + + + + + + + publish + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.apache.maven.plugins + maven-jar-plugin + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.8 + true + + true + ossrh + https://oss.sonatype.org/ + true + + + + + org.apache.maven.plugins + maven-gpg-plugin + + + sign-artifacts + verify + + sign + + + + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + + jar + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + 8 + false + + + + attach-javadocs + + jar + + + + + + + + + diff --git a/generators/generators/app/templates/empty/project/src/main/resources/application.properties b/generators/generators/app/templates/empty/project/src/main/resources/application.properties new file mode 100644 index 000000000..d7d0ee864 --- /dev/null +++ b/generators/generators/app/templates/empty/project/src/main/resources/application.properties @@ -0,0 +1,3 @@ +MicrosoftAppId= +MicrosoftAppPassword= +server.port=3978 diff --git a/generators/generators/app/templates/empty/project/src/main/resources/log4j2.json b/generators/generators/app/templates/empty/project/src/main/resources/log4j2.json new file mode 100644 index 000000000..ad838e77f --- /dev/null +++ b/generators/generators/app/templates/empty/project/src/main/resources/log4j2.json @@ -0,0 +1,21 @@ +{ + "configuration": { + "name": "Default", + "appenders": { + "Console": { + "name": "Console-Appender", + "target": "SYSTEM_OUT", + "PatternLayout": { + "pattern": + "[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n" + } + } + }, + "loggers": { + "root": { + "level": "debug", + "appender-ref": { "ref": "Console-Appender", "level": "debug" } + } + } + } +} diff --git a/generators/generators/app/templates/empty/project/src/main/webapp/META-INF/MANIFEST.MF b/generators/generators/app/templates/empty/project/src/main/webapp/META-INF/MANIFEST.MF new file mode 100644 index 000000000..254272e1c --- /dev/null +++ b/generators/generators/app/templates/empty/project/src/main/webapp/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Class-Path: + diff --git a/generators/generators/app/templates/empty/project/src/main/webapp/WEB-INF/web.xml b/generators/generators/app/templates/empty/project/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..383c19004 --- /dev/null +++ b/generators/generators/app/templates/empty/project/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,12 @@ + + + dispatcher + + org.springframework.web.servlet.DispatcherServlet + + + contextConfigLocation + /WEB-INF/spring/dispatcher-config.xml + + 1 + \ No newline at end of file diff --git a/generators/generators/app/templates/empty/project/src/main/webapp/index.html b/generators/generators/app/templates/empty/project/src/main/webapp/index.html new file mode 100644 index 000000000..1b10a9051 --- /dev/null +++ b/generators/generators/app/templates/empty/project/src/main/webapp/index.html @@ -0,0 +1,418 @@ + + + + + + + <%= botName %> + + + + + +
+
+
+
<%= botName %>
+
+
+
+
+
Your bot is ready!
+
You can test your bot in the Bot Framework Emulator
+ by connecting to http://localhost:3978/api/messages.
+ +
Visit Azure + Bot Service to register your bot and add it to
+ various channels. The bot's endpoint URL typically looks + like this:
+
https://your_bots_hostname/api/messages
+
+
+
+
+ +
+ + + diff --git a/generators/generators/app/templates/empty/src/main/java/Application.java b/generators/generators/app/templates/empty/src/main/java/Application.java new file mode 100644 index 000000000..44992727f --- /dev/null +++ b/generators/generators/app/templates/empty/src/main/java/Application.java @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package <%= packageName %>; + +import com.microsoft.bot.builder.Bot; +import com.microsoft.bot.integration.AdapterWithErrorHandler; +import com.microsoft.bot.integration.BotFrameworkHttpAdapter; +import com.microsoft.bot.integration.Configuration; +import com.microsoft.bot.integration.spring.BotController; +import com.microsoft.bot.integration.spring.BotDependencyConfiguration; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; + +// +// This is the starting point of the Sprint Boot Bot application. +// +@SpringBootApplication + +// Use the default BotController to receive incoming Channel messages. A custom +// controller could be used by eliminating this import and creating a new +// org.springframework.web.bind.annotation.RestController. +// The default controller is created by the Spring Boot container using +// dependency injection. The default route is /api/messages. +@Import({BotController.class}) + +/** + * This class extends the BotDependencyConfiguration which provides the default + * implementations for a Bot application. The Application class should + * override methods in order to provide custom implementations. + */ +public class Application extends BotDependencyConfiguration { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + + /** + * Returns the Bot for this application. + * + *

+ * The @Component annotation could be used on the Bot class instead of this method + * with the @Bean annotation. + *

+ * + * @return The Bot implementation for this application. + */ + @Bean + public Bot getBot() { + return new EmptyBot(); + } + + /** + * Returns a custom Adapter that provides error handling. + * + * @param configuration The Configuration object to use. + * @return An error handling BotFrameworkHttpAdapter. + */ + @Override + public BotFrameworkHttpAdapter getBotFrameworkHttpAdaptor(Configuration configuration) { + return new AdapterWithErrorHandler(configuration); + } +} diff --git a/generators/generators/app/templates/empty/src/main/java/EmptyBot.java b/generators/generators/app/templates/empty/src/main/java/EmptyBot.java new file mode 100644 index 000000000..3f8d6208d --- /dev/null +++ b/generators/generators/app/templates/empty/src/main/java/EmptyBot.java @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package <%= packageName %>; + +import com.codepoetics.protonpack.collectors.CompletableFutures; +import com.microsoft.bot.builder.ActivityHandler; +import com.microsoft.bot.builder.MessageFactory; +import com.microsoft.bot.builder.TurnContext; +import com.microsoft.bot.schema.ChannelAccount; +import org.apache.commons.lang3.StringUtils; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +/** + * This class implements the functionality of the Bot. + * + *

+ * This is where application specific logic for interacting with the users would be added. For this + * sample, the {@link #onMessageActivity(TurnContext)} echos the text back to the user. The {@link + * #onMembersAdded(List, TurnContext)} will send a greeting to new conversation participants. + *

+ */ +public class EmptyBot extends ActivityHandler { + + @Override + protected CompletableFuture onMembersAdded( + List membersAdded, + TurnContext turnContext + ) { + return membersAdded.stream() + .filter( + member -> !StringUtils + .equals(member.getId(), turnContext.getActivity().getRecipient().getId()) + ).map(channel -> turnContext.sendActivity(MessageFactory.text("Hello world!"))) + .collect(CompletableFutures.toFutureList()).thenApply(resourceResponses -> null); + } +} diff --git a/generators/generators/app/templates/empty/src/test/java/ApplicationTests.java b/generators/generators/app/templates/empty/src/test/java/ApplicationTests.java new file mode 100644 index 000000000..37084390c --- /dev/null +++ b/generators/generators/app/templates/empty/src/test/java/ApplicationTests.java @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package <%= packageName %>; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class ApplicationTests { + + @Test + public void contextLoads() { + } + +} diff --git a/generators/package-lock.json b/generators/package-lock.json new file mode 100644 index 000000000..1ac056e7d --- /dev/null +++ b/generators/package-lock.json @@ -0,0 +1,1927 @@ +{ + "name": "generator-botbuilder-java", + "version": "4.14.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "generator-botbuilder-java", + "version": "4.14.0", + "license": "SEE LICENSE IN LICENSE.md", + "dependencies": { + "chalk": "~4.0.0", + "lodash": "~4.17.15", + "mkdirp": "^1.0.4", + "yeoman-generator": "^5.7.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dependencies": { + "@babel/highlight": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@octokit/auth-token": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", + "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", + "dependencies": { + "@octokit/types": "^6.0.3" + } + }, + "node_modules/@octokit/core": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", + "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", + "dependencies": { + "@octokit/auth-token": "^2.4.4", + "@octokit/graphql": "^4.5.8", + "@octokit/request": "^5.6.3", + "@octokit/request-error": "^2.0.5", + "@octokit/types": "^6.0.3", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/endpoint": { + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", + "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", + "dependencies": { + "@octokit/types": "^6.0.3", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/graphql": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", + "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", + "dependencies": { + "@octokit/request": "^5.6.0", + "@octokit/types": "^6.0.3", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "12.11.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", + "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "2.21.3", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz", + "integrity": "sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw==", + "dependencies": { + "@octokit/types": "^6.40.0" + }, + "peerDependencies": { + "@octokit/core": ">=2" + } + }, + "node_modules/@octokit/plugin-request-log": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", + "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "5.16.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz", + "integrity": "sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw==", + "dependencies": { + "@octokit/types": "^6.39.0", + "deprecation": "^2.3.1" + }, + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/@octokit/request": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", + "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", + "dependencies": { + "@octokit/endpoint": "^6.0.1", + "@octokit/request-error": "^2.1.0", + "@octokit/types": "^6.16.1", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.7", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/request-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", + "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", + "dependencies": { + "@octokit/types": "^6.0.3", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "node_modules/@octokit/rest": { + "version": "18.12.0", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz", + "integrity": "sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q==", + "dependencies": { + "@octokit/core": "^3.5.1", + "@octokit/plugin-paginate-rest": "^2.16.8", + "@octokit/plugin-request-log": "^1.0.4", + "@octokit/plugin-rest-endpoint-methods": "^5.12.0" + } + }, + "node_modules/@octokit/types": { + "version": "6.41.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", + "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", + "dependencies": { + "@octokit/openapi-types": "^12.11.0" + } + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", + "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==" + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/before-after-hook": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", + "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/chalk": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", + "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/dargs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz", + "integrity": "sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/github-username": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/github-username/-/github-username-6.0.0.tgz", + "integrity": "sha512-7TTrRjxblSI5l6adk9zd+cV5d6i1OrJSo3Vr9xdGqFLBQo0mz5P9eIfKCDJ7eekVGGFLbce0qbPSnktXV2BjDQ==", + "dependencies": { + "@octokit/rest": "^18.0.6" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, + "node_modules/is-core-module": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", + "dependencies": { + "resolve": "^1.1.6" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "engines": { + "node": ">=8" + } + }, + "node_modules/shelljs": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", + "dependencies": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + }, + "bin": { + "shjs": "bin/shjs" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "node_modules/sort-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-4.2.0.tgz", + "integrity": "sha512-aUYIEU/UviqPgc8mHR6IW1EGxkAXpeRETYcrzg8cLAvUPZcpAlleSXHV2mY7G12GphSH6Gzv+4MMVSSkbdteHg==", + "dependencies": { + "is-plain-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", + "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==" + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/universal-user-agent": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yeoman-generator": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/yeoman-generator/-/yeoman-generator-5.7.0.tgz", + "integrity": "sha512-z9ZwgKoDOd+llPDCwn8Ax2l4In5FMhlslxdeByW4AMxhT+HbTExXKEAahsClHSbwZz1i5OzRwLwRIUdOJBr5Bw==", + "dependencies": { + "chalk": "^4.1.0", + "dargs": "^7.0.0", + "debug": "^4.1.1", + "execa": "^5.1.1", + "github-username": "^6.0.0", + "lodash": "^4.17.11", + "minimist": "^1.2.5", + "read-pkg-up": "^7.0.1", + "run-async": "^2.0.0", + "semver": "^7.2.1", + "shelljs": "^0.8.5", + "sort-keys": "^4.2.0", + "text-table": "^0.2.0" + }, + "engines": { + "node": ">=12.10.0" + }, + "peerDependencies": { + "yeoman-environment": "^3.2.0" + }, + "peerDependenciesMeta": { + "yeoman-environment": { + "optional": true + } + } + }, + "node_modules/yeoman-generator/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + } + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "requires": { + "@babel/highlight": "^7.18.6" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==" + }, + "@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "requires": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@octokit/auth-token": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", + "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", + "requires": { + "@octokit/types": "^6.0.3" + } + }, + "@octokit/core": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", + "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", + "requires": { + "@octokit/auth-token": "^2.4.4", + "@octokit/graphql": "^4.5.8", + "@octokit/request": "^5.6.3", + "@octokit/request-error": "^2.0.5", + "@octokit/types": "^6.0.3", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/endpoint": { + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", + "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", + "requires": { + "@octokit/types": "^6.0.3", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/graphql": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", + "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", + "requires": { + "@octokit/request": "^5.6.0", + "@octokit/types": "^6.0.3", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/openapi-types": { + "version": "12.11.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", + "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" + }, + "@octokit/plugin-paginate-rest": { + "version": "2.21.3", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz", + "integrity": "sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw==", + "requires": { + "@octokit/types": "^6.40.0" + } + }, + "@octokit/plugin-request-log": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", + "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", + "requires": {} + }, + "@octokit/plugin-rest-endpoint-methods": { + "version": "5.16.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz", + "integrity": "sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw==", + "requires": { + "@octokit/types": "^6.39.0", + "deprecation": "^2.3.1" + } + }, + "@octokit/request": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", + "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", + "requires": { + "@octokit/endpoint": "^6.0.1", + "@octokit/request-error": "^2.1.0", + "@octokit/types": "^6.16.1", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.7", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/request-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", + "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", + "requires": { + "@octokit/types": "^6.0.3", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "@octokit/rest": { + "version": "18.12.0", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz", + "integrity": "sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q==", + "requires": { + "@octokit/core": "^3.5.1", + "@octokit/plugin-paginate-rest": "^2.16.8", + "@octokit/plugin-request-log": "^1.0.4", + "@octokit/plugin-rest-endpoint-methods": "^5.12.0" + } + }, + "@octokit/types": { + "version": "6.41.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", + "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", + "requires": { + "@octokit/openapi-types": "^12.11.0" + } + }, + "@types/normalize-package-data": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", + "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==" + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "before-after-hook": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", + "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chalk": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", + "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "dargs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz", + "integrity": "sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==" + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" + }, + "github-username": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/github-username/-/github-username-6.0.0.tgz", + "integrity": "sha512-7TTrRjxblSI5l6adk9zd+cV5d6i1OrJSo3Vr9xdGqFLBQo0mz5P9eIfKCDJ7eekVGGFLbce0qbPSnktXV2BjDQ==", + "requires": { + "@octokit/rest": "^18.0.6" + } + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==" + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, + "is-core-module": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "requires": { + "has": "^1.0.3" + } + }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==" + }, + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + }, + "lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "requires": { + "p-locate": "^4.1.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==" + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "requires": { + "path-key": "^3.0.0" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==" + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + } + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", + "requires": { + "resolve": "^1.1.6" + } + }, + "resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "requires": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==" + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + }, + "shelljs": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", + "requires": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + } + }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "sort-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-4.2.0.tgz", + "integrity": "sha512-aUYIEU/UviqPgc8mHR6IW1EGxkAXpeRETYcrzg8cLAvUPZcpAlleSXHV2mY7G12GphSH6Gzv+4MMVSSkbdteHg==", + "requires": { + "is-plain-obj": "^2.0.0" + } + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", + "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==" + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" + }, + "universal-user-agent": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "yeoman-generator": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/yeoman-generator/-/yeoman-generator-5.7.0.tgz", + "integrity": "sha512-z9ZwgKoDOd+llPDCwn8Ax2l4In5FMhlslxdeByW4AMxhT+HbTExXKEAahsClHSbwZz1i5OzRwLwRIUdOJBr5Bw==", + "requires": { + "chalk": "^4.1.0", + "dargs": "^7.0.0", + "debug": "^4.1.1", + "execa": "^5.1.1", + "github-username": "^6.0.0", + "lodash": "^4.17.11", + "minimist": "^1.2.5", + "read-pkg-up": "^7.0.1", + "run-async": "^2.0.0", + "semver": "^7.2.1", + "shelljs": "^0.8.5", + "sort-keys": "^4.2.0", + "text-table": "^0.2.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } + } + } + } +} diff --git a/generators/package.json b/generators/package.json new file mode 100644 index 000000000..6c582eac3 --- /dev/null +++ b/generators/package.json @@ -0,0 +1,32 @@ +{ + "name": "generator-botbuilder-java", + "version": "4.14.0", + "description": "A yeoman generator for creating Java bots built with Bot Framework v4", + "homepage": "https://github.com/Microsoft/BotBuilder-Java/tree/main/generators", + "author": { + "name": "Microsoft", + "email": "botframework@microsoft.com", + "url": "http://dev.botframework.com" + }, + "license": "SEE LICENSE IN LICENSE.md", + "repository": "https://github.com/Microsoft/BotBuilder-Java.git", + "files": [ + "components", + "generators" + ], + "keywords": [ + "botbuilder", + "bots", + "bot framework", + "yeoman-generator", + "Microsoft AI", + "Microsoft Teams", + "Conversational AI" + ], + "dependencies": { + "chalk": "~4.0.0", + "lodash": "~4.17.15", + "mkdirp": "^1.0.4", + "yeoman-generator": "^5.7.0" + } +} diff --git a/generators/target/npmlist.json b/generators/target/npmlist.json new file mode 100644 index 000000000..433027994 --- /dev/null +++ b/generators/target/npmlist.json @@ -0,0 +1 @@ +{"name":"generator-java","version":"4.9.1","dependencies":{"camelcase":{"version":"6.0.0"},"chalk":{"version":"4.0.0","dependencies":{"ansi-styles":{"version":"4.3.0","dependencies":{"color-convert":{"version":"2.0.1","dependencies":{"color-name":{"version":"1.1.4"}}}}},"supports-color":{"version":"7.2.0","dependencies":{"has-flag":{"version":"4.0.0"}}}}},"lodash":{"version":"4.17.20"},"mkdirp":{"version":"1.0.4"},"yeoman-generator":{"version":"4.10.1","dependencies":{"async":{"version":"2.6.3","dependencies":{"lodash":{"version":"4.17.20"}}},"cli-table":{"version":"0.3.4","dependencies":{"chalk":{"version":"2.4.2","dependencies":{"ansi-styles":{"version":"3.2.1","dependencies":{"color-convert":{"version":"1.9.3","dependencies":{"color-name":{"version":"1.1.3"}}}}},"supports-color":{"version":"5.5.0","dependencies":{"has-flag":{"version":"3.0.0"}}},"escape-string-regexp":{"version":"1.0.5"}}},"string-width":{"version":"4.2.0","dependencies":{"emoji-regex":{"version":"8.0.0"},"is-fullwidth-code-point":{"version":"3.0.0"},"strip-ansi":{"version":"6.0.0","dependencies":{"ansi-regex":{"version":"5.0.0"}}}}}}},"cross-spawn":{"version":"6.0.5","dependencies":{"semver":{"version":"5.7.1"},"nice-try":{"version":"1.0.5"},"path-key":{"version":"2.0.1"},"shebang-command":{"version":"1.2.0","dependencies":{"shebang-regex":{"version":"1.0.0"}}},"which":{"version":"1.3.1","dependencies":{"isexe":{"version":"2.0.0"}}}}},"dargs":{"version":"6.1.0"},"dateformat":{"version":"3.0.3"},"debug":{"version":"4.3.1","dependencies":{"ms":{"version":"2.1.2"}}},"diff":{"version":"4.0.2"},"error":{"version":"7.2.1","dependencies":{"string-template":{"version":"0.2.1"}}},"find-up":{"version":"3.0.0","dependencies":{"locate-path":{"version":"3.0.0","dependencies":{"p-locate":{"version":"3.0.0","dependencies":{"p-limit":{"version":"2.3.0","dependencies":{"p-try":{"version":"2.2.0"}}}}},"path-exists":{"version":"3.0.0"}}}}},"github-username":{"version":"3.0.0","dependencies":{"gh-got":{"version":"5.0.0","dependencies":{"got":{"version":"6.7.1","dependencies":{"create-error-class":{"version":"3.0.2","dependencies":{"capture-stack-trace":{"version":"1.0.1"}}},"duplexer3":{"version":"0.1.4"},"get-stream":{"version":"3.0.0"},"is-redirect":{"version":"1.0.0"},"is-retry-allowed":{"version":"1.2.0"},"is-stream":{"version":"1.1.0"},"lowercase-keys":{"version":"1.0.1"},"safe-buffer":{"version":"5.2.1"},"timed-out":{"version":"4.0.1"},"unzip-response":{"version":"2.0.1"},"url-parse-lax":{"version":"1.0.0","dependencies":{"prepend-http":{"version":"1.0.4"}}}}},"is-plain-obj":{"version":"1.1.0"}}}}},"grouped-queue":{"version":"1.1.0","dependencies":{"lodash":{"version":"4.17.20"}}},"istextorbinary":{"version":"2.6.0","dependencies":{"binaryextensions":{"version":"2.3.0"},"editions":{"version":"2.3.1","dependencies":{"semver":{"version":"6.3.0"},"errlop":{"version":"2.2.0"}}},"textextensions":{"version":"2.6.0"}}},"lodash":{"version":"4.17.20"},"make-dir":{"version":"3.1.0","dependencies":{"semver":{"version":"6.3.0"}}},"mem-fs-editor":{"version":"6.0.0","dependencies":{"commondir":{"version":"1.0.1"},"deep-extend":{"version":"0.6.0"},"ejs":{"version":"2.7.4"},"glob":{"version":"7.1.6","dependencies":{"fs.realpath":{"version":"1.0.0"},"inflight":{"version":"1.0.6","dependencies":{"once":{"version":"1.4.0"},"wrappy":{"version":"1.0.2"}}},"inherits":{"version":"2.0.4"},"minimatch":{"version":"3.0.4","dependencies":{"brace-expansion":{"version":"1.1.11","dependencies":{"balanced-match":{"version":"1.0.0"},"concat-map":{"version":"0.0.1"}}}}},"once":{"version":"1.4.0","dependencies":{"wrappy":{"version":"1.0.2"}}},"path-is-absolute":{"version":"1.0.1"}}},"globby":{"version":"9.2.0","dependencies":{"@types/glob":{"version":"7.1.3","dependencies":{"@types/minimatch":{"version":"3.0.3"},"@types/node":{"version":"14.14.11"}}},"array-union":{"version":"1.0.2","dependencies":{"array-uniq":{"version":"1.0.3"}}},"dir-glob":{"version":"2.2.2","dependencies":{"path-type":{"version":"3.0.0","dependencies":{"pify":{"version":"3.0.0"}}}}},"fast-glob":{"version":"2.2.7","dependencies":{"@mrmlnc/readdir-enhanced":{"version":"2.2.1","dependencies":{"call-me-maybe":{"version":"1.0.1"},"glob-to-regexp":{"version":"0.3.0"}}},"@nodelib/fs.stat":{"version":"1.1.3"},"glob-parent":{"version":"3.1.0","dependencies":{"is-glob":{"version":"3.1.0","dependencies":{"is-extglob":{"version":"2.1.1"}}},"path-dirname":{"version":"1.0.2"}}},"is-glob":{"version":"4.0.1","dependencies":{"is-extglob":{"version":"2.1.1"}}},"merge2":{"version":"1.4.1"},"micromatch":{"version":"3.1.10","dependencies":{"arr-diff":{"version":"4.0.0"},"array-unique":{"version":"0.3.2"},"braces":{"version":"2.3.2","dependencies":{"arr-flatten":{"version":"1.1.0"},"array-unique":{"version":"0.3.2"},"extend-shallow":{"version":"2.0.1","dependencies":{"is-extendable":{"version":"0.1.1"}}},"fill-range":{"version":"4.0.0","dependencies":{"extend-shallow":{"version":"2.0.1","dependencies":{"is-extendable":{"version":"0.1.1"}}},"is-number":{"version":"3.0.0","dependencies":{"kind-of":{"version":"3.2.2","dependencies":{"is-buffer":{"version":"1.1.6"}}}}},"repeat-string":{"version":"1.6.1"},"to-regex-range":{"version":"2.1.1","dependencies":{"is-number":{"version":"3.0.0"},"repeat-string":{"version":"1.6.1"}}}}},"isobject":{"version":"3.0.1"},"repeat-element":{"version":"1.1.3"},"snapdragon":{"version":"0.8.2"},"snapdragon-node":{"version":"2.1.1","dependencies":{"isobject":{"version":"3.0.1"},"define-property":{"version":"1.0.0","dependencies":{"is-descriptor":{"version":"1.0.2","dependencies":{"kind-of":{"version":"6.0.3"},"is-accessor-descriptor":{"version":"1.0.0","dependencies":{"kind-of":{"version":"6.0.3"}}},"is-data-descriptor":{"version":"1.0.0","dependencies":{"kind-of":{"version":"6.0.3"}}}}}}},"snapdragon-util":{"version":"3.0.1","dependencies":{"kind-of":{"version":"3.2.2","dependencies":{"is-buffer":{"version":"1.1.6"}}}}}}},"split-string":{"version":"3.1.0","dependencies":{"extend-shallow":{"version":"3.0.2"}}},"to-regex":{"version":"3.0.2"}}},"define-property":{"version":"2.0.2","dependencies":{"is-descriptor":{"version":"1.0.2","dependencies":{"is-accessor-descriptor":{"version":"1.0.0","dependencies":{"kind-of":{"version":"6.0.3"}}},"is-data-descriptor":{"version":"1.0.0","dependencies":{"kind-of":{"version":"6.0.3"}}},"kind-of":{"version":"6.0.3"}}},"isobject":{"version":"3.0.1"}}},"extend-shallow":{"version":"3.0.2","dependencies":{"assign-symbols":{"version":"1.0.0"},"is-extendable":{"version":"1.0.1","dependencies":{"is-plain-object":{"version":"2.0.4"}}}}},"extglob":{"version":"2.0.4","dependencies":{"array-unique":{"version":"0.3.2"},"expand-brackets":{"version":"2.1.4","dependencies":{"debug":{"version":"2.6.9","dependencies":{"ms":{"version":"2.0.0"}}},"define-property":{"version":"0.2.5","dependencies":{"is-descriptor":{"version":"0.1.6"}}},"extend-shallow":{"version":"2.0.1","dependencies":{"is-extendable":{"version":"0.1.1"}}},"posix-character-classes":{"version":"0.1.1"},"regex-not":{"version":"1.0.2"},"snapdragon":{"version":"0.8.2"},"to-regex":{"version":"3.0.2"}}},"define-property":{"version":"1.0.0","dependencies":{"is-descriptor":{"version":"1.0.2","dependencies":{"is-accessor-descriptor":{"version":"1.0.0","dependencies":{"kind-of":{"version":"6.0.3"}}},"is-data-descriptor":{"version":"1.0.0","dependencies":{"kind-of":{"version":"6.0.3"}}},"kind-of":{"version":"6.0.3"}}}}},"extend-shallow":{"version":"2.0.1","dependencies":{"is-extendable":{"version":"0.1.1"}}},"fragment-cache":{"version":"0.2.1"},"regex-not":{"version":"1.0.2"},"snapdragon":{"version":"0.8.2"},"to-regex":{"version":"3.0.2"}}},"fragment-cache":{"version":"0.2.1","dependencies":{"map-cache":{"version":"0.2.2"}}},"kind-of":{"version":"6.0.3"},"nanomatch":{"version":"1.2.13","dependencies":{"arr-diff":{"version":"4.0.0"},"array-unique":{"version":"0.3.2"},"define-property":{"version":"2.0.2"},"extend-shallow":{"version":"3.0.2"},"fragment-cache":{"version":"0.2.1"},"is-windows":{"version":"1.0.2"},"kind-of":{"version":"6.0.3"},"object.pick":{"version":"1.3.0"},"regex-not":{"version":"1.0.2"},"snapdragon":{"version":"0.8.2"},"to-regex":{"version":"3.0.2"}}},"object.pick":{"version":"1.3.0","dependencies":{"isobject":{"version":"3.0.1"}}},"regex-not":{"version":"1.0.2","dependencies":{"extend-shallow":{"version":"3.0.2"},"safe-regex":{"version":"1.1.0","dependencies":{"ret":{"version":"0.1.15"}}}}},"snapdragon":{"version":"0.8.2","dependencies":{"base":{"version":"0.11.2","dependencies":{"define-property":{"version":"1.0.0","dependencies":{"is-descriptor":{"version":"1.0.2","dependencies":{"is-accessor-descriptor":{"version":"1.0.0","dependencies":{"kind-of":{"version":"6.0.3"}}},"is-data-descriptor":{"version":"1.0.0","dependencies":{"kind-of":{"version":"6.0.3"}}},"kind-of":{"version":"6.0.3"}}}}},"cache-base":{"version":"1.0.1","dependencies":{"collection-visit":{"version":"1.0.0","dependencies":{"map-visit":{"version":"1.0.0","dependencies":{"object-visit":{"version":"1.0.1"}}},"object-visit":{"version":"1.0.1","dependencies":{"isobject":{"version":"3.0.1"}}}}},"component-emitter":{"version":"1.3.0"},"get-value":{"version":"2.0.6"},"has-value":{"version":"1.0.0","dependencies":{"get-value":{"version":"2.0.6"},"has-values":{"version":"1.0.0","dependencies":{"kind-of":{"version":"4.0.0","dependencies":{"is-buffer":{"version":"1.1.6"}}},"is-number":{"version":"3.0.0"}}},"isobject":{"version":"3.0.1"}}},"isobject":{"version":"3.0.1"},"set-value":{"version":"2.0.1","dependencies":{"is-extendable":{"version":"0.1.1"},"is-plain-object":{"version":"2.0.4"},"extend-shallow":{"version":"2.0.1","dependencies":{"is-extendable":{"version":"0.1.1"}}},"split-string":{"version":"3.1.0"}}},"to-object-path":{"version":"0.3.0"},"union-value":{"version":"1.0.1","dependencies":{"arr-union":{"version":"3.1.0"},"get-value":{"version":"2.0.6"},"is-extendable":{"version":"0.1.1"},"set-value":{"version":"2.0.1"}}},"unset-value":{"version":"1.0.0","dependencies":{"isobject":{"version":"3.0.1"},"has-value":{"version":"0.3.1","dependencies":{"get-value":{"version":"2.0.6"},"isobject":{"version":"2.1.0","dependencies":{"isarray":{"version":"1.0.0"}}},"has-values":{"version":"0.1.4"}}}}}}},"class-utils":{"version":"0.3.6","dependencies":{"arr-union":{"version":"3.1.0"},"define-property":{"version":"0.2.5","dependencies":{"is-descriptor":{"version":"0.1.6"}}},"isobject":{"version":"3.0.1"},"static-extend":{"version":"0.1.2","dependencies":{"object-copy":{"version":"0.1.0","dependencies":{"copy-descriptor":{"version":"0.1.1"},"define-property":{"version":"0.2.5","dependencies":{"is-descriptor":{"version":"0.1.6"}}},"kind-of":{"version":"3.2.2","dependencies":{"is-buffer":{"version":"1.1.6"}}}}},"define-property":{"version":"0.2.5","dependencies":{"is-descriptor":{"version":"0.1.6"}}}}}}},"component-emitter":{"version":"1.3.0"},"isobject":{"version":"3.0.1"},"mixin-deep":{"version":"1.3.2","dependencies":{"for-in":{"version":"1.0.2"},"is-extendable":{"version":"1.0.1","dependencies":{"is-plain-object":{"version":"2.0.4"}}}}},"pascalcase":{"version":"0.1.1"}}},"map-cache":{"version":"0.2.2"},"debug":{"version":"2.6.9","dependencies":{"ms":{"version":"2.0.0"}}},"define-property":{"version":"0.2.5","dependencies":{"is-descriptor":{"version":"0.1.6","dependencies":{"is-accessor-descriptor":{"version":"0.1.6","dependencies":{"kind-of":{"version":"3.2.2","dependencies":{"is-buffer":{"version":"1.1.6"}}}}},"is-data-descriptor":{"version":"0.1.4","dependencies":{"kind-of":{"version":"3.2.2","dependencies":{"is-buffer":{"version":"1.1.6"}}}}},"kind-of":{"version":"5.1.0"}}}}},"extend-shallow":{"version":"2.0.1","dependencies":{"is-extendable":{"version":"0.1.1"}}},"source-map":{"version":"0.5.7"},"source-map-resolve":{"version":"0.5.3","dependencies":{"atob":{"version":"2.1.2"},"decode-uri-component":{"version":"0.2.0"},"resolve-url":{"version":"0.2.1"},"source-map-url":{"version":"0.4.0"},"urix":{"version":"0.1.0"}}},"use":{"version":"3.1.1"}}},"to-regex":{"version":"3.0.2","dependencies":{"define-property":{"version":"2.0.2"},"extend-shallow":{"version":"3.0.2"},"regex-not":{"version":"1.0.2"},"safe-regex":{"version":"1.1.0"}}}}}}},"glob":{"version":"7.1.6"},"ignore":{"version":"4.0.6"},"pify":{"version":"4.0.1"},"slash":{"version":"2.0.0"}}},"isbinaryfile":{"version":"4.0.6"},"mkdirp":{"version":"0.5.5","dependencies":{"minimist":{"version":"1.2.5"}}},"multimatch":{"version":"4.0.0","dependencies":{"@types/minimatch":{"version":"3.0.3"},"array-differ":{"version":"3.0.0"},"arrify":{"version":"2.0.1"},"minimatch":{"version":"3.0.4"},"array-union":{"version":"2.1.0"}}},"rimraf":{"version":"2.7.1"},"through2":{"version":"3.0.2"},"vinyl":{"version":"2.2.1","dependencies":{"clone":{"version":"2.1.2"},"clone-buffer":{"version":"1.0.0"},"clone-stats":{"version":"1.0.0"},"cloneable-readable":{"version":"1.1.3","dependencies":{"readable-stream":{"version":"2.3.7","dependencies":{"safe-buffer":{"version":"5.1.2"},"string_decoder":{"version":"1.1.1","dependencies":{"safe-buffer":{"version":"5.1.2"}}},"core-util-is":{"version":"1.0.2"},"inherits":{"version":"2.0.4"},"isarray":{"version":"1.0.0"},"process-nextick-args":{"version":"2.0.1"},"util-deprecate":{"version":"1.0.2"}}},"inherits":{"version":"2.0.4"},"process-nextick-args":{"version":"2.0.1"}}},"remove-trailing-separator":{"version":"1.1.0"},"replace-ext":{"version":"1.0.1"}}}}},"minimist":{"version":"1.2.5"},"pretty-bytes":{"version":"5.4.1"},"read-chunk":{"version":"3.2.0","dependencies":{"pify":{"version":"4.0.1"},"with-open-file":{"version":"0.1.7","dependencies":{"p-finally":{"version":"1.0.0"},"p-try":{"version":"2.2.0"},"pify":{"version":"4.0.1"}}}}},"read-pkg-up":{"version":"5.0.0","dependencies":{"find-up":{"version":"3.0.0"},"read-pkg":{"version":"5.2.0","dependencies":{"@types/normalize-package-data":{"version":"2.4.0"},"normalize-package-data":{"version":"2.5.0","dependencies":{"hosted-git-info":{"version":"2.8.8"},"semver":{"version":"5.7.1"},"resolve":{"version":"1.19.0"},"validate-npm-package-license":{"version":"3.0.4","dependencies":{"spdx-correct":{"version":"3.1.1","dependencies":{"spdx-expression-parse":{"version":"3.0.1"},"spdx-license-ids":{"version":"3.0.7"}}},"spdx-expression-parse":{"version":"3.0.1","dependencies":{"spdx-exceptions":{"version":"2.3.0"},"spdx-license-ids":{"version":"3.0.7"}}}}}}},"parse-json":{"version":"5.1.0","dependencies":{"@babel/code-frame":{"version":"7.10.4","dependencies":{"@babel/highlight":{"version":"7.10.4","dependencies":{"@babel/helper-validator-identifier":{"version":"7.10.4"},"chalk":{"version":"2.4.2","dependencies":{"ansi-styles":{"version":"3.2.1","dependencies":{"color-convert":{"version":"1.9.3","dependencies":{"color-name":{"version":"1.1.3"}}}}},"supports-color":{"version":"5.5.0","dependencies":{"has-flag":{"version":"3.0.0"}}},"escape-string-regexp":{"version":"1.0.5"}}},"js-tokens":{"version":"4.0.0"}}}}},"error-ex":{"version":"1.3.2","dependencies":{"is-arrayish":{"version":"0.2.1"}}},"json-parse-even-better-errors":{"version":"2.3.1"},"lines-and-columns":{"version":"1.1.6"}}},"type-fest":{"version":"0.6.0"}}}}},"rimraf":{"version":"2.7.1","dependencies":{"glob":{"version":"7.1.6"}}},"run-async":{"version":"2.4.1"},"semver":{"version":"7.3.4","dependencies":{"lru-cache":{"version":"6.0.0","dependencies":{"yallist":{"version":"4.0.0"}}}}},"shelljs":{"version":"0.8.4","dependencies":{"glob":{"version":"7.1.6"},"interpret":{"version":"1.4.0"},"rechoir":{"version":"0.6.2","dependencies":{"resolve":{"version":"1.19.0","dependencies":{"is-core-module":{"version":"2.2.0","dependencies":{"has":{"version":"1.0.3","dependencies":{"function-bind":{"version":"1.1.1"}}}}},"path-parse":{"version":"1.0.6"}}}}}}},"text-table":{"version":"0.2.0"},"through2":{"version":"3.0.2","dependencies":{"inherits":{"version":"2.0.4"},"readable-stream":{"version":"3.6.0","dependencies":{"inherits":{"version":"2.0.4"},"string_decoder":{"version":"1.3.0","dependencies":{"safe-buffer":{"version":"5.2.1"}}},"util-deprecate":{"version":"1.0.2"}}}}},"yeoman-environment":{"version":"2.10.3","dependencies":{"escape-string-regexp":{"version":"1.0.5"},"execa":{"version":"4.1.0","dependencies":{"cross-spawn":{"version":"7.0.3","dependencies":{"path-key":{"version":"3.1.1"},"shebang-command":{"version":"2.0.0","dependencies":{"shebang-regex":{"version":"3.0.0"}}},"which":{"version":"2.0.2","dependencies":{"isexe":{"version":"2.0.0"}}}}},"get-stream":{"version":"5.2.0","dependencies":{"pump":{"version":"3.0.0","dependencies":{"end-of-stream":{"version":"1.4.4","dependencies":{"once":{"version":"1.4.0"}}},"once":{"version":"1.4.0"}}}}},"is-stream":{"version":"2.0.0"},"human-signals":{"version":"1.1.1"},"merge-stream":{"version":"2.0.0"},"npm-run-path":{"version":"4.0.1","dependencies":{"path-key":{"version":"3.1.1"}}},"onetime":{"version":"5.1.2","dependencies":{"mimic-fn":{"version":"2.1.0"}}},"signal-exit":{"version":"3.0.3"},"strip-final-newline":{"version":"2.0.0"}}},"grouped-queue":{"version":"1.1.0"},"inquirer":{"version":"7.3.3","dependencies":{"ansi-escapes":{"version":"4.3.1","dependencies":{"type-fest":{"version":"0.11.0"}}},"cli-cursor":{"version":"3.1.0","dependencies":{"restore-cursor":{"version":"3.1.0","dependencies":{"onetime":{"version":"5.1.2"},"signal-exit":{"version":"3.0.3"}}}}},"cli-width":{"version":"3.0.0"},"external-editor":{"version":"3.1.0","dependencies":{"chardet":{"version":"0.7.0"},"iconv-lite":{"version":"0.4.24","dependencies":{"safer-buffer":{"version":"2.1.2"}}},"tmp":{"version":"0.0.33","dependencies":{"os-tmpdir":{"version":"1.0.2"}}}}},"figures":{"version":"3.2.0","dependencies":{"escape-string-regexp":{"version":"1.0.5"}}},"chalk":{"version":"4.1.0","dependencies":{"ansi-styles":{"version":"4.3.0","dependencies":{"color-convert":{"version":"2.0.1","dependencies":{"color-name":{"version":"1.1.4"}}}}},"supports-color":{"version":"7.2.0","dependencies":{"has-flag":{"version":"4.0.0"}}}}},"lodash":{"version":"4.17.20"},"mute-stream":{"version":"0.0.8"},"run-async":{"version":"2.4.1"},"rxjs":{"version":"6.6.3","dependencies":{"tslib":{"version":"1.14.1"}}},"string-width":{"version":"4.2.0"},"strip-ansi":{"version":"6.0.0"},"through":{"version":"2.3.8"}}},"is-scoped":{"version":"1.0.0","dependencies":{"scoped-regex":{"version":"1.0.0"}}},"lodash":{"version":"4.17.20"},"log-symbols":{"version":"2.2.0","dependencies":{"chalk":{"version":"2.4.2","dependencies":{"escape-string-regexp":{"version":"1.0.5"},"ansi-styles":{"version":"3.2.1","dependencies":{"color-convert":{"version":"1.9.3","dependencies":{"color-name":{"version":"1.1.3"}}}}},"supports-color":{"version":"5.5.0","dependencies":{"has-flag":{"version":"3.0.0"}}}}}}},"mem-fs":{"version":"1.2.0","dependencies":{"through2":{"version":"3.0.2"},"vinyl":{"version":"2.2.1"},"vinyl-file":{"version":"3.0.0","dependencies":{"graceful-fs":{"version":"4.2.4"},"strip-bom-buf":{"version":"1.0.0","dependencies":{"is-utf8":{"version":"0.2.1"}}},"strip-bom-stream":{"version":"2.0.0","dependencies":{"first-chunk-stream":{"version":"2.0.0","dependencies":{"readable-stream":{"version":"2.3.7","dependencies":{"core-util-is":{"version":"1.0.2"},"safe-buffer":{"version":"5.1.2"},"string_decoder":{"version":"1.1.1","dependencies":{"safe-buffer":{"version":"5.1.2"}}},"inherits":{"version":"2.0.4"},"isarray":{"version":"1.0.0"},"process-nextick-args":{"version":"2.0.1"},"util-deprecate":{"version":"1.0.2"}}}}},"strip-bom":{"version":"2.0.0","dependencies":{"is-utf8":{"version":"0.2.1"}}}}},"vinyl":{"version":"2.2.1"},"pify":{"version":"2.3.0"}}}}},"mem-fs-editor":{"version":"6.0.0"},"npm-api":{"version":"1.0.0","dependencies":{"JSONStream":{"version":"1.3.5","dependencies":{"jsonparse":{"version":"1.3.1"},"through":{"version":"2.3.8"}}},"clone-deep":{"version":"4.0.1","dependencies":{"is-plain-object":{"version":"2.0.4","dependencies":{"isobject":{"version":"3.0.1"}}},"kind-of":{"version":"6.0.3"},"shallow-clone":{"version":"3.0.1","dependencies":{"kind-of":{"version":"6.0.3"}}}}},"download-stats":{"version":"0.3.4","dependencies":{"JSONStream":{"version":"1.3.5"},"lazy-cache":{"version":"2.0.2","dependencies":{"set-getter":{"version":"0.1.0","dependencies":{"to-object-path":{"version":"0.3.0","dependencies":{"kind-of":{"version":"3.2.2","dependencies":{"is-buffer":{"version":"1.1.6"}}}}}}}}},"moment":{"version":"2.29.1"}}},"moment":{"version":"2.29.1"},"paged-request":{"version":"2.0.1","dependencies":{"axios":{"version":"0.18.1","dependencies":{"is-buffer":{"version":"2.0.5"},"follow-redirects":{"version":"1.5.10","dependencies":{"debug":{"version":"3.1.0","dependencies":{"ms":{"version":"2.0.0"}}}}}}}}},"request":{"version":"2.88.2","dependencies":{"aws-sign2":{"version":"0.7.0"},"aws4":{"version":"1.11.0"},"caseless":{"version":"0.12.0"},"combined-stream":{"version":"1.0.8","dependencies":{"delayed-stream":{"version":"1.0.0"}}},"extend":{"version":"3.0.2"},"forever-agent":{"version":"0.6.1"},"form-data":{"version":"2.3.3","dependencies":{"asynckit":{"version":"0.4.0"},"combined-stream":{"version":"1.0.8"},"mime-types":{"version":"2.1.27"}}},"har-validator":{"version":"5.1.5","dependencies":{"ajv":{"version":"6.12.6","dependencies":{"fast-deep-equal":{"version":"3.1.3"},"fast-json-stable-stringify":{"version":"2.1.0"},"json-schema-traverse":{"version":"0.4.1"},"uri-js":{"version":"4.4.0","dependencies":{"punycode":{"version":"2.1.1"}}}}},"har-schema":{"version":"2.0.0"}}},"http-signature":{"version":"1.2.0","dependencies":{"assert-plus":{"version":"1.0.0"},"jsprim":{"version":"1.4.1","dependencies":{"assert-plus":{"version":"1.0.0"},"extsprintf":{"version":"1.3.0"},"json-schema":{"version":"0.2.3"},"verror":{"version":"1.10.0","dependencies":{"assert-plus":{"version":"1.0.0"},"core-util-is":{"version":"1.0.2"},"extsprintf":{"version":"1.3.0"}}}}},"sshpk":{"version":"1.16.1","dependencies":{"asn1":{"version":"0.2.4","dependencies":{"safer-buffer":{"version":"2.1.2"}}},"assert-plus":{"version":"1.0.0"},"bcrypt-pbkdf":{"version":"1.0.2","dependencies":{"tweetnacl":{"version":"0.14.5"}}},"dashdash":{"version":"1.14.1","dependencies":{"assert-plus":{"version":"1.0.0"}}},"ecc-jsbn":{"version":"0.1.2","dependencies":{"jsbn":{"version":"0.1.1"},"safer-buffer":{"version":"2.1.2"}}},"getpass":{"version":"0.1.7","dependencies":{"assert-plus":{"version":"1.0.0"}}},"jsbn":{"version":"0.1.1"},"safer-buffer":{"version":"2.1.2"},"tweetnacl":{"version":"0.14.5"}}}}},"is-typedarray":{"version":"1.0.0"},"isstream":{"version":"0.1.2"},"json-stringify-safe":{"version":"5.0.1"},"mime-types":{"version":"2.1.27","dependencies":{"mime-db":{"version":"1.44.0"}}},"oauth-sign":{"version":"0.9.0"},"performance-now":{"version":"2.1.0"},"qs":{"version":"6.5.2"},"uuid":{"version":"3.4.0"},"safe-buffer":{"version":"5.2.1"},"tough-cookie":{"version":"2.5.0","dependencies":{"psl":{"version":"1.8.0"},"punycode":{"version":"2.1.1"}}},"tunnel-agent":{"version":"0.6.0","dependencies":{"safe-buffer":{"version":"5.2.1"}}}}}}},"semver":{"version":"7.3.4"},"text-table":{"version":"0.2.0"},"untildify":{"version":"3.0.3"},"chalk":{"version":"2.4.2","dependencies":{"escape-string-regexp":{"version":"1.0.5"},"ansi-styles":{"version":"3.2.1","dependencies":{"color-convert":{"version":"1.9.3","dependencies":{"color-name":{"version":"1.1.3"}}}}},"supports-color":{"version":"5.5.0","dependencies":{"has-flag":{"version":"3.0.0"}}}}},"debug":{"version":"3.2.7","dependencies":{"ms":{"version":"2.1.2"}}},"diff":{"version":"3.5.0"},"globby":{"version":"8.0.2","dependencies":{"array-union":{"version":"1.0.2"},"fast-glob":{"version":"2.2.7"},"glob":{"version":"7.1.6"},"dir-glob":{"version":"2.0.0","dependencies":{"path-type":{"version":"3.0.0"},"arrify":{"version":"1.0.1"}}},"ignore":{"version":"3.3.10"},"pify":{"version":"3.0.0"},"slash":{"version":"1.0.0"}}},"strip-ansi":{"version":"4.0.0","dependencies":{"ansi-regex":{"version":"3.0.0"}}},"yeoman-generator":{"version":"4.10.1"}}},"chalk":{"version":"2.4.2","dependencies":{"escape-string-regexp":{"version":"1.0.5"},"ansi-styles":{"version":"3.2.1","dependencies":{"color-convert":{"version":"1.9.3","dependencies":{"color-name":{"version":"1.1.3"}}}}},"supports-color":{"version":"5.5.0","dependencies":{"has-flag":{"version":"3.0.0"}}}}}}}}} \ No newline at end of file diff --git a/libraries/bot-ai-luis-v3/pom.xml b/libraries/bot-ai-luis-v3/pom.xml index 17f6212b6..c5d73f696 100644 --- a/libraries/bot-ai-luis-v3/pom.xml +++ b/libraries/bot-ai-luis-v3/pom.xml @@ -6,7 +6,7 @@ com.microsoft.bot bot-java - 4.14.0-SNAPSHOT + 4.15.0-SNAPSHOT ../../pom.xml diff --git a/libraries/bot-ai-luis-v3/src/main/java/com/microsoft/bot/ai/luis/package-info.java b/libraries/bot-ai-luis-v3/src/main/java/com/microsoft/bot/ai/luis/package-info.java index 181eab803..6927f945d 100644 --- a/libraries/bot-ai-luis-v3/src/main/java/com/microsoft/bot/ai/luis/package-info.java +++ b/libraries/bot-ai-luis-v3/src/main/java/com/microsoft/bot/ai/luis/package-info.java @@ -3,7 +3,7 @@ // license information. /** - * This package contains the classes for Bot-AI-LUIS. + * This package contains the classes for com.microsoft.bot.ai.luis. */ - +@Deprecated package com.microsoft.bot.ai.luis; diff --git a/libraries/bot-ai-qna/pom.xml b/libraries/bot-ai-qna/pom.xml index a1a78b912..fb2612a14 100644 --- a/libraries/bot-ai-qna/pom.xml +++ b/libraries/bot-ai-qna/pom.xml @@ -6,7 +6,7 @@ com.microsoft.bot bot-java - 4.14.0-SNAPSHOT + 4.15.0-SNAPSHOT ../../pom.xml diff --git a/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/dialogs/QnAMakerDialog.java b/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/dialogs/QnAMakerDialog.java index 9c5480071..606caef8a 100644 --- a/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/dialogs/QnAMakerDialog.java +++ b/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/dialogs/QnAMakerDialog.java @@ -731,9 +731,9 @@ protected CompletableFuture getQnAMakerClient(DialogContext dc) * Gets the options for the QnA Maker client that the dialog will use to query * the knowledge base. * - * @param dc The for the current turn of + * @param dc The {@link "DialogContext"} for the current turn of * conversation. - * @return A representing the asynchronous operation. If the + * @return A {@link "Task"} representing the asynchronous operation. If the * task is successful, the result contains the QnA Maker options to use. */ protected CompletableFuture getQnAMakerOptions(DialogContext dc) { diff --git a/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/dialogs/package-info.java b/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/dialogs/package-info.java index 73805a3b0..d20c8015b 100644 --- a/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/dialogs/package-info.java +++ b/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/dialogs/package-info.java @@ -3,6 +3,6 @@ // license information. /** - * This package contains the classes for Bot-Builder. + * This package contains the classes for com.microsoft.bot.ai.qna.dialogs. */ package com.microsoft.bot.ai.qna.dialogs; diff --git a/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/models/package-info.java b/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/models/package-info.java index cc5be8f6c..10e6bf1d1 100644 --- a/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/models/package-info.java +++ b/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/models/package-info.java @@ -3,6 +3,6 @@ // license information. /** - * This package contains the classes for Bot-Builder. + * This package contains the classes for com.microsoft.bot.ai.qna.models. */ package com.microsoft.bot.ai.qna.models; diff --git a/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/package-info.java b/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/package-info.java index 6ee0d0731..692c85b66 100644 --- a/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/package-info.java +++ b/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/package-info.java @@ -3,6 +3,7 @@ // license information. /** - * This package contains the classes for Bot-Builder. + * This package contains the classes for com.microsoft.bot.ai.qna. */ +@Deprecated package com.microsoft.bot.ai.qna; diff --git a/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/utils/package-info.java b/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/utils/package-info.java index 145e9ebb8..f21e0372a 100644 --- a/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/utils/package-info.java +++ b/libraries/bot-ai-qna/src/main/java/com/microsoft/bot/ai/qna/utils/package-info.java @@ -3,6 +3,6 @@ // license information. /** - * This package contains the classes for Bot-Builder. + * This package contains the classes for com.microsoft.bot.ai.qna.utils. */ package com.microsoft.bot.ai.qna.utils; diff --git a/libraries/bot-ai-qna/src/test/java/com/microsoft/bot/ai/qna/QnAMakerTests.java b/libraries/bot-ai-qna/src/test/java/com/microsoft/bot/ai/qna/QnAMakerTests.java index 1dbc9eb9f..c4ca798cc 100644 --- a/libraries/bot-ai-qna/src/test/java/com/microsoft/bot/ai/qna/QnAMakerTests.java +++ b/libraries/bot-ai-qna/src/test/java/com/microsoft/bot/ai/qna/QnAMakerTests.java @@ -107,6 +107,8 @@ private String getTrainRequestUrl() { @Test public void qnaMakerTraceActivity() { + PrintMethodName(); + MockWebServer mockWebServer = new MockWebServer(); try { QnAMaker qna = this.qnaReturnsAnswer(mockWebServer); @@ -139,6 +141,7 @@ public void qnaMakerTraceActivity() { Assert.assertTrue(activity.isType(ActivityTypes.TYPING)); }) .assertReply("echo:how do I clean the stove?") + .delay(500) // This delay avoids the assert immediately above occasionally getting "bar". .send("bar") .assertReply(activity -> Assert.assertTrue(activity.isType(ActivityTypes.TYPING))) .assertReply("echo:bar") @@ -170,6 +173,8 @@ public void qnaMakerTraceActivity() { @Test public void qnaMakerTraceActivityEmptyText() { + PrintMethodName(); + MockWebServer mockWebServer = new MockWebServer(); try { // Get basic Qna @@ -199,6 +204,8 @@ public void qnaMakerTraceActivityEmptyText() { @Test public void qnaMakerTraceActivityNullText() { + PrintMethodName(); + MockWebServer mockWebServer = new MockWebServer(); try { // Get basic Qna @@ -228,6 +235,8 @@ public void qnaMakerTraceActivityNullText() { @Test public void qnaMakerTraceActivityNullContext() { + PrintMethodName(); + MockWebServer mockWebServer = new MockWebServer(); try { // Get basic Qna @@ -247,6 +256,8 @@ public void qnaMakerTraceActivityNullContext() { @Test public void qnaMakerTraceActivityBadMessage() { + PrintMethodName(); + MockWebServer mockWebServer = new MockWebServer(); try { // Get basic Qna @@ -276,6 +287,8 @@ public void qnaMakerTraceActivityBadMessage() { @Test public void qnaMakerTraceActivityNullActivity() { + PrintMethodName(); + MockWebServer mockWebServer = new MockWebServer(); try { // Get basic Qna @@ -299,6 +312,8 @@ public void qnaMakerTraceActivityNullActivity() { @Test public void qnaMakerReturnsAnswer() { + PrintMethodName(); + MockWebServer mockWebServer = new MockWebServer(); try { QnAMaker qna = this.qnaReturnsAnswer(mockWebServer); @@ -319,6 +334,8 @@ public void qnaMakerReturnsAnswer() { @Test public void qnaMakerReturnsAnswerRaw() { + PrintMethodName(); + MockWebServer mockWebServer = new MockWebServer(); try { QnAMaker qna = this.qnaReturnsAnswer(mockWebServer); @@ -344,6 +361,8 @@ public void qnaMakerReturnsAnswerRaw() { @Test public void qnaMakerLowScoreVariation() { + PrintMethodName(); + MockWebServer mockWebServer = new MockWebServer(); try { String content = readFileContent("QnaMaker_TopNAnswer.json"); @@ -394,6 +413,8 @@ public void qnaMakerLowScoreVariation() { @Test public void qnaMakerCallTrain() { + PrintMethodName(); + MockWebServer mockWebServer = new MockWebServer(); ObjectMapper objectMapper = new ObjectMapper().findAndRegisterModules(); String url = this.getTrainRequestUrl(); @@ -441,6 +462,8 @@ public void qnaMakerCallTrain() { @Test public void qnaMakerReturnsAnswerConfiguration() { + PrintMethodName(); + MockWebServer mockWebServer = new MockWebServer(); try { QnAMaker qna = this.qnaReturnsAnswer(mockWebServer); @@ -462,6 +485,8 @@ public void qnaMakerReturnsAnswerConfiguration() { @Test public void qnaMakerReturnsAnswerWithFiltering() { + PrintMethodName(); + MockWebServer mockWebServer = new MockWebServer(); try { String content = readFileContent("QnaMaker_UsesStrictFilters_ToReturnAnswer.json"); @@ -520,6 +545,8 @@ public void qnaMakerReturnsAnswerWithFiltering() { @Test public void qnaMakerSetScoreThresholdWhenThresholdIsZero() { + PrintMethodName(); + MockWebServer mockWebServer = new MockWebServer(); try { String content = readFileContent("QnaMaker_ReturnsAnswer.json"); @@ -560,6 +587,8 @@ public void qnaMakerSetScoreThresholdWhenThresholdIsZero() { @Test public void qnaMakerTestThreshold() { + PrintMethodName(); + MockWebServer mockWebServer = new MockWebServer(); try { String content = readFileContent("QnaMaker_TestThreshold.json"); @@ -598,6 +627,8 @@ public void qnaMakerTestThreshold() { @Test public void qnaMakerTestScoreThresholdTooLargeOutOfRange() { + PrintMethodName(); + QnAMakerEndpoint qnAMakerEndpoint = new QnAMakerEndpoint(); qnAMakerEndpoint.setKnowledgeBaseId(knowledgeBaseId); qnAMakerEndpoint.setEndpointKey(endpointKey); @@ -612,6 +643,8 @@ public void qnaMakerTestScoreThresholdTooLargeOutOfRange() { @Test public void qnaMakerTestScoreThresholdTooSmallOutOfRange() { + PrintMethodName(); + QnAMakerEndpoint qnAMakerEndpoint = new QnAMakerEndpoint(); qnAMakerEndpoint.setKnowledgeBaseId(knowledgeBaseId); qnAMakerEndpoint.setEndpointKey(endpointKey); @@ -626,6 +659,8 @@ public void qnaMakerTestScoreThresholdTooSmallOutOfRange() { @Test public void qnaMakerReturnsAnswerWithContext() { + PrintMethodName(); + MockWebServer mockWebServer = new MockWebServer(); try { String content = readFileContent("QnaMaker_ReturnsAnswerWithContext.json"); @@ -670,6 +705,8 @@ public void qnaMakerReturnsAnswerWithContext() { @Test public void qnaMakerReturnAnswersWithoutContext() { + PrintMethodName(); + MockWebServer mockWebServer = new MockWebServer(); try { String content = readFileContent("QnaMaker_ReturnsAnswerWithoutContext.json"); @@ -708,6 +745,8 @@ public void qnaMakerReturnAnswersWithoutContext() { @Test public void qnaMakerReturnsHighScoreWhenIdPassed() { + PrintMethodName(); + MockWebServer mockWebServer = new MockWebServer(); try { String content = readFileContent("QnaMaker_ReturnsAnswerWithContext.json"); @@ -747,6 +786,8 @@ public void qnaMakerReturnsHighScoreWhenIdPassed() { @Test public void qnaMakerTestTopOutOfRange() { + PrintMethodName(); + QnAMakerEndpoint qnAMakerEndpoint = new QnAMakerEndpoint(); qnAMakerEndpoint.setKnowledgeBaseId(knowledgeBaseId); qnAMakerEndpoint.setEndpointKey(endpointKey); @@ -761,6 +802,8 @@ public void qnaMakerTestTopOutOfRange() { @Test public void qnaMakerTestEndpointEmptyKbId() { + PrintMethodName(); + QnAMakerEndpoint qnAMakerEndpoint = new QnAMakerEndpoint(); qnAMakerEndpoint.setKnowledgeBaseId(new String()); qnAMakerEndpoint.setEndpointKey(endpointKey); @@ -771,6 +814,8 @@ public void qnaMakerTestEndpointEmptyKbId() { @Test public void qnaMakerTestEndpointEmptyEndpointKey() { + PrintMethodName(); + QnAMakerEndpoint qnAMakerEndpoint = new QnAMakerEndpoint(); qnAMakerEndpoint.setKnowledgeBaseId(knowledgeBaseId); qnAMakerEndpoint.setEndpointKey(new String()); @@ -781,6 +826,8 @@ public void qnaMakerTestEndpointEmptyEndpointKey() { @Test public void qnaMakerTestEndpointEmptyHost() { + PrintMethodName(); + QnAMakerEndpoint qnAMakerEndpoint = new QnAMakerEndpoint(); qnAMakerEndpoint.setKnowledgeBaseId(knowledgeBaseId); qnAMakerEndpoint.setEndpointKey(endpointKey); @@ -791,6 +838,8 @@ public void qnaMakerTestEndpointEmptyHost() { @Test public void qnaMakerUserAgent() { + PrintMethodName(); + MockWebServer mockWebServer = new MockWebServer(); try { QnAMaker qna = this.qnaReturnsAnswer(mockWebServer); @@ -803,7 +852,7 @@ public void qnaMakerUserAgent() { results[0].getAnswer()); // Verify that we added the bot.builder package details. - Assert.assertTrue(request.getHeader("User-Agent").contains("BotBuilder/4.0.0")); + Assert.assertTrue(request.getHeader("User-Agent").contains("BotBuilder/4.")); } catch (Exception ex) { fail(); } finally { @@ -817,6 +866,8 @@ public void qnaMakerUserAgent() { @Test public void qnaMakerV2LegacyEndpointShouldThrow() { + PrintMethodName(); + MockWebServer mockWebServer = new MockWebServer(); try { String content = readFileContent("QnaMaker_LegacyEndpointAnswer.json"); @@ -848,6 +899,8 @@ public void qnaMakerV2LegacyEndpointShouldThrow() { @Test public void qnaMakerV3LeagacyEndpointShouldThrow() { + PrintMethodName(); + MockWebServer mockWebServer = new MockWebServer(); try { String content = readFileContent("QnaMaker_LegacyEndpointAnswer.json"); @@ -879,6 +932,8 @@ public void qnaMakerV3LeagacyEndpointShouldThrow() { @Test public void qnaMakerReturnsAnswerWithMetadataBoost() { + PrintMethodName(); + MockWebServer mockWebServer = new MockWebServer(); try { String content = readFileContent("QnaMaker_ReturnsAnswersWithMetadataBoost.json"); @@ -917,6 +972,8 @@ public void qnaMakerReturnsAnswerWithMetadataBoost() { @Test public void qnaMakerTestThresholdInQueryOption() { + PrintMethodName(); + MockWebServer mockWebServer = new MockWebServer(); try { String content = readFileContent("QnaMaker_ReturnsAnswer_GivenScoreThresholdQueryOption.json"); @@ -963,6 +1020,8 @@ public void qnaMakerTestThresholdInQueryOption() { @Test public void qnaMakerTestUnsuccessfulResponse() { + PrintMethodName(); + MockWebServer mockWebServer = new MockWebServer(); mockWebServer.enqueue(new MockResponse().setResponseCode(502)); try { @@ -988,6 +1047,8 @@ public void qnaMakerTestUnsuccessfulResponse() { @Test public void qnaMakerIsTestTrue() { + PrintMethodName(); + MockWebServer mockWebServer = new MockWebServer(); try { String content = readFileContent("QnaMaker_IsTest_True.json"); @@ -1026,6 +1087,8 @@ public void qnaMakerIsTestTrue() { @Test public void qnaMakerRankerTypeQuestionOnly() { + PrintMethodName(); + MockWebServer mockWebServer = new MockWebServer(); try { String content = readFileContent("QnaMaker_RankerType_QuestionOnly.json"); @@ -1064,6 +1127,8 @@ public void qnaMakerRankerTypeQuestionOnly() { @Test public void qnaMakerTestOptionsHydration() { + PrintMethodName(); + MockWebServer mockWebServer = new MockWebServer(); try { String url = this.getRequestUrl(); @@ -1183,6 +1248,8 @@ public void qnaMakerTestOptionsHydration() { @Test public void qnaMakerStrictFiltersCompoundOperationType() { + PrintMethodName(); + MockWebServer mockWebServer = new MockWebServer(); try { String content = readFileContent("QnaMaker_ReturnsAnswer.json"); @@ -1236,6 +1303,8 @@ public void qnaMakerStrictFiltersCompoundOperationType() { @Test public void telemetryNullTelemetryClient() { + PrintMethodName(); + // Arrange MockWebServer mockWebServer = new MockWebServer(); try { @@ -1278,6 +1347,8 @@ public void telemetryNullTelemetryClient() { @Test public void telemetryReturnsAnswer() { + PrintMethodName(); + // Arrange MockWebServer mockWebServer = new MockWebServer(); try { @@ -1341,6 +1412,8 @@ public void telemetryReturnsAnswer() { @Test public void telemetryReturnsAnswerWhenNoAnswerFoundInKB() { + PrintMethodName(); + // Arrange MockWebServer mockWebServer = new MockWebServer(); try { @@ -1404,6 +1477,8 @@ public void telemetryReturnsAnswerWhenNoAnswerFoundInKB() { @Test public void telemetryPii() { + PrintMethodName(); + // Arrange MockWebServer mockWebServer = new MockWebServer(); try { @@ -1470,6 +1545,8 @@ public void telemetryPii() { @Test public void telemetryOverride() { + PrintMethodName(); + MockWebServer mockWebServer = new MockWebServer(); try { String content = readFileContent("QnaMaker_ReturnsAnswer.json"); @@ -1537,7 +1614,9 @@ public void telemetryOverride() { @Test public void telemetryAdditionalPropsMetrics() { - //Arrange + PrintMethodName(); + + // Arrange MockWebServer mockWebServer = new MockWebServer(); try { String content = readFileContent("QnaMaker_ReturnsAnswer.json"); @@ -1614,6 +1693,8 @@ public void telemetryAdditionalPropsMetrics() { @Test public void telemetryAdditionalPropsOverride() { + PrintMethodName(); + // Arrange MockWebServer mockWebServer = new MockWebServer(); try { @@ -1687,7 +1768,9 @@ public void telemetryAdditionalPropsOverride() { @Test public void telemetryFillPropsOverride() { - //Arrange + PrintMethodName(); + + // Arrange MockWebServer mockWebServer = new MockWebServer(); try { String content = readFileContent("QnaMaker_ReturnsAnswer.json"); @@ -1765,6 +1848,11 @@ public void telemetryFillPropsOverride() { } } + private void PrintMethodName() + { + System.out.println("Running " + (new Throwable().getStackTrace()[1].getMethodName()) + "()"); + } + private static TurnContext getContext(String utterance) { TestAdapter b = new TestAdapter(); Activity a = new Activity(ActivityTypes.MESSAGE); diff --git a/libraries/bot-applicationinsights/pom.xml b/libraries/bot-applicationinsights/pom.xml index e0bbd2376..d9d1e2385 100644 --- a/libraries/bot-applicationinsights/pom.xml +++ b/libraries/bot-applicationinsights/pom.xml @@ -6,7 +6,7 @@ com.microsoft.bot bot-java - 4.14.0-SNAPSHOT + 4.15.0-SNAPSHOT ../../pom.xml diff --git a/libraries/bot-applicationinsights/src/main/java/com/microsoft/bot/applicationinsights/core/package-info.java b/libraries/bot-applicationinsights/src/main/java/com/microsoft/bot/applicationinsights/core/package-info.java index 300ffc6bb..be4da7780 100644 --- a/libraries/bot-applicationinsights/src/main/java/com/microsoft/bot/applicationinsights/core/package-info.java +++ b/libraries/bot-applicationinsights/src/main/java/com/microsoft/bot/applicationinsights/core/package-info.java @@ -3,6 +3,6 @@ // license information. /** - * This package contains the classes for bot-applicationinsights. + * This package contains the classes for com.microsoft.bot.applicationinsights.core. */ package com.microsoft.bot.applicationinsights.core; diff --git a/libraries/bot-applicationinsights/src/main/java/com/microsoft/bot/applicationinsights/package-info.java b/libraries/bot-applicationinsights/src/main/java/com/microsoft/bot/applicationinsights/package-info.java index 4bf130b60..549072bb9 100644 --- a/libraries/bot-applicationinsights/src/main/java/com/microsoft/bot/applicationinsights/package-info.java +++ b/libraries/bot-applicationinsights/src/main/java/com/microsoft/bot/applicationinsights/package-info.java @@ -3,6 +3,7 @@ // license information. /** - * This package contains the classes for bot-applicationinsights. + * This package contains the classes for com.microsoft.bot.applicationinsights. */ +@Deprecated package com.microsoft.bot.applicationinsights; diff --git a/libraries/bot-azure/pom.xml b/libraries/bot-azure/pom.xml index e8d8709b6..0ada29d2b 100644 --- a/libraries/bot-azure/pom.xml +++ b/libraries/bot-azure/pom.xml @@ -6,7 +6,7 @@ com.microsoft.bot bot-java - 4.14.0-SNAPSHOT + 4.15.0-SNAPSHOT ../../pom.xml @@ -57,7 +57,7 @@ com.microsoft.azure azure-documentdb - 2.4.3 + 2.6.4 @@ -76,7 +76,7 @@ com.azure azure-storage-queue - 12.8.0 + 12.15.0 @@ -90,7 +90,7 @@ com.azure azure-storage-blob - 12.10.0 + 12.20.0 diff --git a/libraries/bot-azure/src/main/java/com/microsoft/bot/azure/blobs/package-info.java b/libraries/bot-azure/src/main/java/com/microsoft/bot/azure/blobs/package-info.java index 76f1907f6..a3bfa207f 100644 --- a/libraries/bot-azure/src/main/java/com/microsoft/bot/azure/blobs/package-info.java +++ b/libraries/bot-azure/src/main/java/com/microsoft/bot/azure/blobs/package-info.java @@ -3,6 +3,6 @@ // license information. /** - * This package contains the classes for bot-azure. + * This package contains the classes for com.microsoft.bot.azure.blobs. */ package com.microsoft.bot.azure.blobs; diff --git a/libraries/bot-azure/src/main/java/com/microsoft/bot/azure/package-info.java b/libraries/bot-azure/src/main/java/com/microsoft/bot/azure/package-info.java index 48232c6e0..a96f4b587 100644 --- a/libraries/bot-azure/src/main/java/com/microsoft/bot/azure/package-info.java +++ b/libraries/bot-azure/src/main/java/com/microsoft/bot/azure/package-info.java @@ -3,6 +3,7 @@ // license information. /** - * This package contains the classes for bot-azure. + * This package contains the classes for com.microsoft.bot.azure. */ +@Deprecated package com.microsoft.bot.azure; diff --git a/libraries/bot-azure/src/main/java/com/microsoft/bot/azure/queues/package-info.java b/libraries/bot-azure/src/main/java/com/microsoft/bot/azure/queues/package-info.java index 07134724a..6e6a29c76 100644 --- a/libraries/bot-azure/src/main/java/com/microsoft/bot/azure/queues/package-info.java +++ b/libraries/bot-azure/src/main/java/com/microsoft/bot/azure/queues/package-info.java @@ -3,6 +3,6 @@ // license information. /** - * This package contains the classes for bot-integration-core. + * This package contains the classes for com.microsoft.bot.azure.queues. */ package com.microsoft.bot.azure.queues; diff --git a/libraries/bot-azure/src/test/java/com/microsoft/bot/azure/TranscriptStoreTests.java b/libraries/bot-azure/src/test/java/com/microsoft/bot/azure/TranscriptStoreTests.java index cb54cbfba..36acf2882 100644 --- a/libraries/bot-azure/src/test/java/com/microsoft/bot/azure/TranscriptStoreTests.java +++ b/libraries/bot-azure/src/test/java/com/microsoft/bot/azure/TranscriptStoreTests.java @@ -88,6 +88,7 @@ public void testCleanup() { // These tests require Azure Storage Emulator v5.7 @Test public void blobTranscriptParamTest() { + PrintMethodName(); Assert.assertThrows(IllegalArgumentException.class, () -> new BlobsTranscriptStore(null, getContainerName())); Assert.assertThrows(IllegalArgumentException.class, () -> new BlobsTranscriptStore(blobStorageEmulatorConnectionString, null)); @@ -99,6 +100,7 @@ public void blobTranscriptParamTest() { @Test public void transcriptsEmptyTest() { + PrintMethodName(); TranscriptStore transcriptStore = getTranscriptStore(); String unusedChannelId = UUID.randomUUID().toString(); PagedResult transcripts = transcriptStore.listTranscripts(unusedChannelId).join(); @@ -107,6 +109,7 @@ public void transcriptsEmptyTest() { @Test public void activityEmptyTest() { + PrintMethodName(); TranscriptStore transcriptStore = getTranscriptStore(); for (String convoId : CONVERSATION_SPECIAL_IDS) { PagedResult activities = transcriptStore.getTranscriptActivities(channelId, convoId).join(); @@ -116,6 +119,7 @@ public void activityEmptyTest() { @Test public void activityAddTest() { + PrintMethodName(); TranscriptStore transcriptStore = getTranscriptStore(); Activity[] loggedActivities = new Activity[5]; List activities = new ArrayList(); @@ -132,6 +136,7 @@ public void activityAddTest() { @Test public void transcriptRemoveTest() { + PrintMethodName(); TranscriptStore transcriptStore = getTranscriptStore(); for (int i = 0; i < 5; i++) { Activity a = TranscriptStoreTests.createActivity(i, i, CONVERSATION_IDS); @@ -147,6 +152,7 @@ public void transcriptRemoveTest() { @Test public void activityAddSpecialCharsTest() { + PrintMethodName(); TranscriptStore transcriptStore = getTranscriptStore(); Activity[] loggedActivities = new Activity[CONVERSATION_SPECIAL_IDS.length]; List activities = new ArrayList(); @@ -165,6 +171,7 @@ public void activityAddSpecialCharsTest() { @Test public void transcriptRemoveSpecialCharsTest() { + PrintMethodName(); TranscriptStore transcriptStore = getTranscriptStore(); for (int i = 0; i < CONVERSATION_SPECIAL_IDS.length; i++) { Activity a = TranscriptStoreTests.createActivity(i, i, CONVERSATION_SPECIAL_IDS); @@ -178,6 +185,7 @@ public void transcriptRemoveSpecialCharsTest() { @Test public void activityAddPagedResultTest() { + PrintMethodName(); TranscriptStore transcriptStore = getTranscriptStore(); String cleanChannel = UUID.randomUUID().toString(); @@ -205,6 +213,7 @@ public void activityAddPagedResultTest() { @Test public void transcriptRemovePagedTest() { + PrintMethodName(); TranscriptStore transcriptStore = getTranscriptStore(); int i; for (i = 0; i < CONVERSATION_SPECIAL_IDS.length; i++) { @@ -219,6 +228,7 @@ public void transcriptRemovePagedTest() { @Test public void nullParameterTests() { + PrintMethodName(); TranscriptStore store = getTranscriptStore(); Assert.assertThrows(IllegalArgumentException.class, () -> store.logActivity(null)); @@ -229,6 +239,7 @@ public void nullParameterTests() { @Test public void logActivities() { + PrintMethodName(); TranscriptStore transcriptStore = getTranscriptStore(); ConversationReference conversation = TestAdapter.createConversationReference(UUID.randomUUID().toString(), "User1", "Bot"); @@ -273,6 +284,7 @@ public void logActivities() { @Test public void logUpdateActivities() { + PrintMethodName(); TranscriptStore transcriptStore = getTranscriptStore(); ConversationReference conversation = TestAdapter.createConversationReference(UUID.randomUUID().toString(), "User1", "Bot"); @@ -318,6 +330,7 @@ public void logUpdateActivities() { @Test public void logMissingUpdateActivity() { + PrintMethodName(); TranscriptStore transcriptStore = getTranscriptStore(); ConversationReference conversation = TestAdapter.createConversationReference(UUID.randomUUID().toString(), "User1", "Bot"); @@ -359,6 +372,7 @@ public void logMissingUpdateActivity() { @Test public void testDateLogUpdateActivities() { + PrintMethodName(); TranscriptStore transcriptStore = getTranscriptStore(); OffsetDateTime dateTimeStartOffset1 = OffsetDateTime.now(); ConversationReference conversation = TestAdapter.createConversationReference(UUID.randomUUID().toString(), @@ -375,6 +389,8 @@ public void testDateLogUpdateActivities() { ResourceResponse response = turnContext.sendActivity(activity).join(); activity.setId(response.getId()); + delay(200); + ObjectMapper objectMapper = new ObjectMapper().findAndRegisterModules(); try { // clone the activity, so we can use it to do an update @@ -385,7 +401,7 @@ public void testDateLogUpdateActivities() { } } return CompletableFuture.completedFuture(null); - }).send("foo").send("update").assertReply("new response").startTest().join(); + }).send("foo").delay(500).send("update").delay(500).assertReply("new response").startTest().join(); try { TimeUnit.MILLISECONDS.sleep(5000); @@ -423,6 +439,7 @@ public void testDateLogUpdateActivities() { @Test public void logDeleteActivities() { + PrintMethodName(); TranscriptStore transcriptStore = getTranscriptStore(); ConversationReference conversation = TestAdapter.createConversationReference(UUID.randomUUID().toString(), @@ -493,7 +510,7 @@ private CompletableFuture> getPagedResult(ConversationRefe Integer expectedLength, Integer maxTimeout) throws TimeoutException { TranscriptStore transcriptStore = getTranscriptStore(); if (maxTimeout == null) { - maxTimeout = 5000; + maxTimeout = 10000; } PagedResult pagedResult = null; @@ -518,6 +535,11 @@ private CompletableFuture> getPagedResult(ConversationRefe return CompletableFuture.completedFuture(pagedResult); } + private void PrintMethodName() + { + System.out.println("Running " + (new Throwable().getStackTrace()[1].getMethodName()) + "()"); + } + /** * Time period delay. * diff --git a/libraries/bot-builder/pom.xml b/libraries/bot-builder/pom.xml index cef0cc547..bdf4df4f6 100644 --- a/libraries/bot-builder/pom.xml +++ b/libraries/bot-builder/pom.xml @@ -6,7 +6,7 @@ com.microsoft.bot bot-java - 4.14.0-SNAPSHOT + 4.15.0-SNAPSHOT ../../pom.xml diff --git a/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/ActivityHandler.java b/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/ActivityHandler.java index 74d7f5ed3..0a4fe8d25 100644 --- a/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/ActivityHandler.java +++ b/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/ActivityHandler.java @@ -3,7 +3,6 @@ package com.microsoft.bot.builder; -import com.fasterxml.jackson.databind.JsonNode; import com.microsoft.bot.connector.Async; import java.net.HttpURLConnection; import java.util.List; @@ -28,6 +27,7 @@ * {@link Activity} types. Pre and post processing of Activities can be plugged * in by deriving and calling the base class implementation. */ +@Deprecated public class ActivityHandler implements Bot { /** @@ -470,7 +470,7 @@ protected CompletableFuture onSignInInvoke(TurnContext turnContext) { * @param body The body to return in the invoke response. * @return The InvokeResponse object. */ - protected static InvokeResponse createInvokeResponse(Object body) { + protected InvokeResponse createInvokeResponse(Object body) { return new InvokeResponse(HttpURLConnection.HTTP_OK, body); } @@ -636,8 +636,8 @@ protected CompletableFuture onInstallationUpdateRemove(TurnContext turnCon * * @return A task that represents the work queued to execute. * - * When the {@link OnInvokeActivity(TurnContext{InvokeActivity})} method - * receives an Invoke with a {@link InvokeActivity#name} of + * When the {@link OnInvokeActivity(TurnContext(InvokeActivity))} method + * receives an Invoke with a {@link InvokeActivity.name} of * `adaptiveCard/action`, it calls this method. */ protected CompletableFuture onAdaptiveCardInvoke( @@ -702,17 +702,7 @@ private AdaptiveCardInvokeValue getAdaptiveCardInvokeValue(Activity activity) th throw new InvokeResponseException(HttpURLConnection.HTTP_BAD_REQUEST, response); } - Object obj = activity.getValue(); - JsonNode node = null; - if (obj instanceof JsonNode) { - node = (JsonNode) obj; - } else { - AdaptiveCardInvokeResponse response = createAdaptiveCardInvokeErrorResponse( - HttpURLConnection.HTTP_BAD_REQUEST, "BadRequest", "Value property instanceof not properly formed"); - throw new InvokeResponseException(HttpURLConnection.HTTP_BAD_REQUEST, response); - } - - AdaptiveCardInvokeValue invokeValue = Serialization.treeToValue(node, AdaptiveCardInvokeValue.class); + AdaptiveCardInvokeValue invokeValue = Serialization.getAs(activity.getValue(), AdaptiveCardInvokeValue.class); if (invokeValue == null) { AdaptiveCardInvokeResponse response = createAdaptiveCardInvokeErrorResponse( HttpURLConnection.HTTP_BAD_REQUEST, "BadRequest", "Value property instanceof not properly formed"); diff --git a/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/BotFrameworkAdapter.java b/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/BotFrameworkAdapter.java index 6e1fa16d4..8bc110c2a 100644 --- a/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/BotFrameworkAdapter.java +++ b/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/BotFrameworkAdapter.java @@ -1096,6 +1096,7 @@ public CompletableFuture createConversation( * @return A task that represents the work queued to execute. */ @SuppressWarnings("checkstyle:InnerAssignment") + @Deprecated public CompletableFuture createConversation( String channelId, String serviceUrl, @@ -1112,13 +1113,21 @@ public CompletableFuture createConversation( if (!StringUtils.isEmpty(tenantId)) { // Putting tenantId in channelData is a temporary solution while we wait for the // Teams API to be updated - ObjectNode channelData = JsonNodeFactory.instance.objectNode(); - channelData.set( - "tenant", - JsonNodeFactory.instance.objectNode().set("tenantId", JsonNodeFactory.instance.textNode(tenantId)) - ); + if (conversationParameters.getChannelData() != null) { + ((ObjectNode) conversationParameters.getChannelData()).set( + "tenantId", + JsonNodeFactory.instance.textNode(tenantId) + ); + } else { + ObjectNode channelData = JsonNodeFactory.instance.objectNode(); + channelData.set( + "tenant", + JsonNodeFactory.instance.objectNode() + .set("tenantId", JsonNodeFactory.instance.textNode(tenantId)) + ); - conversationParameters.setChannelData(channelData); + conversationParameters.setChannelData(channelData); + } conversationParameters.setTenantId(tenantId); } diff --git a/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/inspection/package-info.java b/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/inspection/package-info.java index e1395a104..84bce0bc9 100644 --- a/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/inspection/package-info.java +++ b/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/inspection/package-info.java @@ -3,6 +3,6 @@ // license information. /** - * This package contains the classes for Bot-Builder-Inspection. + * This package contains the classes for com.microsoft.bot.builder.inspection. */ package com.microsoft.bot.builder.inspection; diff --git a/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/integration/package-info.java b/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/integration/package-info.java index ced996d02..379395d3e 100644 --- a/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/integration/package-info.java +++ b/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/integration/package-info.java @@ -3,6 +3,6 @@ // license information. /** - * This package contains the classes for Bot-Builder-Inspection. + * This package contains the classes for com.microsoft.bot.builder.integration. */ package com.microsoft.bot.builder.integration; diff --git a/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/package-info.java b/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/package-info.java index 92f759e30..dfc9bd89e 100644 --- a/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/package-info.java +++ b/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/package-info.java @@ -3,6 +3,7 @@ // license information. /** - * This package contains the classes for Bot-Builder. + * This package contains the classes for com.microsoft.bot.builder. */ +@Deprecated package com.microsoft.bot.builder; diff --git a/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/skills/package-info.java b/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/skills/package-info.java index 4bf1c6ad6..5d86a65e1 100644 --- a/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/skills/package-info.java +++ b/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/skills/package-info.java @@ -3,6 +3,6 @@ // license information. /** - * This package contains the classes for Bot-Builder. + * This package contains the classes for com.microsoft.bot.builder.skills. */ package com.microsoft.bot.builder.skills; diff --git a/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/teams/TeamsActivityHandler.java b/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/teams/TeamsActivityHandler.java index 3d51319f4..5ef8317f1 100644 --- a/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/teams/TeamsActivityHandler.java +++ b/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/teams/TeamsActivityHandler.java @@ -9,6 +9,8 @@ import com.microsoft.bot.builder.ActivityHandler; import com.microsoft.bot.builder.InvokeResponse; import com.microsoft.bot.builder.TurnContext; +import com.microsoft.bot.connector.Async; +import com.microsoft.bot.connector.Channels; import com.microsoft.bot.connector.rest.ErrorResponseException; import com.microsoft.bot.schema.ChannelAccount; import com.microsoft.bot.schema.Error; @@ -18,6 +20,8 @@ import com.microsoft.bot.schema.teams.AppBasedLinkQuery; import com.microsoft.bot.schema.teams.ChannelInfo; import com.microsoft.bot.schema.teams.FileConsentCardResponse; +import com.microsoft.bot.schema.teams.MeetingEndEventDetails; +import com.microsoft.bot.schema.teams.MeetingStartEventDetails; import com.microsoft.bot.schema.teams.MessagingExtensionAction; import com.microsoft.bot.schema.teams.MessagingExtensionActionResponse; import com.microsoft.bot.schema.teams.MessagingExtensionQuery; @@ -45,7 +49,7 @@ * Pre and post processing of Activities can be plugged in by deriving and * calling the base class implementation. */ -@SuppressWarnings({ "checkstyle:JavadocMethod", "checkstyle:DesignForExtension", "checkstyle:MethodLength" }) +@SuppressWarnings({"checkstyle:JavadocMethod", "checkstyle:DesignForExtension", "checkstyle:MethodLength"}) public class TeamsActivityHandler extends ActivityHandler { /** * Invoked when an invoke activity is received from the connector when the base @@ -59,142 +63,109 @@ protected CompletableFuture onInvokeActivity(TurnContext turnCon CompletableFuture result; try { - if ( - turnContext.getActivity().getName() == null - && turnContext.getActivity().isTeamsActivity() - ) { + if (turnContext.getActivity().getName() == null && turnContext.getActivity().isTeamsActivity()) { result = onTeamsCardActionInvoke(turnContext); } else { switch (turnContext.getActivity().getName()) { case "fileConsent/invoke": result = onTeamsFileConsent( turnContext, - Serialization.safeGetAs( - turnContext.getActivity().getValue(), - FileConsentCardResponse.class - ) + Serialization.safeGetAs(turnContext.getActivity().getValue(), FileConsentCardResponse.class) ); break; case "actionableMessage/executeAction": result = onTeamsO365ConnectorCardAction( turnContext, - Serialization.safeGetAs( - turnContext.getActivity().getValue(), - O365ConnectorCardActionQuery.class - ) + Serialization + .safeGetAs(turnContext.getActivity().getValue(), O365ConnectorCardActionQuery.class) ).thenApply(aVoid -> createInvokeResponse(null)); break; case "composeExtension/queryLink": result = onTeamsAppBasedLinkQuery( turnContext, - Serialization.safeGetAs( - turnContext.getActivity().getValue(), - AppBasedLinkQuery.class - ) - ).thenApply(ActivityHandler::createInvokeResponse); + Serialization.safeGetAs(turnContext.getActivity().getValue(), AppBasedLinkQuery.class) + ).thenApply(this::createInvokeResponse); break; case "composeExtension/query": result = onTeamsMessagingExtensionQuery( turnContext, - Serialization.safeGetAs( - turnContext.getActivity().getValue(), - MessagingExtensionQuery.class - ) - ).thenApply(ActivityHandler::createInvokeResponse); + Serialization.safeGetAs(turnContext.getActivity().getValue(), MessagingExtensionQuery.class) + ).thenApply(this::createInvokeResponse); break; case "composeExtension/selectItem": - result = onTeamsMessagingExtensionSelectItem( - turnContext, - turnContext.getActivity().getValue() - ).thenApply(ActivityHandler::createInvokeResponse); + result = onTeamsMessagingExtensionSelectItem(turnContext, turnContext.getActivity().getValue()) + .thenApply(this::createInvokeResponse); break; case "composeExtension/submitAction": - result = onTeamsMessagingExtensionSubmitActionDispatch( - turnContext, - Serialization.safeGetAs( - turnContext.getActivity().getValue(), - MessagingExtensionAction.class - ) - ).thenApply(ActivityHandler::createInvokeResponse); + result = + onTeamsMessagingExtensionSubmitActionDispatch( + turnContext, + Serialization + .safeGetAs(turnContext.getActivity().getValue(), MessagingExtensionAction.class) + ).thenApply(this::createInvokeResponse); break; case "composeExtension/fetchTask": - result = onTeamsMessagingExtensionFetchTask( - turnContext, - Serialization.safeGetAs( - turnContext.getActivity().getValue(), - MessagingExtensionAction.class - ) - ).thenApply(ActivityHandler::createInvokeResponse); + result = + onTeamsMessagingExtensionFetchTask( + turnContext, + Serialization + .safeGetAs(turnContext.getActivity().getValue(), MessagingExtensionAction.class) + ).thenApply(this::createInvokeResponse); break; case "composeExtension/querySettingUrl": result = onTeamsMessagingExtensionConfigurationQuerySettingUrl( turnContext, - Serialization.safeGetAs( - turnContext.getActivity().getValue(), - MessagingExtensionQuery.class - ) - ).thenApply(ActivityHandler::createInvokeResponse); + Serialization.safeGetAs(turnContext.getActivity().getValue(), MessagingExtensionQuery.class) + ).thenApply(this::createInvokeResponse); break; case "composeExtension/setting": result = onTeamsMessagingExtensionConfigurationSetting( turnContext, turnContext.getActivity().getValue() - ).thenApply(ActivityHandler::createInvokeResponse); + ).thenApply(this::createInvokeResponse); break; case "composeExtension/onCardButtonClicked": result = onTeamsMessagingExtensionCardButtonClicked( turnContext, turnContext.getActivity().getValue() - ).thenApply(ActivityHandler::createInvokeResponse); + ).thenApply(this::createInvokeResponse); break; case "task/fetch": result = onTeamsTaskModuleFetch( turnContext, - Serialization.safeGetAs( - turnContext.getActivity().getValue(), - TaskModuleRequest.class - ) - ).thenApply(ActivityHandler::createInvokeResponse); + Serialization.safeGetAs(turnContext.getActivity().getValue(), TaskModuleRequest.class) + ).thenApply(this::createInvokeResponse); break; case "task/submit": result = onTeamsTaskModuleSubmit( turnContext, - Serialization.safeGetAs( - turnContext.getActivity().getValue(), - TaskModuleRequest.class - ) - ).thenApply(ActivityHandler::createInvokeResponse); + Serialization.safeGetAs(turnContext.getActivity().getValue(), TaskModuleRequest.class) + ).thenApply(this::createInvokeResponse); break; case "tab/fetch": result = onTeamsTabFetch( turnContext, - Serialization.safeGetAs( - turnContext.getActivity().getValue(), - TabRequest.class - ) - ).thenApply(ActivityHandler::createInvokeResponse); + Serialization.safeGetAs(turnContext.getActivity().getValue(), TabRequest.class) + ).thenApply(this::createInvokeResponse); break; case "tab/submit": result = onTeamsTabSubmit( turnContext, - Serialization.safeGetAs( - turnContext.getActivity().getValue(), - TabSubmit.class - ) - ).thenApply(ActivityHandler::createInvokeResponse); + Serialization.safeGetAs(turnContext.getActivity().getValue(), TabSubmit.class) + ).thenApply(this::createInvokeResponse); break; default: @@ -208,17 +179,12 @@ protected CompletableFuture onInvokeActivity(TurnContext turnCon } return result.exceptionally(e -> { - if ( - e instanceof CompletionException && e.getCause() instanceof InvokeResponseException - ) { + if (e instanceof CompletionException && e.getCause() instanceof InvokeResponseException) { return ((InvokeResponseException) e.getCause()).createInvokeResponse(); } else if (e instanceof InvokeResponseException) { return ((InvokeResponseException) e).createInvokeResponse(); } - return new InvokeResponse( - HttpURLConnection.HTTP_INTERNAL_ERROR, - e.getLocalizedMessage() - ); + return new InvokeResponse(HttpURLConnection.HTTP_INTERNAL_ERROR, e.getLocalizedMessage()); }); } @@ -255,9 +221,10 @@ protected CompletableFuture onTeamsSigninVerifyState(TurnContext turnConte /** * Invoked when a file consent card activity is received from the connector. * - * @param turnContext The current TurnContext. - * @param fileConsentCardResponse The response representing the value of the invoke activity sent - * when the user acts on a file consent card. + * @param turnContext The current TurnContext. + * @param fileConsentCardResponse The response representing the value of the + * invoke activity sent when the user acts on a + * file consent card. * @return An InvokeResponse depending on the action of the file consent card. */ protected CompletableFuture onTeamsFileConsent( @@ -266,14 +233,12 @@ protected CompletableFuture onTeamsFileConsent( ) { switch (fileConsentCardResponse.getAction()) { case "accept": - return onTeamsFileConsentAccept(turnContext, fileConsentCardResponse).thenApply( - aVoid -> createInvokeResponse(null) - ); + return onTeamsFileConsentAccept(turnContext, fileConsentCardResponse) + .thenApply(aVoid -> createInvokeResponse(null)); case "decline": - return onTeamsFileConsentDecline(turnContext, fileConsentCardResponse).thenApply( - aVoid -> createInvokeResponse(null) - ); + return onTeamsFileConsentDecline(turnContext, fileConsentCardResponse) + .thenApply(aVoid -> createInvokeResponse(null)); default: CompletableFuture result = new CompletableFuture<>(); @@ -290,9 +255,10 @@ protected CompletableFuture onTeamsFileConsent( /** * Invoked when a file consent card is accepted by the user. * - * @param turnContext The current TurnContext. - * @param fileConsentCardResponse The response representing the value of the invoke activity sent - * when the user accepts a file consent card. + * @param turnContext The current TurnContext. + * @param fileConsentCardResponse The response representing the value of the + * invoke activity sent when the user accepts a + * file consent card. * @return A task that represents the work queued to execute. */ protected CompletableFuture onTeamsFileConsentAccept( @@ -305,9 +271,10 @@ protected CompletableFuture onTeamsFileConsentAccept( /** * Invoked when a file consent card is declined by the user. * - * @param turnContext The current TurnContext. - * @param fileConsentCardResponse The response representing the value of the invoke activity sent - * when the user declines a file consent card. + * @param turnContext The current TurnContext. + * @param fileConsentCardResponse The response representing the value of the + * invoke activity sent when the user declines a + * file consent card. * @return A task that represents the work queued to execute. */ protected CompletableFuture onTeamsFileConsentDecline( @@ -318,10 +285,11 @@ protected CompletableFuture onTeamsFileConsentDecline( } /** - * Invoked when a Messaging Extension Query activity is received from the connector. + * Invoked when a Messaging Extension Query activity is received from the + * connector. * * @param turnContext The current TurnContext. - * @param query The query for the search command. + * @param query The query for the search command. * @return The Messaging Extension Response for the query. */ protected CompletableFuture onTeamsMessagingExtensionQuery( @@ -332,10 +300,11 @@ protected CompletableFuture onTeamsMessagingExtensio } /** - * Invoked when a O365 Connector Card Action activity is received from the connector. + * Invoked when a O365 Connector Card Action activity is received from the + * connector. * * @param turnContext The current TurnContext. - * @param query The O365 connector card HttpPOST invoke query. + * @param query The O365 connector card HttpPOST invoke query. * @return A task that represents the work queued to execute. */ protected CompletableFuture onTeamsO365ConnectorCardAction( @@ -349,7 +318,7 @@ protected CompletableFuture onTeamsO365ConnectorCardAction( * Invoked when an app based link query activity is received from the connector. * * @param turnContext The current TurnContext. - * @param query The invoke request body type for app-based link query. + * @param query The invoke request body type for app-based link query. * @return The Messaging Extension Response for the query. */ protected CompletableFuture onTeamsAppBasedLinkQuery( @@ -360,10 +329,11 @@ protected CompletableFuture onTeamsAppBasedLinkQuery } /** - * Invoked when a messaging extension select item activity is received from the connector. + * Invoked when a messaging extension select item activity is received from the + * connector. * * @param turnContext The current TurnContext. - * @param query The object representing the query. + * @param query The object representing the query. * @return The Messaging Extension Response for the query. */ protected CompletableFuture onTeamsMessagingExtensionSelectItem( @@ -374,10 +344,11 @@ protected CompletableFuture onTeamsMessagingExtensio } /** - * Invoked when a Messaging Extension Fetch activity is received from the connector. + * Invoked when a Messaging Extension Fetch activity is received from the + * connector. * * @param turnContext The current TurnContext. - * @param action The messaging extension action. + * @param action The messaging extension action. * @return The Messaging Extension Action Response for the action. */ protected CompletableFuture onTeamsMessagingExtensionFetchTask( @@ -388,10 +359,11 @@ protected CompletableFuture onTeamsMessagingEx } /** - * Invoked when a messaging extension submit action dispatch activity is received from the connector. + * Invoked when a messaging extension submit action dispatch activity is + * received from the connector. * * @param turnContext The current TurnContext. - * @param action The messaging extension action. + * @param action The messaging extension action. * @return The Messaging Extension Action Response for the action. */ protected CompletableFuture onTeamsMessagingExtensionSubmitActionDispatch( @@ -411,8 +383,7 @@ protected CompletableFuture onTeamsMessagingEx result.completeExceptionally( new InvokeResponseException( HttpURLConnection.HTTP_BAD_REQUEST, - action.getBotMessagePreviewAction() - + " is not a support BotMessagePreviewAction" + action.getBotMessagePreviewAction() + " is not a support BotMessagePreviewAction" ) ); return result; @@ -423,10 +394,11 @@ protected CompletableFuture onTeamsMessagingEx } /** - * Invoked when a messaging extension submit action activity is received from the connector. + * Invoked when a messaging extension submit action activity is received from + * the connector. * * @param turnContext The current TurnContext. - * @param action The messaging extension action. + * @param action The messaging extension action. * @return The Messaging Extension Action Response for the action. */ protected CompletableFuture onTeamsMessagingExtensionSubmitAction( @@ -437,10 +409,11 @@ protected CompletableFuture onTeamsMessagingEx } /** - * Invoked when a messaging extension bot message preview edit activity is received from the connector. + * Invoked when a messaging extension bot message preview edit activity is + * received from the connector. * * @param turnContext The current TurnContext. - * @param action The messaging extension action. + * @param action The messaging extension action. * @return The Messaging Extension Action Response for the action. */ protected CompletableFuture onTeamsMessagingExtensionBotMessagePreviewEdit( @@ -451,10 +424,11 @@ protected CompletableFuture onTeamsMessagingEx } /** - * Invoked when a messaging extension bot message preview send activity is received from the connector. + * Invoked when a messaging extension bot message preview send activity is + * received from the connector. * * @param turnContext The current TurnContext. - * @param action The messaging extension action. + * @param action The messaging extension action. * @return The Messaging Extension Action Response for the action. */ protected CompletableFuture onTeamsMessagingExtensionBotMessagePreviewSend( @@ -465,10 +439,11 @@ protected CompletableFuture onTeamsMessagingEx } /** - * Invoked when a messaging extension configuration query setting url activity is received from the connector. + * Invoked when a messaging extension configuration query setting url activity + * is received from the connector. * * @param turnContext The current TurnContext. - * @param query The Messaging extension query. + * @param query The Messaging extension query. * @return The Messaging Extension Response for the query. */ protected CompletableFuture onTeamsMessagingExtensionConfigurationQuerySettingUrl( @@ -479,10 +454,11 @@ protected CompletableFuture onTeamsMessagingExtensio } /** - * Override this in a derived class to provide logic for when a configuration is set for a messaging extension. + * Override this in a derived class to provide logic for when a configuration is + * set for a messaging extension. * * @param turnContext The current TurnContext. - * @param settings Object representing the configuration settings. + * @param settings Object representing the configuration settings. * @return A task that represents the work queued to execute. */ protected CompletableFuture onTeamsMessagingExtensionConfigurationSetting( @@ -493,9 +469,10 @@ protected CompletableFuture onTeamsMessagingExtensionConfigurationSetting( } /** - * Override this in a derived class to provide logic for when a task module is fetched. + * Override this in a derived class to provide logic for when a task module is + * fetched. * - * @param turnContext The current TurnContext. + * @param turnContext The current TurnContext. * @param taskModuleRequest The task module invoke request value payload. * @return A Task Module Response for the request. */ @@ -507,11 +484,11 @@ protected CompletableFuture onTeamsTaskModuleFetch( } /** - * Override this in a derived class to provide logic for when a card button - * is clicked in a messaging extension. + * Override this in a derived class to provide logic for when a card button is + * clicked in a messaging extension. * * @param turnContext The current TurnContext. - * @param cardData Object representing the card data. + * @param cardData Object representing the card data. * @return A task that represents the work queued to execute. */ protected CompletableFuture onTeamsMessagingExtensionCardButtonClicked( @@ -522,9 +499,10 @@ protected CompletableFuture onTeamsMessagingExtensionCardButtonClicked( } /** - * Override this in a derived class to provide logic for when a task module is submited. + * Override this in a derived class to provide logic for when a task module is + * submited. * - * @param turnContext The current TurnContext. + * @param turnContext The current TurnContext. * @param taskModuleRequest The task module invoke request value payload. * @return A Task Module Response for the request. */ @@ -537,18 +515,17 @@ protected CompletableFuture onTeamsTaskModuleSubmit( /** * Invoked when a conversation update activity is received from the channel. - * Conversation update activities are useful when it comes to responding to users being added to - * or removed from the channel. - * For example, a bot could respond to a user being added by greeting the user. + * Conversation update activities are useful when it comes to responding to + * users being added to or removed from the channel. For example, a bot could + * respond to a user being added by greeting the user. * * @param turnContext The current TurnContext. * @return A task that represents the work queued to execute. */ protected CompletableFuture onConversationUpdateActivity(TurnContext turnContext) { if (turnContext.getActivity().isTeamsActivity()) { - ResultPair channelData = turnContext.getActivity().tryGetChannelData( - TeamsChannelData.class - ); + ResultPair channelData = + turnContext.getActivity().tryGetChannelData(TeamsChannelData.class); if (turnContext.getActivity().getMembersAdded() != null) { return onTeamsMembersAddedDispatch( @@ -648,14 +625,14 @@ protected CompletableFuture onConversationUpdateActivity(TurnContext turnC } /** - * Override this in a derived class to provide logic for when members other than the bot - * join the channel, such as your bot's welcome logic. - * It will get the associated members with the provided accounts. + * Override this in a derived class to provide logic for when members other than + * the bot join the channel, such as your bot's welcome logic. It will get the + * associated members with the provided accounts. * - * @param membersAdded A list of all the accounts added to the channel, as described by - * the conversation update activity. - * @param teamInfo The team info object representing the team. - * @param turnContext The current TurnContext. + * @param membersAdded A list of all the accounts added to the channel, as + * described by the conversation update activity. + * @param teamInfo The team info object representing the team. + * @param turnContext The current TurnContext. * @return A task that represents the work queued to execute. */ protected CompletableFuture onTeamsMembersAddedDispatch( @@ -694,7 +671,7 @@ protected CompletableFuture onTeamsMembersAddedDispatch( throw ex; } } - } else { + } else { throw ex; } } @@ -716,14 +693,14 @@ protected CompletableFuture onTeamsMembersAddedDispatch( } /** - * Override this in a derived class to provide logic for when members other than the bot - * leave the channel, such as your bot's good-bye logic. - * It will get the associated members with the provided accounts. + * Override this in a derived class to provide logic for when members other than + * the bot leave the channel, such as your bot's good-bye logic. It will get the + * associated members with the provided accounts. * - * @param membersRemoved A list of all the accounts removed from the channel, as described by - * the conversation update activity. - * @param teamInfo The team info object representing the team. - * @param turnContext The current TurnContext. + * @param membersRemoved A list of all the accounts removed from the channel, as + * described by the conversation update activity. + * @param teamInfo The team info object representing the team. + * @param turnContext The current TurnContext. * @return A task that represents the work queued to execute. */ protected CompletableFuture onTeamsMembersRemovedDispatch( @@ -748,13 +725,13 @@ protected CompletableFuture onTeamsMembersRemovedDispatch( } /** - * Override this in a derived class to provide logic for when members other than the bot - * join the channel, such as your bot's welcome logic. + * Override this in a derived class to provide logic for when members other than + * the bot join the channel, such as your bot's welcome logic. * - * @param membersAdded A list of all the members added to the channel, as described by - * the conversation update activity. - * @param teamInfo The team info object representing the team. - * @param turnContext The current TurnContext. + * @param membersAdded A list of all the members added to the channel, as + * described by the conversation update activity. + * @param teamInfo The team info object representing the team. + * @param turnContext The current TurnContext. * @return A task that represents the work queued to execute. */ protected CompletableFuture onTeamsMembersAdded( @@ -766,13 +743,13 @@ protected CompletableFuture onTeamsMembersAdded( } /** - * Override this in a derived class to provide logic for when members other than the bot - * leave the channel, such as your bot's good-bye logic. + * Override this in a derived class to provide logic for when members other than + * the bot leave the channel, such as your bot's good-bye logic. * - * @param membersRemoved A list of all the members removed from the channel, as described by - * the conversation update activity. - * @param teamInfo The team info object representing the team. - * @param turnContext The current TurnContext. + * @param membersRemoved A list of all the members removed from the channel, as + * described by the conversation update activity. + * @param teamInfo The team info object representing the team. + * @param turnContext The current TurnContext. * @return A task that represents the work queued to execute. */ protected CompletableFuture onTeamsMembersRemoved( @@ -788,7 +765,7 @@ protected CompletableFuture onTeamsMembersRemoved( * Channel Created correspond to the user creating a new channel. * * @param channelInfo The channel info object which describes the channel. - * @param teamInfo The team info object representing the team. + * @param teamInfo The team info object representing the team. * @param turnContext The current TurnContext. * @return A task that represents the work queued to execute. */ @@ -805,7 +782,7 @@ protected CompletableFuture onTeamsChannelCreated( * Channel Deleted correspond to the user deleting an existing channel. * * @param channelInfo The channel info object which describes the channel. - * @param teamInfo The team info object representing the team. + * @param teamInfo The team info object representing the team. * @param turnContext The current TurnContext. * @return A task that represents the work queued to execute. */ @@ -822,7 +799,7 @@ protected CompletableFuture onTeamsChannelDeleted( * Channel Renamed correspond to the user renaming an existing channel. * * @param channelInfo The channel info object which describes the channel. - * @param teamInfo The team info object representing the team. + * @param teamInfo The team info object representing the team. * @param turnContext The current TurnContext. * @return A task that represents the work queued to execute. */ @@ -835,11 +812,12 @@ protected CompletableFuture onTeamsChannelRenamed( } /** - * Invoked when a Channel Restored event activity is received from the connector. - * Channel Restored correspond to the user restoring a previously deleted channel. + * Invoked when a Channel Restored event activity is received from the + * connector. Channel Restored correspond to the user restoring a previously + * deleted channel. * * @param channelInfo The channel info object which describes the channel. - * @param teamInfo The team info object representing the team. + * @param teamInfo The team info object representing the team. * @param turnContext The current TurnContext. * @return A task that represents the work queued to execute. */ @@ -882,8 +860,8 @@ protected CompletableFuture onTeamsTeamDeleted( } /** - * Invoked when a Team Hard Deleted event activity is received from the connector. - * Team Hard Deleted correspond to the user hard-deleting a team. + * Invoked when a Team Hard Deleted event activity is received from the + * connector. Team Hard Deleted correspond to the user hard-deleting a team. * * @param turnContext The current TurnContext. * @return A task that represents the work queued to execute. @@ -901,7 +879,7 @@ protected CompletableFuture onTeamsTeamHardDeleted( * Channel Renamed correspond to the user renaming an existing channel. * * @param channelInfo The channel info object which describes the channel. - * @param teamInfo The team info object representing the team. + * @param teamInfo The team info object representing the team. * @param turnContext The current TurnContext. * @return A task that represents the work queued to execute. */ @@ -945,8 +923,9 @@ protected CompletableFuture onTeamsTeamUnarchived( /** * Override this in a derived class to provide logic for when a tab is fetched. + * * @param turnContext The context object for this turn. - * @param tabRequest The tab invoke request value payload. + * @param tabRequest The tab invoke request value payload. * @return A Tab Response for the request. */ protected CompletableFuture onTeamsTabFetch(TurnContext turnContext, TabRequest tabRequest) { @@ -954,19 +933,101 @@ protected CompletableFuture onTeamsTabFetch(TurnContext turnContext } /** - * Override this in a derived class to provide logic for when a tab is submitted. + * Override this in a derived class to provide logic for when a tab is + * submitted. + * * @param turnContext The context object for this turn. - * @param tabSubmit The tab submit invoke request value payload. + * @param tabSubmit The tab submit invoke request value payload. * @return A Tab Response for the request. */ protected CompletableFuture onTeamsTabSubmit(TurnContext turnContext, TabSubmit tabSubmit) { return withException(new InvokeResponseException(HttpURLConnection.HTTP_NOT_IMPLEMENTED)); } + /** + * Invoked when a "tokens/response" event is received when the base behavior of + * {@link #onEventActivity(TurnContext)} is used. + * + *

+ * If using an OAuthPrompt, override this method to forward this + * {@link com.microsoft.bot.schema.Activity} to the current dialog. + *

+ * + *

+ * By default, this method does nothing. + *

+ *

+ * When the {@link #onEventActivity(TurnContext)} method receives an event with + * a {@link com.microsoft.bot.schema.Activity#getName()} of `tokens/response`, it calls this method. + * + * @param turnContext The context object for this turn. + * @return A task that represents the work queued to execute. + */ + @Override + protected CompletableFuture onEventActivity(TurnContext turnContext) { + if (StringUtils.equals(turnContext.getActivity().getChannelId(), Channels.MSTEAMS)) { + try { + switch (turnContext.getActivity().getName()) { + case "application/vnd.microsoft.meetingStart": + return onTeamsMeetingStart( + Serialization.safeGetAs( + turnContext.getActivity().getValue(), + MeetingStartEventDetails.class + ), + turnContext + ); + + case "application/vnd.microsoft.meetingEnd": + return onTeamsMeetingEnd( + Serialization.safeGetAs( + turnContext.getActivity().getValue(), + MeetingEndEventDetails.class + ), + turnContext + ); + + default: + break; + } + } catch (Throwable t) { + return Async.completeExceptionally(t); + } + } + + return super.onEventActivity(turnContext); + } + + /** + * Invoked when a Teams Meeting Start event activity is received from the + * connector. Override this in a derived class to provide logic for when a + * meeting is started. + * + * @param meeting The details of the meeting. + * @param turnContext The context object for this turn. + * @return A task that represents the work queued to execute. + */ + protected CompletableFuture onTeamsMeetingStart(MeetingStartEventDetails meeting, TurnContext turnContext) { + return CompletableFuture.completedFuture(null); + } + + /** + * Invoked when a Teams Meeting End event activity is received from the + * connector. Override this in a derived class to provide logic for when a + * meeting is ended. + * + * @param meeting The details of the meeting. + * @param turnContext The context object for this turn. + * @return A task that represents the work queued to execute. + */ + protected CompletableFuture onTeamsMeetingEnd(MeetingEndEventDetails meeting, TurnContext turnContext) { + return CompletableFuture.completedFuture(null); + } + /** * Invoke a new InvokeResponseException with a HTTP 501 code status. * - * @return true if this invocation caused this CompletableFuture to transition to a completed state, else false + * @return true if this invocation caused this CompletableFuture to transition + * to a completed state, else false */ protected CompletableFuture notImplemented() { return notImplemented(null); @@ -976,13 +1037,12 @@ protected CompletableFuture notImplemented() { * Invoke a new InvokeResponseException with a HTTP 501 code status. * * @param body The body for the InvokeResponseException. - * @return true if this invocation caused this CompletableFuture to transition to a completed state, else false + * @return true if this invocation caused this CompletableFuture to transition + * to a completed state, else false */ protected CompletableFuture notImplemented(String body) { CompletableFuture result = new CompletableFuture<>(); - result.completeExceptionally( - new InvokeResponseException(HttpURLConnection.HTTP_NOT_IMPLEMENTED, body) - ); + result.completeExceptionally(new InvokeResponseException(HttpURLConnection.HTTP_NOT_IMPLEMENTED, body)); return result; } diff --git a/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/teams/TeamsInfo.java b/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/teams/TeamsInfo.java index 65f65c780..f5d96da44 100644 --- a/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/teams/TeamsInfo.java +++ b/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/teams/TeamsInfo.java @@ -17,6 +17,7 @@ import com.microsoft.bot.schema.Serialization; import com.microsoft.bot.schema.teams.ChannelInfo; import com.microsoft.bot.schema.teams.ConversationList; +import com.microsoft.bot.schema.teams.MeetingInfo; import com.microsoft.bot.schema.teams.TeamDetails; import com.microsoft.bot.schema.teams.TeamsChannelAccount; import com.microsoft.bot.schema.teams.TeamsChannelData; @@ -269,6 +270,33 @@ public static CompletableFuture getMeetingParticipant( ); } + /** + * Gets the information for the given meeting id. + * @param turnContext Turn context. + * @return Meeting Details. + */ + public static CompletableFuture getMeetingInfo(TurnContext turnContext) { + return getMeetingInfo(turnContext, null); + } + + /** + * Gets the information for the given meeting id. + * @param turnContext Turn context. + * @param meetingId The BASE64-encoded id of the Teams meeting. + * @return Meeting Details. + */ + public static CompletableFuture getMeetingInfo(TurnContext turnContext, String meetingId) { + if (StringUtils.isEmpty(meetingId) && turnContext.getActivity().teamsGetMeetingInfo() != null) { + meetingId = turnContext.getActivity().teamsGetMeetingInfo().getId(); + } + + if (StringUtils.isEmpty(meetingId)) { + return illegalArgument("TeamsInfo.getMeetingInfo: method requires a meetingId"); + } + + return getTeamsConnectorClient(turnContext).getTeams().fetchMeetingInfo(meetingId); + } + private static CompletableFuture> getMembers( ConnectorClient connectorClient, String conversationId diff --git a/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/teams/package-info.java b/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/teams/package-info.java index 9b0c0b20a..9080ec831 100644 --- a/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/teams/package-info.java +++ b/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/teams/package-info.java @@ -3,6 +3,6 @@ // license information. /** - * This package contains the classes for Bot-Builder-Inspection. + * This package contains the classes for com.microsoft.bot.builder.teams. */ package com.microsoft.bot.builder.teams; diff --git a/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/adapters/TestAdapter.java b/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/adapters/TestAdapter.java index dcb458ebf..ce265c2e3 100644 --- a/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/adapters/TestAdapter.java +++ b/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/adapters/TestAdapter.java @@ -193,7 +193,10 @@ public CompletableFuture processActivity(Activity activity, BotCallbackHan // ready for next reply if (activity.getType() == null) activity.setType(ActivityTypes.MESSAGE); - activity.setChannelId(conversationReference().getChannelId()); + + if (activity.getChannelId() == null) { + activity.setChannelId(conversationReference().getChannelId()); + } if (activity.getFrom() == null || StringUtils.equalsIgnoreCase(activity.getFrom().getId(), "unknown") || activity.getFrom().getRole() == RoleTypes.BOT) { diff --git a/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/base/InterceptorManager.java b/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/base/InterceptorManager.java index e99035bb6..3be10f4f6 100644 --- a/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/base/InterceptorManager.java +++ b/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/base/InterceptorManager.java @@ -7,7 +7,6 @@ import com.fasterxml.jackson.databind.SerializationFeature; import com.google.common.io.BaseEncoding; import okhttp3.*; -import okhttp3.internal.Util; import okio.Buffer; import okio.BufferedSource; @@ -22,6 +21,8 @@ import java.util.Map; import java.util.zip.GZIPInputStream; +import static java.nio.charset.StandardCharsets.UTF_8; + public class InterceptorManager { private final static String RECORD_FOLDER = "session-records/"; @@ -288,7 +289,7 @@ private void extractResponseData( String contentType = response.header("Content-Type"); if (contentType != null) { if (contentType.startsWith("application/json")) { - content = buffer.readString(Util.UTF_8); + content = buffer.readString(UTF_8); } else { content = BaseEncoding.base64().encode(buffer.readByteArray()); } diff --git a/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/teams/TeamsActivityHandlerTests.java b/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/teams/TeamsActivityHandlerTests.java index 3d6ee347b..ab813f3fb 100644 --- a/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/teams/TeamsActivityHandlerTests.java +++ b/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/teams/TeamsActivityHandlerTests.java @@ -24,10 +24,13 @@ import com.microsoft.bot.schema.ConversationReference; import com.microsoft.bot.schema.ConversationResourceResponse; import com.microsoft.bot.schema.ResourceResponse; +import com.microsoft.bot.schema.Serialization; import com.microsoft.bot.schema.teams.AppBasedLinkQuery; import com.microsoft.bot.schema.teams.ChannelInfo; import com.microsoft.bot.schema.teams.FileConsentCardResponse; import com.microsoft.bot.schema.teams.FileUploadInfo; +import com.microsoft.bot.schema.teams.MeetingEndEventDetails; +import com.microsoft.bot.schema.teams.MeetingStartEventDetails; import com.microsoft.bot.schema.teams.MessagingExtensionAction; import com.microsoft.bot.schema.teams.MessagingExtensionActionResponse; import com.microsoft.bot.schema.teams.MessagingExtensionQuery; @@ -39,6 +42,7 @@ import com.microsoft.bot.schema.teams.TeamInfo; import com.microsoft.bot.schema.teams.TeamsChannelAccount; import com.microsoft.bot.schema.teams.TeamsChannelData; +import java.io.IOException; import org.apache.commons.lang3.NotImplementedException; import org.junit.Assert; import org.junit.Test; @@ -857,6 +861,80 @@ public void TestSigninVerifyState() { ); } + @Test + public void TestOnEventActivity() { + // Arrange + Activity activity = new Activity(ActivityTypes.EVENT); + activity.setChannelId(Channels.DIRECTLINE); + + TurnContext turnContext = new TurnContextImpl(new SimpleAdapter(), activity); + + // Act + TestActivityHandler bot = new TestActivityHandler(); + bot.onTurn(turnContext).join(); + + // Assert + Assert.assertEquals(1, bot.record.size()); + Assert.assertEquals("onEventActivity", bot.record.get(0)); + } + + @Test + public void TestMeetingStartEvent() throws IOException { + // Arrange + Activity activity = new Activity(ActivityTypes.EVENT); + activity.setChannelId(Channels.MSTEAMS); + activity.setName("application/vnd.microsoft.meetingStart"); + activity.setValue(Serialization.jsonToTree("{\"StartTime\": \"2021-06-05T00:01:02.0Z\"}")); + + AtomicReference> activitiesToSend = new AtomicReference<>(); + + TurnContext turnContext = new TurnContextImpl( + new SimpleAdapter(activitiesToSend::set), + activity + ); + + // Act + TestActivityHandler bot = new TestActivityHandler(); + bot.onTurn(turnContext).join(); + + // Assert + Assert.assertEquals(2, bot.record.size()); + Assert.assertEquals("onEventActivity", bot.record.get(0)); + Assert.assertEquals("onTeamsMeetingStart", bot.record.get(1)); + + Assert.assertNotNull(activitiesToSend.get()); + Assert.assertEquals(1, activitiesToSend.get().size()); + Assert.assertTrue(activitiesToSend.get().get(0).getText().contains("00:01:02")); + } + + @Test + public void TestMeetingEndEvent() throws IOException { + // Arrange + Activity activity = new Activity(ActivityTypes.EVENT); + activity.setChannelId(Channels.MSTEAMS); + activity.setName("application/vnd.microsoft.meetingEnd"); + activity.setValue(Serialization.jsonToTree("{\"EndTime\": \"2021-06-05T01:02:03.0Z\"}")); + + AtomicReference> activitiesToSend = new AtomicReference<>(); + + TurnContext turnContext = new TurnContextImpl( + new SimpleAdapter(activitiesToSend::set), + activity + ); + + // Act + TestActivityHandler bot = new TestActivityHandler(); + bot.onTurn(turnContext).join(); + + // Assert + Assert.assertEquals(2, bot.record.size()); + Assert.assertEquals("onEventActivity", bot.record.get(0)); + Assert.assertEquals("onTeamsMeetingEnd", bot.record.get(1)); + Assert.assertNotNull(activitiesToSend.get()); + Assert.assertEquals(1, activitiesToSend.get().size()); + Assert.assertTrue(activitiesToSend.get().get(0).getText().contains("1:02:03")); + } + private static class NotImplementedAdapter extends BotAdapter { @Override @@ -1209,6 +1287,34 @@ protected CompletableFuture onTeamsTeamUnarchived( record.add("onTeamsTeamUnarchived"); return super.onTeamsTeamUnarchived(channelInfo, teamInfo, turnContext); } + + @Override + protected CompletableFuture onEventActivity( + TurnContext turnContext + ) { + record.add("onEventActivity"); + return super.onEventActivity(turnContext); + } + + @Override + protected CompletableFuture onTeamsMeetingStart( + MeetingStartEventDetails meeting, + TurnContext turnContext + ) { + record.add("onTeamsMeetingStart"); + return turnContext.sendActivity(meeting.getStartTime().toString()) + .thenCompose(resourceResponse -> super.onTeamsMeetingStart(meeting, turnContext)); + } + + @Override + protected CompletableFuture onTeamsMeetingEnd( + MeetingEndEventDetails meeting, + TurnContext turnContext + ) { + record.add("onTeamsMeetingEnd"); + return turnContext.sendActivity(meeting.getEndTime().toString()) + .thenCompose(resourceResponse -> super.onTeamsMeetingEnd(meeting, turnContext)); + } } private static ConnectorClient getConnectorClient(String baseUri, AppCredentials credentials) { diff --git a/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/teams/TeamsInfoTests.java b/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/teams/TeamsInfoTests.java index cd47f251e..942590aa8 100644 --- a/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/teams/TeamsInfoTests.java +++ b/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/teams/TeamsInfoTests.java @@ -29,10 +29,13 @@ import com.microsoft.bot.schema.Pair; import com.microsoft.bot.schema.teams.ChannelInfo; import com.microsoft.bot.schema.teams.ConversationList; +import com.microsoft.bot.schema.teams.MeetingDetails; +import com.microsoft.bot.schema.teams.MeetingInfo; import com.microsoft.bot.schema.teams.TeamDetails; import com.microsoft.bot.schema.teams.TeamInfo; import com.microsoft.bot.schema.teams.TeamsChannelAccount; import com.microsoft.bot.schema.teams.TeamsChannelData; +import com.microsoft.bot.schema.teams.TeamsMeetingInfo; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @@ -176,6 +179,31 @@ public void TestGetChannels() { handler.onTurn(turnContext).join(); } + @Test + public void TestGetMeetingInfo() { + String baseUri = "https://test.coffee"; + MicrosoftAppCredentials credentials = MicrosoftAppCredentials.empty(); + ConnectorClient connectorClient = getConnectorClient(baseUri, credentials); + + Activity activity = new Activity(ActivityTypes.MESSAGE); + activity.setText("Test-GetMeetingInfoAsync"); + activity.setChannelId(Channels.MSTEAMS); + TeamsChannelData data = new TeamsChannelData(); + data.setMeeting(new TeamsMeetingInfo("meeting-id")); + activity.setChannelData(data); + + TurnContext turnContext = new TurnContextImpl(new SimpleAdapter(), activity); + turnContext.getTurnState().add(BotFrameworkAdapter.CONNECTOR_CLIENT_KEY, connectorClient); + turnContext.getTurnState().add( + BotFrameworkAdapter.TEAMSCONNECTOR_CLIENT_KEY, + getTeamsConnectorClient(connectorClient.baseUrl(), credentials) + ); + turnContext.getActivity().setServiceUrl("https://test.coffee"); + + ActivityHandler handler = new TestTeamsActivityHandler(); + handler.onTurn(turnContext).join(); + } + private class TestBotFrameworkAdapter extends BotFrameworkAdapter { public TestBotFrameworkAdapter(CredentialProvider withCredentialProvider) { @@ -213,6 +241,9 @@ public CompletableFuture onTurn(TurnContext turnContext) { case "Test-SendMessageToTeamsChannelAsync": return callSendMessageToTeamsChannel(turnContext); + case "Test-GetMeetingInfoAsync": + return callTeamsInfoGetMeetingInfo(turnContext); + default: Assert.fail(); } @@ -308,6 +339,16 @@ private CompletableFuture callGetChannels(TurnContext turnContext) { return CompletableFuture.completedFuture(null); } + + private CompletableFuture callTeamsInfoGetMeetingInfo(TurnContext turnContext) { + MeetingInfo meeting = TeamsInfo.getMeetingInfo(turnContext, null).join(); + + Assert.assertEquals("meeting-id", meeting.getDetails().getId()); + Assert.assertEquals("organizer-id", meeting.getOrganizer().getId()); + Assert.assertEquals("meetingConversationId-1", meeting.getConversation().getId()); + + return CompletableFuture.completedFuture(null); + } } private static ConnectorClient getConnectorClient(String baseUri, AppCredentials credentials) { @@ -412,6 +453,24 @@ private static TeamsConnectorClient getTeamsConnectorClient( CompletableFuture.completedFuture(details) ); + // fetchTeamDetails + MeetingInfo meetingInfo = new MeetingInfo(); + MeetingDetails meetingDetails = new MeetingDetails(); + meetingDetails.setId("meeting-id"); + meetingInfo.setDetails(meetingDetails); + + TeamsChannelAccount organizer = new TeamsChannelAccount(); + organizer.setId("organizer-id"); + meetingInfo.setOrganizer(organizer); + + ConversationAccount conversationAccount = new ConversationAccount(); + conversationAccount.setId("meetingConversationId-1"); + meetingInfo.setConversation(conversationAccount); + + Mockito.when(mockOperations.fetchMeetingInfo(Mockito.anyString())).thenReturn( + CompletableFuture.completedFuture(meetingInfo) + ); + TeamsConnectorClient mockConnectorClient = Mockito.mock(TeamsConnectorClient.class); Mockito.when(mockConnectorClient.getTeams()).thenReturn(mockOperations); Mockito.when(mockConnectorClient.baseUrl()).thenReturn(baseUri); diff --git a/libraries/bot-connector/pom.xml b/libraries/bot-connector/pom.xml index 94665c8b8..b533c369c 100644 --- a/libraries/bot-connector/pom.xml +++ b/libraries/bot-connector/pom.xml @@ -6,7 +6,7 @@ com.microsoft.bot bot-java - 4.14.0-SNAPSHOT + 4.15.0-SNAPSHOT ../../pom.xml @@ -53,6 +53,11 @@ org.mockito mockito-core + + org.skyscreamer + jsonassert + 1.5.0 + @@ -98,7 +103,7 @@ com.microsoft.azure msal4j - 1.8.1 + 1.11.0 diff --git a/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/ConnectorConfiguration.java b/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/ConnectorConfiguration.java index 469f70cd5..1c0dc0868 100644 --- a/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/ConnectorConfiguration.java +++ b/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/ConnectorConfiguration.java @@ -13,19 +13,18 @@ /** * Loads configuration properties for bot-connector. * - * Properties are located in an optional connector.properties file in the - * classpath. + * The version of the package will be in the project.properties file. */ public class ConnectorConfiguration { /** * Load and pass properties to a function. - * + * * @param func The function to process the loaded properties. */ public void process(Consumer func) { final Properties properties = new Properties(); try (InputStream propStream = - UserAgent.class.getClassLoader().getResourceAsStream("connector.properties")) { + UserAgent.class.getClassLoader().getResourceAsStream("project.properties")) { properties.load(propStream); if (!properties.containsKey("version")) { diff --git a/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/ConversationConstants.java b/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/ConversationConstants.java new file mode 100644 index 000000000..d158a5799 --- /dev/null +++ b/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/ConversationConstants.java @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.bot.connector; + +/** + * Values and constants used for Conversation specific info. + */ +public final class ConversationConstants { + private ConversationConstants() { + } + + /** + * The name of Http Request Header to add Conversation Id to skills requests. + */ + public static final String CONVERSATION_ID_HTTP_HEADERNAME = "x-ms-conversation-id"; +} diff --git a/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/UserAgent.java b/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/UserAgent.java index 8b8b7af40..fb5d66bd8 100644 --- a/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/UserAgent.java +++ b/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/UserAgent.java @@ -13,7 +13,7 @@ *

* Conforms to spec: * https://github.com/Microsoft/botbuilder-dotnet/blob/d342cd66d159a023ac435aec0fdf791f93118f5f/doc/UserAgents.md - *

+ * */ public final class UserAgent { // os/java and botbuilder will never change - static initialize once diff --git a/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/authentication/AllowedCallersClaimsValidator.java b/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/authentication/AllowedCallersClaimsValidator.java index da3eff071..71cc7bd77 100644 --- a/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/authentication/AllowedCallersClaimsValidator.java +++ b/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/authentication/AllowedCallersClaimsValidator.java @@ -20,7 +20,7 @@ public class AllowedCallersClaimsValidator extends ClaimsValidator { /** * Creates an instance of an {@link AllowedCallersClaimsValidator}. - * @param withAllowedCallers A List that contains the list of allowed callers. + * @param withAllowedCallers A {@link List} that contains the list of allowed callers. */ public AllowedCallersClaimsValidator(List withAllowedCallers) { this.allowedCallers = withAllowedCallers != null ? withAllowedCallers : new ArrayList(); diff --git a/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/authentication/JwtTokenValidation.java b/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/authentication/JwtTokenValidation.java index 34a77a78f..26639614c 100644 --- a/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/authentication/JwtTokenValidation.java +++ b/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/authentication/JwtTokenValidation.java @@ -250,7 +250,7 @@ public static String getAppIdFromClaims(Map claims) throws Illeg /** * Internal helper to check if the token has the shape we expect "Bearer [big long string]". * - * @param authHeader >A string containing the token header. + * @param authHeader A string containing the token header. * @return True if the token is valid, false if not. */ public static boolean isValidTokenFormat(String authHeader) { diff --git a/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/authentication/SkillValidation.java b/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/authentication/SkillValidation.java index cbf1885be..ededd5a65 100644 --- a/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/authentication/SkillValidation.java +++ b/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/authentication/SkillValidation.java @@ -78,18 +78,18 @@ public static boolean isSkillToken(String authHeader) { /** * Checks if the given list of claims represents a skill. * - * A skill claim should contain: An {@link AuthenticationConstants#versionClaim} - * claim. An {@link AuthenticationConstants#audienceClaim} claim. An - * {@link AuthenticationConstants#appIdClaim} claim (v1) or an a - * {@link AuthenticationConstants#authorizedParty} claim (v2). And the appId + * A skill claim should contain: An {@link AuthenticationConstants.VERSION_CLAIM} + * claim. An {@link AuthenticationConstants.AUTIENCE_CLAIM} claim. An + * {@link AuthenticationConstants.APPID_CLAIM} claim (v1) or an a + * {@link AuthenticationConstants.AUTHORIZED_PARTY} claim (v2). And the appId * claim should be different than the audience claim. When a channel (webchat, - * teams, etc.) invokes a bot, the {@link AuthenticationConstants#audienceClaim} - * is set to {@link AuthenticationConstants#toBotFromChannelTokenIssuer} but + * teams, etc.) invokes a bot, the {@link AuthenticationConstants.AUTIENCE_CLAIM} + * is set to {@link AuthenticationConstants.TO_BOT_FROM_CHANNEL_TOKEN_ISSUER} but * when a bot calls another bot, the audience claim is set to the appId of the * bot being invoked. The protocol supports v1 and v2 tokens: For v1 tokens, the - * {@link AuthenticationConstants#appIdClaim} is present and set to the app Id + * {@link AuthenticationConstants.APPID_CLAIM} is present and set to the app Id * of the calling bot. For v2 tokens, the - * {@link AuthenticationConstants#authorizedParty} is present and set to the app + * {@link AuthenticationConstants.AUTHORIZED_PARTY} is present and set to the app * Id of the calling bot. * * @param claims A list of claims. diff --git a/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/authentication/package-info.java b/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/authentication/package-info.java index b02693e21..bade06394 100644 --- a/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/authentication/package-info.java +++ b/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/authentication/package-info.java @@ -3,6 +3,6 @@ // license information. /** - * This package contains the implementation auth classes for ConnectorClient. + * This package contains the implementation auth classes for com.microsoft.bot.connector.authentication. */ package com.microsoft.bot.connector.authentication; diff --git a/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/package-info.java b/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/package-info.java index c4b63b1fc..6b974b45b 100644 --- a/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/package-info.java +++ b/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/package-info.java @@ -3,6 +3,7 @@ // license information. /** - * This package contains the classes for Bot-Connector. + * This package contains the classes for com.microsoft.bot.connector. */ +@Deprecated package com.microsoft.bot.connector; diff --git a/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/rest/RestConversations.java b/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/rest/RestConversations.java index aea20187c..82bce2eed 100644 --- a/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/rest/RestConversations.java +++ b/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/rest/RestConversations.java @@ -7,6 +7,7 @@ package com.microsoft.bot.connector.rest; import com.microsoft.bot.connector.Async; +import com.microsoft.bot.connector.ConversationConstants; import com.microsoft.bot.restclient.ServiceResponseBuilder; import com.microsoft.bot.schema.Activity; import com.microsoft.bot.schema.AttachmentData; @@ -111,15 +112,18 @@ CompletableFuture> updateActivity( @Header("User-Agent") String userAgent ); - @Headers({ "Content-Type: application/json; charset=utf-8", - "x-ms-logging-context: com.microsoft.bot.schema.Conversations replyToActivity" }) + @Headers({ + "Content-Type: application/json; charset=utf-8", + "x-ms-logging-context: com.microsoft.bot.schema.Conversations replyToActivity" + }) @POST("v3/conversations/{conversationId}/activities/{activityId}") CompletableFuture> replyToActivity( @Path("conversationId") String conversationId, @Path("activityId") String activityId, @Body Activity activity, @Header("accept-language") String acceptLanguage, - @Header("User-Agent") String userAgent + @Header("User-Agent") String userAgent, + @Header(ConversationConstants.CONVERSATION_ID_HTTP_HEADERNAME) String conversationIdHeader ); @Headers({ "Content-Type: application/json; charset=utf-8", @@ -454,7 +458,12 @@ public CompletableFuture replyToActivity( Validator.validate(activity); return service.replyToActivity( - conversationId, activityId, activity, client.getAcceptLanguage(), client.getUserAgent() + conversationId, + activityId, + activity, + client.getAcceptLanguage(), + client.getUserAgent(), + conversationId ) .thenApply(responseBodyResponse -> { diff --git a/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/rest/RestTeamsOperations.java b/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/rest/RestTeamsOperations.java index bf1367450..4c6c7caed 100644 --- a/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/rest/RestTeamsOperations.java +++ b/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/rest/RestTeamsOperations.java @@ -11,6 +11,7 @@ import com.microsoft.bot.connector.teams.TeamsOperations; import com.microsoft.bot.restclient.ServiceResponse; import com.microsoft.bot.schema.teams.ConversationList; +import com.microsoft.bot.schema.teams.MeetingInfo; import com.microsoft.bot.schema.teams.TeamDetails; import com.microsoft.bot.schema.teams.TeamsMeetingParticipant; import okhttp3.ResponseBody; @@ -162,6 +163,39 @@ private ServiceResponse fetchParticipantDelegate( .build(response); } + /** + * Fetches Teams meeting participant details. + * @param meetingId Teams meeting id + * @return TeamsParticipantChannelAccount + */ + @Override + public CompletableFuture fetchMeetingInfo(String meetingId) { + return service.fetchMeetingInfo( + meetingId, client.getAcceptLanguage(), client.getUserAgent() + ) + .thenApply(responseBodyResponse -> { + try { + return fetchMeetingInfoDelegate(responseBodyResponse).body(); + } catch (ErrorResponseException e) { + throw e; + } catch (Throwable t) { + throw new ErrorResponseException("fetchMeetingInfo", responseBodyResponse); + } + }); + } + + private ServiceResponse fetchMeetingInfoDelegate( + Response response + ) throws ErrorResponseException, IOException, IllegalArgumentException { + return client.restClient() + .responseBuilderFactory() + .newInstance(client.serializerAdapter()) + .register(HttpURLConnection.HTTP_OK, new TypeToken() { + }.getType()) + .registerError(ErrorResponseException.class) + .build(response); + } + /** * The interface defining all the services for TeamsOperations to be used by * Retrofit to perform actually REST calls. @@ -196,5 +230,14 @@ CompletableFuture> fetchParticipant( @Header("accept-language") String acceptLanguage, @Header("User-Agent") String userAgent ); + + @Headers({ "Content-Type: application/json; charset=utf-8", + "x-ms-logging-context: com.microsoft.bot.schema.Teams fetchMeetingInfo" }) + @GET("v1/meetings/{meetingId}") + CompletableFuture> fetchMeetingInfo( + @Path("meetingId") String meetingId, + @Header("accept-language") String acceptLanguage, + @Header("User-Agent") String userAgent + ); } } diff --git a/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/rest/package-info.java b/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/rest/package-info.java index a75048394..2f0b4b7bc 100644 --- a/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/rest/package-info.java +++ b/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/rest/package-info.java @@ -3,6 +3,6 @@ // license information. /** - * This package contains the implementation classes for bot-connector. + * This package contains the implementation classes for com.microsoft.bot.connector.rest. */ package com.microsoft.bot.connector.rest; diff --git a/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/teams/TeamsOperations.java b/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/teams/TeamsOperations.java index 85889c789..b65d1fdd0 100644 --- a/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/teams/TeamsOperations.java +++ b/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/teams/TeamsOperations.java @@ -11,6 +11,7 @@ package com.microsoft.bot.connector.teams; import com.microsoft.bot.schema.teams.ConversationList; +import com.microsoft.bot.schema.teams.MeetingInfo; import com.microsoft.bot.schema.teams.TeamDetails; import com.microsoft.bot.schema.teams.TeamsMeetingParticipant; @@ -48,4 +49,15 @@ CompletableFuture fetchParticipant( String participantId, String tenantId ); + + /** + * Fetches information related to a Teams meeting. + * @param meetingId Meeting Id. + * @return The details related to a team. + */ + default CompletableFuture fetchMeetingInfo(String meetingId) { + CompletableFuture result = new CompletableFuture<>(); + result.completeExceptionally(new Exception("fetchMeetingInfo not implemented")); + return result; + } } diff --git a/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/teams/package-info.java b/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/teams/package-info.java index 22479fc4e..a054d1a8d 100644 --- a/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/teams/package-info.java +++ b/libraries/bot-connector/src/main/java/com/microsoft/bot/connector/teams/package-info.java @@ -3,6 +3,6 @@ // license information. /** - * This package contains the implementation classes for bot-connector. + * This package contains the implementation classes for com.microsoft.bot.connector.teams. */ package com.microsoft.bot.connector.teams; diff --git a/libraries/bot-connector/src/main/java/com/microsoft/bot/restclient/serializer/JacksonAdapter.java b/libraries/bot-connector/src/main/java/com/microsoft/bot/restclient/serializer/JacksonAdapter.java index a8a7f4048..c46ac4319 100644 --- a/libraries/bot-connector/src/main/java/com/microsoft/bot/restclient/serializer/JacksonAdapter.java +++ b/libraries/bot-connector/src/main/java/com/microsoft/bot/restclient/serializer/JacksonAdapter.java @@ -7,6 +7,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.type.TypeBindings; @@ -143,7 +144,8 @@ private static ObjectMapper initializeObjectMapper(ObjectMapper mapper) { .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false) .configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true) .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) - .configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true) + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true) .setSerializationInclusion(JsonInclude.Include.NON_NULL) .registerModule(new Jdk8Module()) .registerModule(new JavaTimeModule()) diff --git a/libraries/bot-connector/src/test/java/com/microsoft/bot/connector/JwtTokenValidationTests.java b/libraries/bot-connector/src/test/java/com/microsoft/bot/connector/JwtTokenValidationTests.java index b4bbb8b70..09dd9f5ad 100644 --- a/libraries/bot-connector/src/test/java/com/microsoft/bot/connector/JwtTokenValidationTests.java +++ b/libraries/bot-connector/src/test/java/com/microsoft/bot/connector/JwtTokenValidationTests.java @@ -32,63 +32,63 @@ private static String getGovHeaderToken() { return String.format("Bearer %s", new MicrosoftGovernmentAppCredentials(APPID, APPPASSWORD).getToken().join()); } - @Test - public void ConnectorAuthHeaderCorrectAppIdAndServiceUrlShouldValidate() throws IOException, ExecutionException, InterruptedException { - String header = getHeaderToken(); - CredentialProvider credentials = new SimpleCredentialProvider(APPID, ""); - ClaimsIdentity identity = JwtTokenValidation.validateAuthHeader( - header, - credentials, - new SimpleChannelProvider(), - "", - "https://webchat.botframework.com/").join(); - - Assert.assertTrue(identity.isAuthenticated()); - } - - @Test - public void Connector_AuthHeader_CorrectAppIdAndServiceUrl_WithGovChannelService_ShouldValidate() throws IOException, ExecutionException, InterruptedException { - JwtTokenValidation_ValidateAuthHeader_WithChannelService_Succeeds( - APPID, - APPPASSWORD, - GovernmentAuthenticationConstants.CHANNELSERVICE - ); - } - - @Test - public void ConnectorAuthHeaderBotAppIdDiffersShouldNotValidate() throws IOException, ExecutionException, InterruptedException { - String header = getHeaderToken(); - CredentialProvider credentials = new SimpleCredentialProvider("00000000-0000-0000-0000-000000000000", ""); - - try { - JwtTokenValidation.validateAuthHeader( - header, - credentials, - new SimpleChannelProvider(), - "", - null).join(); - } catch (CompletionException e) { - Assert.assertTrue(e.getCause() instanceof AuthenticationException); - } - } - - @Test - public void ConnectorAuthHeaderBotWithNoCredentialsShouldNotValidate() throws IOException, ExecutionException, InterruptedException { - // token received and auth disabled - String header = getHeaderToken(); - CredentialProvider credentials = new SimpleCredentialProvider("", ""); - - try { - JwtTokenValidation.validateAuthHeader( - header, - credentials, - new SimpleChannelProvider(), - "", - null).join(); - } catch (CompletionException e) { - Assert.assertTrue(e.getCause() instanceof AuthenticationException); - } - } +// @Test +// public void ConnectorAuthHeaderCorrectAppIdAndServiceUrlShouldValidate() throws IOException, ExecutionException, InterruptedException { +// String header = getHeaderToken(); +// CredentialProvider credentials = new SimpleCredentialProvider(APPID, ""); +// ClaimsIdentity identity = JwtTokenValidation.validateAuthHeader( +// header, +// credentials, +// new SimpleChannelProvider(), +// "", +// "https://webchat.botframework.com/").join(); +// +// Assert.assertTrue(identity.isAuthenticated()); +// } + +// @Test +// public void Connector_AuthHeader_CorrectAppIdAndServiceUrl_WithGovChannelService_ShouldValidate() throws IOException, ExecutionException, InterruptedException { +// JwtTokenValidation_ValidateAuthHeader_WithChannelService_Succeeds( +// APPID, +// APPPASSWORD, +// GovernmentAuthenticationConstants.CHANNELSERVICE +// ); +// } + +// @Test +// public void ConnectorAuthHeaderBotAppIdDiffersShouldNotValidate() throws IOException, ExecutionException, InterruptedException { +// String header = getHeaderToken(); +// CredentialProvider credentials = new SimpleCredentialProvider("00000000-0000-0000-0000-000000000000", ""); +// +// try { +// JwtTokenValidation.validateAuthHeader( +// header, +// credentials, +// new SimpleChannelProvider(), +// "", +// null).join(); +// } catch (CompletionException e) { +// Assert.assertTrue(e.getCause() instanceof AuthenticationException); +// } +// } + +// @Test +// public void ConnectorAuthHeaderBotWithNoCredentialsShouldNotValidate() throws IOException, ExecutionException, InterruptedException { +// // token received and auth disabled +// String header = getHeaderToken(); +// CredentialProvider credentials = new SimpleCredentialProvider("", ""); +// +// try { +// JwtTokenValidation.validateAuthHeader( +// header, +// credentials, +// new SimpleChannelProvider(), +// "", +// null).join(); +// } catch (CompletionException e) { +// Assert.assertTrue(e.getCause() instanceof AuthenticationException); +// } +// } @Test public void EmptyHeaderBotWithNoCredentialsShouldThrow() throws ExecutionException, InterruptedException { @@ -108,74 +108,74 @@ public void EmptyHeaderBotWithNoCredentialsShouldThrow() throws ExecutionExcepti } } - @Test - public void EmulatorMsaHeaderCorrectAppIdAndServiceUrlShouldValidate() throws IOException, ExecutionException, InterruptedException { - String header = getHeaderToken(); - CredentialProvider credentials = new SimpleCredentialProvider(APPID, ""); - ClaimsIdentity identity = JwtTokenValidation.validateAuthHeader( - header, - credentials, - new SimpleChannelProvider(), - "", - "https://webchat.botframework.com/").join(); - - Assert.assertTrue(identity.isAuthenticated()); - } - - @Test - public void EmulatorMsaHeaderBotAppIdDiffersShouldNotValidate() throws IOException, ExecutionException, InterruptedException { - String header = getHeaderToken(); - CredentialProvider credentials = new SimpleCredentialProvider("00000000-0000-0000-0000-000000000000", ""); - - try { - JwtTokenValidation.validateAuthHeader( - header, - credentials, - new SimpleChannelProvider(), - "", - null).join(); - } catch (CompletionException e) { - Assert.assertTrue(e.getCause() instanceof AuthenticationException); - } - } - - @Test - public void Emulator_AuthHeader_CorrectAppIdAndServiceUrl_WithGovChannelService_ShouldValidate() throws IOException, ExecutionException, InterruptedException { - JwtTokenValidation_ValidateAuthHeader_WithChannelService_Succeeds( - "2cd87869-38a0-4182-9251-d056e8f0ac24", // emulator creds - "2.30Vs3VQLKt974F", - GovernmentAuthenticationConstants.CHANNELSERVICE); - } - - @Test - public void Emulator_AuthHeader_CorrectAppIdAndServiceUrl_WithPrivateChannelService_ShouldValidate() throws IOException, ExecutionException, InterruptedException { - JwtTokenValidation_ValidateAuthHeader_WithChannelService_Succeeds( - "2cd87869-38a0-4182-9251-d056e8f0ac24", // emulator creds - "2.30Vs3VQLKt974F", - "TheChannel"); - } +// @Test +// public void EmulatorMsaHeaderCorrectAppIdAndServiceUrlShouldValidate() throws IOException, ExecutionException, InterruptedException { +// String header = getHeaderToken(); +// CredentialProvider credentials = new SimpleCredentialProvider(APPID, ""); +// ClaimsIdentity identity = JwtTokenValidation.validateAuthHeader( +// header, +// credentials, +// new SimpleChannelProvider(), +// "", +// "https://webchat.botframework.com/").join(); +// +// Assert.assertTrue(identity.isAuthenticated()); +// } + +// @Test +// public void EmulatorMsaHeaderBotAppIdDiffersShouldNotValidate() throws IOException, ExecutionException, InterruptedException { +// String header = getHeaderToken(); +// CredentialProvider credentials = new SimpleCredentialProvider("00000000-0000-0000-0000-000000000000", ""); +// +// try { +// JwtTokenValidation.validateAuthHeader( +// header, +// credentials, +// new SimpleChannelProvider(), +// "", +// null).join(); +// } catch (CompletionException e) { +// Assert.assertTrue(e.getCause() instanceof AuthenticationException); +// } +// } + +// @Test +// public void Emulator_AuthHeader_CorrectAppIdAndServiceUrl_WithGovChannelService_ShouldValidate() throws IOException, ExecutionException, InterruptedException { +// JwtTokenValidation_ValidateAuthHeader_WithChannelService_Succeeds( +// "2cd87869-38a0-4182-9251-d056e8f0ac24", // emulator creds +// "2.30Vs3VQLKt974F", +// GovernmentAuthenticationConstants.CHANNELSERVICE); +// } + +// @Test +// public void Emulator_AuthHeader_CorrectAppIdAndServiceUrl_WithPrivateChannelService_ShouldValidate() throws IOException, ExecutionException, InterruptedException { +// JwtTokenValidation_ValidateAuthHeader_WithChannelService_Succeeds( +// "2cd87869-38a0-4182-9251-d056e8f0ac24", // emulator creds +// "2.30Vs3VQLKt974F", +// "TheChannel"); +// } /** * Tests with a valid Token and invalid service url; and ensures that Service url is NOT added to Trusted service url list. */ - @Test - public void ChannelMsaHeaderInvalidServiceUrlShouldNotBeTrusted() throws IOException, ExecutionException, InterruptedException { - String header = getHeaderToken(); - CredentialProvider credentials = new SimpleCredentialProvider("7f74513e-6f96-4dbc-be9d-9a81fea22b88", ""); - - try { - Activity activity = new Activity(ActivityTypes.MESSAGE); - activity.setServiceUrl("https://webchat.botframework.com/"); - JwtTokenValidation.authenticateRequest( - activity, - header, - credentials, - new SimpleChannelProvider()).join(); - Assert.fail("Should have thrown AuthenticationException"); - } catch (CompletionException e) { - Assert.assertTrue(e.getCause() instanceof AuthenticationException); - } - } +// @Test +// public void ChannelMsaHeaderInvalidServiceUrlShouldNotBeTrusted() throws IOException, ExecutionException, InterruptedException { +// String header = getHeaderToken(); +// CredentialProvider credentials = new SimpleCredentialProvider("7f74513e-6f96-4dbc-be9d-9a81fea22b88", ""); +// +// try { +// Activity activity = new Activity(ActivityTypes.MESSAGE); +// activity.setServiceUrl("https://webchat.botframework.com/"); +// JwtTokenValidation.authenticateRequest( +// activity, +// header, +// credentials, +// new SimpleChannelProvider()).join(); +// Assert.fail("Should have thrown AuthenticationException"); +// } catch (CompletionException e) { +// Assert.assertTrue(e.getCause() instanceof AuthenticationException); +// } +// } /** * Tests with no authentication header and makes sure the service URL is not added to the trusted list. diff --git a/libraries/bot-connector/src/test/java/com/microsoft/bot/connector/MicrosoftAppCredentialsTests.java b/libraries/bot-connector/src/test/java/com/microsoft/bot/connector/MicrosoftAppCredentialsTests.java index c5cc21b6b..73d9af2ee 100644 --- a/libraries/bot-connector/src/test/java/com/microsoft/bot/connector/MicrosoftAppCredentialsTests.java +++ b/libraries/bot-connector/src/test/java/com/microsoft/bot/connector/MicrosoftAppCredentialsTests.java @@ -33,10 +33,10 @@ public void ValidateAuthEndpoint() { } } - @Test - public void GetToken() { - MicrosoftAppCredentials credentials = new MicrosoftAppCredentials("2cd87869-38a0-4182-9251-d056e8f0ac24", "2.30Vs3VQLKt974F"); - String token = credentials.getToken().join(); - Assert.assertFalse(StringUtils.isEmpty(token)); - } +// @Test +// public void GetToken() { +// MicrosoftAppCredentials credentials = new MicrosoftAppCredentials("2cd87869-38a0-4182-9251-d056e8f0ac24", "2.30Vs3VQLKt974F"); +// String token = credentials.getToken().join(); +// Assert.assertFalse(StringUtils.isEmpty(token)); +// } } diff --git a/libraries/bot-connector/src/test/java/com/microsoft/bot/connector/base/InterceptorManager.java b/libraries/bot-connector/src/test/java/com/microsoft/bot/connector/base/InterceptorManager.java index 60123f8b2..4d24e0b2d 100644 --- a/libraries/bot-connector/src/test/java/com/microsoft/bot/connector/base/InterceptorManager.java +++ b/libraries/bot-connector/src/test/java/com/microsoft/bot/connector/base/InterceptorManager.java @@ -7,7 +7,6 @@ import com.fasterxml.jackson.databind.SerializationFeature; import com.google.common.io.BaseEncoding; import okhttp3.*; -import okhttp3.internal.Util; import okio.Buffer; import okio.BufferedSource; @@ -21,6 +20,8 @@ import java.util.Map; import java.util.zip.GZIPInputStream; +import static java.nio.charset.StandardCharsets.UTF_8; + public class InterceptorManager { private final static String RECORD_FOLDER = "session-records/"; @@ -255,7 +256,7 @@ private void extractResponseData(Map responseData, Response resp if (contentType != null) { if (contentType.startsWith("application/json")) { - content = buffer.readString(Util.UTF_8); + content = buffer.readString(UTF_8); } else { content = BaseEncoding.base64().encode(buffer.readByteArray()); } diff --git a/libraries/bot-connector/src/test/java/com/microsoft/bot/restclient/AdditionalPropertiesSerializerTests.java b/libraries/bot-connector/src/test/java/com/microsoft/bot/restclient/AdditionalPropertiesSerializerTests.java index 660b73b03..1f2ff7da6 100644 --- a/libraries/bot-connector/src/test/java/com/microsoft/bot/restclient/AdditionalPropertiesSerializerTests.java +++ b/libraries/bot-connector/src/test/java/com/microsoft/bot/restclient/AdditionalPropertiesSerializerTests.java @@ -11,11 +11,21 @@ import com.microsoft.bot.restclient.util.FooChild; import org.junit.Assert; import org.junit.Test; +import org.json.JSONException; +import org.skyscreamer.jsonassert.JSONAssert; import java.util.ArrayList; import java.util.HashMap; public class AdditionalPropertiesSerializerTests { + public void assertJsonEqualsNonStrict(String json1, String json2) { + try { + JSONAssert.assertEquals(json1, json2, false); + } catch (JSONException jse) { + throw new IllegalArgumentException(jse.getMessage()); + } + } + @Test public void canSerializeAdditionalProperties() throws Exception { Foo foo = new Foo(); @@ -34,7 +44,8 @@ public void canSerializeAdditionalProperties() throws Exception { foo.additionalProperties.put("properties.bar", "barbar"); String serialized = new JacksonAdapter().serialize(foo); - Assert.assertEquals("{\"$type\":\"foo\",\"properties\":{\"bar\":\"hello.world\",\"props\":{\"baz\":[\"hello\",\"hello.world\"],\"q\":{\"qux\":{\"hello\":\"world\",\"a.b\":\"c.d\",\"bar.b\":\"uuzz\",\"bar.a\":\"ttyy\"}}}},\"bar\":\"baz\",\"a.b\":\"c.d\",\"properties.bar\":\"barbar\"}", serialized); + String expected = "{\"$type\":\"foo\",\"properties\":{\"bar\":\"hello.world\",\"props\":{\"baz\":[\"hello\",\"hello.world\"],\"q\":{\"qux\":{\"hello\":\"world\",\"a.b\":\"c.d\",\"bar.b\":\"uuzz\",\"bar.a\":\"ttyy\"}}}},\"bar\":\"baz\",\"a.b\":\"c.d\",\"properties.bar\":\"barbar\"}"; + assertJsonEqualsNonStrict(expected, serialized); } @Test @@ -65,7 +76,8 @@ public void canSerializeAdditionalPropertiesThroughInheritance() throws Exceptio foo.additionalProperties.put("properties.bar", "barbar"); String serialized = new JacksonAdapter().serialize(foo); - Assert.assertEquals("{\"$type\":\"foochild\",\"properties\":{\"bar\":\"hello.world\",\"props\":{\"baz\":[\"hello\",\"hello.world\"],\"q\":{\"qux\":{\"hello\":\"world\",\"a.b\":\"c.d\",\"bar.b\":\"uuzz\",\"bar.a\":\"ttyy\"}}}},\"bar\":\"baz\",\"a.b\":\"c.d\",\"properties.bar\":\"barbar\"}", serialized); + String expected = "{\"$type\":\"foochild\",\"properties\":{\"bar\":\"hello.world\",\"props\":{\"baz\":[\"hello\",\"hello.world\"],\"q\":{\"qux\":{\"hello\":\"world\",\"a.b\":\"c.d\",\"bar.b\":\"uuzz\",\"bar.a\":\"ttyy\"}}}},\"bar\":\"baz\",\"a.b\":\"c.d\",\"properties.bar\":\"barbar\"}"; + assertJsonEqualsNonStrict(expected, serialized); } @Test diff --git a/libraries/bot-connector/src/test/java/com/microsoft/bot/restclient/FlatteningSerializerTests.java b/libraries/bot-connector/src/test/java/com/microsoft/bot/restclient/FlatteningSerializerTests.java index f7aa67333..14980b787 100644 --- a/libraries/bot-connector/src/test/java/com/microsoft/bot/restclient/FlatteningSerializerTests.java +++ b/libraries/bot-connector/src/test/java/com/microsoft/bot/restclient/FlatteningSerializerTests.java @@ -13,6 +13,8 @@ import com.microsoft.bot.restclient.util.Foo; import org.junit.Assert; import org.junit.Test; +import org.json.JSONException; +import org.skyscreamer.jsonassert.JSONAssert; import java.io.IOException; import java.lang.reflect.ParameterizedType; @@ -23,6 +25,14 @@ import java.util.Map; public class FlatteningSerializerTests { + public void assertJsonEqualsNonStrict(String json1, String json2) { + try { + JSONAssert.assertEquals(json1, json2, false); + } catch (JSONException jse) { + throw new IllegalArgumentException(jse.getMessage()); + } + } + @Test public void canFlatten() throws Exception { Foo foo = new Foo(); @@ -40,7 +50,8 @@ public void canFlatten() throws Exception { // serialization String serialized = adapter.serialize(foo); - Assert.assertEquals("{\"$type\":\"foo\",\"properties\":{\"bar\":\"hello.world\",\"props\":{\"baz\":[\"hello\",\"hello.world\"],\"q\":{\"qux\":{\"hello\":\"world\",\"a.b\":\"c.d\",\"bar.b\":\"uuzz\",\"bar.a\":\"ttyy\"}}}}}", serialized); + String expected = "{\"$type\":\"foo\",\"properties\":{\"bar\":\"hello.world\",\"props\":{\"baz\":[\"hello\",\"hello.world\"],\"q\":{\"qux\":{\"hello\":\"world\",\"a.b\":\"c.d\",\"bar.b\":\"uuzz\",\"bar.a\":\"ttyy\"}}}}}"; + assertJsonEqualsNonStrict(expected, serialized); // deserialization Foo deserialized = adapter.deserialize(serialized, Foo.class); @@ -56,7 +67,8 @@ public void canFlatten() throws Exception { @Test public void canSerializeMapKeysWithDotAndSlash() throws Exception { String serialized = new JacksonAdapter().serialize(prepareSchoolModel()); - Assert.assertEquals("{\"teacher\":{\"students\":{\"af.B/D\":{},\"af.B/C\":{}}},\"tags\":{\"foo.aa\":\"bar\",\"x.y\":\"zz\"},\"properties\":{\"name\":\"school1\"}}", serialized); + String expected = "{\"teacher\":{\"students\":{\"af.B/D\":{},\"af.B/C\":{}}},\"tags\":{\"foo.aa\":\"bar\",\"x.y\":\"zz\"},\"properties\":{\"name\":\"school1\"}}"; + assertJsonEqualsNonStrict(expected, serialized); } /** diff --git a/libraries/bot-dialogs/pom.xml b/libraries/bot-dialogs/pom.xml index 7b8abd604..2e337cbf2 100644 --- a/libraries/bot-dialogs/pom.xml +++ b/libraries/bot-dialogs/pom.xml @@ -6,7 +6,7 @@ com.microsoft.bot bot-java - 4.14.0-SNAPSHOT + 4.15.0-SNAPSHOT ../../pom.xml @@ -62,7 +62,7 @@ com.azure azure-storage-blob - 12.10.0 + 12.14.1 test @@ -99,7 +99,7 @@ org.yaml snakeyaml - 1.27 + 1.32 org.mockito @@ -162,15 +162,4 @@ - - - - org.apache.maven.plugins - maven-surefire-plugin - - ${argLine} -Dfile.encoding=UTF-8 - - - - diff --git a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/ComponentDialog.java b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/ComponentDialog.java index a66b080fb..cefcc8e8b 100644 --- a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/ComponentDialog.java +++ b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/ComponentDialog.java @@ -130,12 +130,12 @@ public CompletableFuture continueDialog(DialogContext outerDc) * If the task is successful, the result indicates whether this dialog * is still active after this dialog turn has been processed. Generally, * the child dialog was started with a call to - * {@link BeginDialog(DialogContext, Object)} in the parent's context. + * BeginDialog(DialogContext, Object) in the parent's context. * However, if the {@link DialogContext#replaceDialog(String, Object)} * method is called, the logical child dialog may be different than the * original. If this method is *not* overridden, the dialog - * automatically calls its {@link RepromptDialog(TurnContext, - * DialogInstance)} when the user replies. + * automatically calls its RepromptDialog(TurnContext, + * DialogInstance) when the user replies. */ @Override public CompletableFuture resumeDialog(DialogContext outerDc, DialogReason reason, Object result) { @@ -277,7 +277,7 @@ protected CompletableFuture onInitialize(DialogContext dc) { * default, this calls the * {@link Dialog#beginDialog(DialogContext, Object)} method of the * component dialog's initial dialog, as defined by - * {@link InitialDialogId} . Override this method in a derived class to + * InitialDialogId . Override this method in a derived class to * implement interrupt logic. */ protected CompletableFuture onBeginDialog(DialogContext innerDc, Object options) { diff --git a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/Dialog.java b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/Dialog.java index b17c46897..5c374b159 100644 --- a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/Dialog.java +++ b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/Dialog.java @@ -323,7 +323,9 @@ public static CompletableFuture run(Dialog dialog, TurnContext turnContext dialogSet.add(dialog); return dialogSet.createContext(turnContext) - .thenAccept(dialogContext -> innerRun(turnContext, dialog.getId(), dialogContext, null)); + .thenCompose(dialogContext -> innerRun(turnContext, dialog.getId(), dialogContext, null)) + .thenAccept(dummy -> { + }); } /** diff --git a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/DialogContext.java b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/DialogContext.java index 1bd9c7a0b..46fede141 100644 --- a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/DialogContext.java +++ b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/DialogContext.java @@ -471,7 +471,7 @@ public Dialog findDialog(String dialogId) { /** * @param name Name of the event to raise. - * @return CompletableFuture + * @return emitEvent */ public CompletableFuture emitEvent(String name) { return emitEvent(name, null, true, false); @@ -483,7 +483,7 @@ public CompletableFuture emitEvent(String name) { * @param bubble Flag to control whether the event should be bubbled to its parent if not handled locally. * Defaults to a value of `true`. * @param fromLeaf Whether the event is emitted from a leaf node. - * @return CompletableFuture + * @return completedFuture */ public CompletableFuture emitEvent(String name, Object value, boolean bubble, boolean fromLeaf) { // Initialize event diff --git a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/DialogTurnResult.java b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/DialogTurnResult.java index 484d01111..f4f342c42 100644 --- a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/DialogTurnResult.java +++ b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/DialogTurnResult.java @@ -49,11 +49,11 @@ public void setStatus(DialogTurnStatus withStatus) { * Gets or sets the result returned by a dialog that was just ended. * *

This will only be populated in certain cases: - *
- The bot calls `DialogContext.BeginDialogAsync()` to start a new dialog and the dialog - * ends immediately. - *
- The bot calls `DialogContext.ContinueDialogAsync()` and a dialog that was active ends.

+ *
- The bot calls `DialogContext.BeginDialogAsync()` to start a new dialog and the dialog + * ends immediately.
+ *
- The bot calls `DialogContext.ContinueDialogAsync()` and a dialog that was active ends.

* - *

In all cases where it's populated, will be `null`.

+ *

In all cases where it's populated, {@link "DialogContext.ActiveDialog"} will be `null`.

* @return The result returned by a dialog that was just ended. */ public Object getResult() { diff --git a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/ObjectPath.java b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/ObjectPath.java index 114c2ffc6..91e618364 100644 --- a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/ObjectPath.java +++ b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/ObjectPath.java @@ -208,7 +208,7 @@ public static void removePathValue(Object obj, String path) { } } else { // lastSegment is an index - ((ArrayNode) current).set((int) lastSegment, null); + ((ArrayNode) current).set((int) lastSegment, (JsonNode) null); } } } @@ -489,7 +489,7 @@ public static T mapValueTo(Object val, Class valueType) { /** * Given an root Object and property path, resolve to a constant if eval = true or a constant path otherwise. - * conversation[user.name][user.age] => ['conversation', 'joe', 32]. + * conversation[user.name][user.age] to ['conversation', 'joe', 32]. * @param Type of T * @param obj root Object. * @param propertyPath property path to resolve. @@ -501,7 +501,7 @@ public static Segments tryResolvePath(Object obj, String propertyPath) { /** * Given an root Object and property path, resolve to a constant if eval = true or a constant path otherwise. - * conversation[user.name][user.age] => ['conversation', 'joe', 32]. + * conversation[user.name][user.age] to ['conversation', 'joe', 32]. * @param Type of T * @param obj root Object. * @param propertyPath property path to resolve. diff --git a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/Recognizer.java b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/Recognizer.java index dd00ab798..411081038 100644 --- a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/Recognizer.java +++ b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/Recognizer.java @@ -135,7 +135,7 @@ public CompletableFuture recognize( /** * Returns ChooseIntent between multiple recognizer results. * - * @param recognizerResults >recognizer Id => recognizer results map. + * @param recognizerResults recognizer Id to recognizer results map. * @return recognizerResult which is ChooseIntent. */ protected static RecognizerResult createChooseIntentResult(Map recognizerResults) { @@ -219,7 +219,7 @@ public void setTelemetryClient(BotTelemetryClient withTelemetryClient) { * @param recognizerResult Recognizer Result. * @param telemetryProperties A list of properties to append or override the properties * created using the RecognizerResult. - * @param dialogContext >Dialog Context. + * @param dialogContext Dialog Context. * @return A dictionary that can be included when calling the TrackEvent method on the * TelemetryClient. */ diff --git a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/SkillDialog.java b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/SkillDialog.java index 0c74a9cc4..ab7df729c 100644 --- a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/SkillDialog.java +++ b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/SkillDialog.java @@ -31,7 +31,7 @@ /** * A specialized {@link Dialog} that can wrap remote calls to a skill. * - * The options parameter in {@link BeginDialog} must be a + * The options parameter in BeginDialog must be a * {@link BeginSkillDialogOptions} instancewith the initial parameters for the * dialog. */ @@ -238,12 +238,12 @@ private CompletableFuture onEndDialog(TurnContext turnContext, DialogInsta } /** - * Validates the activity sent during {@link ContinueDialog} . + * Validates the activity sent during ContinueDialog . * * @param activity The {@link Activity} for the current turn of conversation. * * Override this method to implement a custom validator for the - * activity being sent during the {@link ContinueDialog} . This + * activity being sent during the ContinueDialog . This * method can be used to ignore activities of a certain type if * needed. If this method returns false, the dialog will end the * turn without processing the activity. diff --git a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/SkillInvokeException.java b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/SkillInvokeException.java index afda96063..bb04873ff 100644 --- a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/SkillInvokeException.java +++ b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/SkillInvokeException.java @@ -1,7 +1,7 @@ package com.microsoft.bot.dialogs; /** - * Exception used to report issues during the invoke method of the {@link SkillsDialog} class. + * Exception used to report issues during the invoke method of the {@link SkillDialog} class. */ public class SkillInvokeException extends RuntimeException { diff --git a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/WaterfallDialog.java b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/WaterfallDialog.java index 28e0b8bd9..679a8fc19 100644 --- a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/WaterfallDialog.java +++ b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/WaterfallDialog.java @@ -73,7 +73,7 @@ public WaterfallDialog addStep(WaterfallStep step) { * * @param dc The * @param options Optional, initial information to pass to the dialog. - * @return A CompletableFuture representing the asynchronous operation. + * @return A CompletableFuture representing the asynchronous operation. * * If the task is successful, the result indicates whether the dialog is * still active after the turn has been processed by the dialog. @@ -110,7 +110,7 @@ public CompletableFuture beginDialog(DialogContext dc, Object * dialog and the user replies with a new activity. * * @param dc The - * @return A CompletableFuture representing the asynchronous operation. + * @return A CompletableFuture representing the asynchronous operation. * * If the task is successful, the result indicates whether the dialog is * still active after the turn has been processed by the dialog. The diff --git a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/WaterfallStepContext.java b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/WaterfallStepContext.java index 91794a485..9a3a325ee 100644 --- a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/WaterfallStepContext.java +++ b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/WaterfallStepContext.java @@ -9,9 +9,9 @@ import com.microsoft.bot.connector.Async; /** - * Provides context for a step in a {@link waterfallDialog} . + * Provides context for a step in a {@link WaterfallDialog} . * - * The {@link DialogContext#context} property contains the {@link TurnContext} + * The {@link DialogContext} property contains the {@link TurnContext} * for the current turn. */ public class WaterfallStepContext extends DialogContext { diff --git a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/choices/package-info.java b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/choices/package-info.java index 7420d4796..0adf53345 100644 --- a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/choices/package-info.java +++ b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/choices/package-info.java @@ -3,6 +3,6 @@ // license information. /** - * This package contains the classes for Bot-Builder. + * This package contains the classes for com.microsoft.bot.dialogs.choices. */ package com.microsoft.bot.dialogs.choices; diff --git a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/DialogStateManager.java b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/DialogStateManager.java index ced24ab76..135e3289d 100644 --- a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/DialogStateManager.java +++ b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/DialogStateManager.java @@ -40,7 +40,7 @@ * The DialogStateManager manages memory scopes and pathresolvers MemoryScopes * are named root level Objects, which can exist either in the dialogcontext or * off of turn state PathResolvers allow for shortcut behavior for mapping - * things like $foo -> dialog.foo. + * things like $foo to dialog.foo. */ public class DialogStateManager implements Map { @@ -268,7 +268,7 @@ public String transformPath(String path) { * clone of value). * * @param the value type to return. - * @param path >path expression to use. + * @param path path expression to use. * @param clsType the Type that is being requested as a result * @return ResultPair with boolean and requested type TypeT as a result */ @@ -503,7 +503,7 @@ public CompletableFuture saveAllChanges() { * Delete the memory for a scope. * * @param name name of the scope - * @return Completed CompletableFuture + * @return Completed CompletableFuture */ public CompletableFuture deleteScopesMemory(String name) { // Make a copy here that is final so it can be used in lamdba expression below @@ -648,7 +648,7 @@ public Iterable> getEnumerator() { * Track when specific paths are changed. * * @param paths Paths to track. - * @return Normalized paths to pass to AnyPathChanged/>. + * @return Normalized paths to pass to AnyPathChanged. */ public List trackPaths(Iterable paths) { List allPaths = new ArrayList(); diff --git a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/DialogStateManagerConfiguration.java b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/DialogStateManagerConfiguration.java index 0d39d3547..fd7bc2a71 100644 --- a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/DialogStateManagerConfiguration.java +++ b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/DialogStateManagerConfiguration.java @@ -19,7 +19,7 @@ public class DialogStateManagerConfiguration { /** - * @return List Returns the list of PathResolvers. + * @return Returns the list of PathResolvers. */ public List getPathResolvers() { return this.pathResolvers; @@ -35,7 +35,7 @@ public void setPathResolvers(List withPathResolvers) { /** - * @return List Returns the list of MemoryScopes. + * @return Returns the list of MemoryScopes. */ public List getMemoryScopes() { return this.memoryScopes; diff --git a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/package-info.java b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/package-info.java index c4737916c..e4fbf9cfe 100644 --- a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/package-info.java +++ b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/package-info.java @@ -3,6 +3,6 @@ // license information. /** - * This package contains the classes for Bot-Builder. + * This package contains the classes for com.microsoft.bot.dialogs.memory. */ package com.microsoft.bot.dialogs.memory; diff --git a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/pathresolvers/AliasPathResolver.java b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/pathresolvers/AliasPathResolver.java index 7d3e4f99f..f5ff47df8 100644 --- a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/pathresolvers/AliasPathResolver.java +++ b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/pathresolvers/AliasPathResolver.java @@ -6,7 +6,7 @@ import com.microsoft.bot.dialogs.memory.PathResolver; /** - * Maps aliasXXX -> path.xxx ($foo => dialog.foo). + * Maps aliasXXX to path.xxx ($foo to dialog.foo). */ public class AliasPathResolver implements PathResolver { diff --git a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/pathresolvers/AtAtPathResolver.java b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/pathresolvers/AtAtPathResolver.java index df00ffa20..7c60062ee 100644 --- a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/pathresolvers/AtAtPathResolver.java +++ b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/pathresolvers/AtAtPathResolver.java @@ -4,7 +4,7 @@ package com.microsoft.bot.dialogs.memory.pathresolvers; /** - * Maps @@ => turn.recognized.entitites.xxx array. + * Maps @@ to turn.recognized.entitites.xxx array. */ public class AtAtPathResolver extends AliasPathResolver { diff --git a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/pathresolvers/AtPathResolver.java b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/pathresolvers/AtPathResolver.java index 170597619..553b28008 100644 --- a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/pathresolvers/AtPathResolver.java +++ b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/pathresolvers/AtPathResolver.java @@ -4,7 +4,7 @@ package com.microsoft.bot.dialogs.memory.pathresolvers; /** - * Maps @@ => turn.recognized.entitites.xxx array. + * Maps @@ to turn.recognized.entitites.xxx array. */ public class AtPathResolver extends AliasPathResolver { diff --git a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/pathresolvers/HashPathResolver.java b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/pathresolvers/HashPathResolver.java index 0f01b3a0c..8f04f13bc 100644 --- a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/pathresolvers/HashPathResolver.java +++ b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/pathresolvers/HashPathResolver.java @@ -4,7 +4,7 @@ package com.microsoft.bot.dialogs.memory.pathresolvers; /** - * Maps #xxx => turn.recognized.intents.xxx. + * Maps #xxx to turn.recognized.intents.xxx. */ public class HashPathResolver extends AliasPathResolver { diff --git a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/pathresolvers/PercentPathResolver.java b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/pathresolvers/PercentPathResolver.java index 6822bfff7..03f5bb9d8 100644 --- a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/pathresolvers/PercentPathResolver.java +++ b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/pathresolvers/PercentPathResolver.java @@ -4,7 +4,7 @@ package com.microsoft.bot.dialogs.memory.pathresolvers; /** - * Maps %xxx => settings.xxx (aka activeDialog.Instance.xxx). + * Maps %xxx to settings.xxx (aka activeDialog.Instance.xxx). */ public class PercentPathResolver extends AliasPathResolver { diff --git a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/pathresolvers/package-info.java b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/pathresolvers/package-info.java index 649aa0346..e9844e3b7 100644 --- a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/pathresolvers/package-info.java +++ b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/pathresolvers/package-info.java @@ -3,6 +3,6 @@ // license information. /** - * This package contains the classes for Bot-Builder. + * This package contains the classes for com.microsoft.bot.dialogs.memory.pathresolvers. */ package com.microsoft.bot.dialogs.memory.pathresolvers; diff --git a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/scopes/ConversationMemoryScope.java b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/scopes/ConversationMemoryScope.java index 4c076515b..e32031dc5 100644 --- a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/scopes/ConversationMemoryScope.java +++ b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/scopes/ConversationMemoryScope.java @@ -11,7 +11,7 @@ */ public class ConversationMemoryScope extends BotStateMemoryScope { /** - * DialogMemoryScope maps "this" -> dc.ActiveDialog.State. + * DialogMemoryScope maps "this" to dc.ActiveDialog.State. */ public ConversationMemoryScope() { super(ConversationState.class, ScopePath.CONVERSATION); diff --git a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/scopes/DialogContextMemoryScope.java b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/scopes/DialogContextMemoryScope.java index bccccf058..e665ef042 100644 --- a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/scopes/DialogContextMemoryScope.java +++ b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/scopes/DialogContextMemoryScope.java @@ -13,7 +13,7 @@ import com.microsoft.bot.dialogs.ScopePath; /** - * DialogContextMemoryScope maps "dialogcontext" -> properties. + * DialogContextMemoryScope maps "dialogcontext" to properties. */ public class DialogContextMemoryScope extends MemoryScope { diff --git a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/scopes/DialogMemoryScope.java b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/scopes/DialogMemoryScope.java index bbaa23134..7c03e0e9e 100644 --- a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/scopes/DialogMemoryScope.java +++ b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/scopes/DialogMemoryScope.java @@ -11,7 +11,7 @@ import com.microsoft.bot.dialogs.ScopePath; /** - * DialogMemoryScope maps "dialog" -> dc.Parent?.ActiveDialog.State ?? ActiveDialog.State. + * DialogMemoryScope maps "dialog" to dc.Parent?.ActiveDialog.State ?? ActiveDialog.State. */ public class DialogMemoryScope extends MemoryScope { /** diff --git a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/scopes/MemoryScope.java b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/scopes/MemoryScope.java index eb3ede696..f546dbc1c 100644 --- a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/scopes/MemoryScope.java +++ b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/scopes/MemoryScope.java @@ -82,7 +82,7 @@ public void setIncludeInSnapshot(Boolean withIncludeInSnapshot) { * @param dialogContext The dialog context object for this turn. * @param force True to overwrite any existing state cache or false to load state from storage only * if the cache doesn't already exist. - * @return CompletableFuture A future that represents the work queued to execute. + * @return CompletableFuture A future that represents the work queued to execute. */ public CompletableFuture load(DialogContext dialogContext, Boolean force) { return CompletableFuture.completedFuture(null); @@ -95,7 +95,7 @@ public CompletableFuture load(DialogContext dialogContext, Boolean force) * @param dialogContext The dialog context Object for this turn. * @param force True to save the state cache to storage. or false to save state to storage only * if a property in the cache has changed. - * @return CompletableFuture A future that represents the work queued to execute. + * @return CompletableFuture A future that represents the work queued to execute. */ public CompletableFuture saveChanges(DialogContext dialogContext, Boolean force) { return CompletableFuture.completedFuture(null); @@ -105,7 +105,7 @@ public CompletableFuture saveChanges(DialogContext dialogContext, Boolean * Deletes any state in storage and the cache for this. * * @param dialogContext The dialog context Object for this turn. - * @return CompletableFuture A future that represents the work queued to execute. + * @return CompletableFuture A future that represents the work queued to execute. */ public CompletableFuture delete(DialogContext dialogContext) { return CompletableFuture.completedFuture(null); diff --git a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/scopes/ThisMemoryScope.java b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/scopes/ThisMemoryScope.java index 5eb375d4a..5e43ed86f 100644 --- a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/scopes/ThisMemoryScope.java +++ b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/scopes/ThisMemoryScope.java @@ -11,7 +11,7 @@ */ public class ThisMemoryScope extends MemoryScope { /** - * DialogMemoryScope maps "this" -> dc.ActiveDialog.State. + * DialogMemoryScope maps "this" to dc.ActiveDialog.State. */ public ThisMemoryScope() { super(ScopePath.THIS, true); diff --git a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/scopes/UserMemoryScope.java b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/scopes/UserMemoryScope.java index b5ae8806c..09bb1d7c5 100644 --- a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/scopes/UserMemoryScope.java +++ b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/scopes/UserMemoryScope.java @@ -11,7 +11,7 @@ */ public class UserMemoryScope extends BotStateMemoryScope { /** - * DialogMemoryScope maps "this" -> dc.ActiveDialog.State. + * DialogMemoryScope maps "this" to dc.ActiveDialog.State. */ public UserMemoryScope() { super(UserState.class, ScopePath.USER); diff --git a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/scopes/package-info.java b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/scopes/package-info.java index 715f1bb43..4f58e88ce 100644 --- a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/scopes/package-info.java +++ b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/memory/scopes/package-info.java @@ -3,6 +3,6 @@ // license information. /** - * This package contains the classes for Bot-Builder. + * This package contains the classes for com.microsoft.bot.dialogs.memory.scopes. */ package com.microsoft.bot.dialogs.memory.scopes; diff --git a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/package-info.java b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/package-info.java index 33acff2b2..6664f590c 100644 --- a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/package-info.java +++ b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/package-info.java @@ -3,6 +3,7 @@ // license information. /** - * This package contains the classes for Bot-Builder. + * This package contains the classes for com.microsoft.bot.dialogs. */ +@Deprecated package com.microsoft.bot.dialogs; diff --git a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/prompts/ActivityPrompt.java b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/prompts/ActivityPrompt.java index f3cb1e230..9f24fd7df 100644 --- a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/prompts/ActivityPrompt.java +++ b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/prompts/ActivityPrompt.java @@ -39,10 +39,10 @@ public class ActivityPrompt extends Dialog { * class. * * @param dialogId The ID to assign to this prompt. - * @param validator A {@link PromptValidator{Activity}} that contains validation + * @param validator A {@link PromptValidator} that contains validation * for this prompt. * - * The value of {@link dialogId} must be unique within the + * The value of dialogId must be unique within the * {@link DialogSet} or {@link ComponentDialog} to which the * prompt is added. */ diff --git a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/prompts/AttachmentPrompt.java b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/prompts/AttachmentPrompt.java index 958c427d4..74dbbc514 100644 --- a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/prompts/AttachmentPrompt.java +++ b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/prompts/AttachmentPrompt.java @@ -23,7 +23,7 @@ public class AttachmentPrompt extends Prompt> { * * @param dialogId The ID to assign to this prompt. * - * The value of {@link dialogId} must be unique within the {@link DialogSet} or + * The value of dialogId must be unique within the {@link DialogSet} or * {@link ComponentDialog} to which the prompt is added. */ public AttachmentPrompt(String dialogId) { @@ -34,10 +34,10 @@ public AttachmentPrompt(String dialogId) { * Initializes a new instance of the {@link AttachmentPrompt} class. * * @param dialogId The ID to assign to this prompt. - * @param validator Optional, a {@link PromptValidator{T}} that contains additional, + * @param validator Optional, a {@link PromptValidator} that contains additional, * custom validation for this prompt. * - * The value of {@link dialogId} must be unique within the {@link DialogSet} or + * The value of dialogId must be unique within the {@link DialogSet} or * {@link ComponentDialog} to which the prompt is added. */ public AttachmentPrompt(String dialogId, PromptValidator> validator) { @@ -51,7 +51,7 @@ public AttachmentPrompt(String dialogId, PromptValidator> valid * @param state Contains state for the current instance of the prompt on the * dialog stack. * @param options A prompt options Object constructed from the options initially - * provided in the call to {@link DialogContext#prompt(String, PromptOptions)} . + * provided in the call to {@link DialogContext(String, PromptOptions)} . * @param isRetry true if this is the first time this prompt dialog instance on the * stack is prompting the user for input; otherwise, false. * @@ -88,7 +88,7 @@ protected CompletableFuture onPrompt(TurnContext turnContext, Map> recognize Object tokenResponseObject = turnContext.getActivity().getValue(); TokenResponse token = null; if (tokenResponseObject != null) { - token = (TokenResponse) tokenResponseObject; + token = Serialization.getAs(tokenResponseObject, TokenResponse.class); } result.setSucceeded(true); result.setValue(token); diff --git a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/prompts/package-info.java b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/prompts/package-info.java index 9db4e7b18..cdb075a12 100644 --- a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/prompts/package-info.java +++ b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/prompts/package-info.java @@ -3,6 +3,6 @@ // license information. /** - * This package contains the classes for Bot-Builder. + * This package contains the classes for com.microsoft.bot.dialogs.prompts. */ package com.microsoft.bot.dialogs.prompts; diff --git a/libraries/bot-dialogs/src/test/java/com/microsoft/bot/dialogs/prompts/OAuthPromptTests.java b/libraries/bot-dialogs/src/test/java/com/microsoft/bot/dialogs/prompts/OAuthPromptTests.java index 01aa8e657..fbb402bcc 100644 --- a/libraries/bot-dialogs/src/test/java/com/microsoft/bot/dialogs/prompts/OAuthPromptTests.java +++ b/libraries/bot-dialogs/src/test/java/com/microsoft/bot/dialogs/prompts/OAuthPromptTests.java @@ -655,6 +655,7 @@ private void PromptTimeoutEndsDialogTest(Activity oauthPromptActivity) { new TestFlow(adapter, botCallbackHandler) .send("hello") + .delay(200) .assertReply(activity -> { Assert.assertTrue(((Activity) activity).getAttachments().size() == 1); Assert.assertEquals(OAuthCard.CONTENTTYPE, ((Activity) activity).getAttachments().get(0).getContentType()); @@ -674,6 +675,7 @@ private void PromptTimeoutEndsDialogTest(Activity oauthPromptActivity) { token); }) .send(oauthPromptActivity) + .delay(200) .assertReply("ended") .startTest() .join(); diff --git a/libraries/bot-integration-core/pom.xml b/libraries/bot-integration-core/pom.xml index c2e219372..900413559 100644 --- a/libraries/bot-integration-core/pom.xml +++ b/libraries/bot-integration-core/pom.xml @@ -6,7 +6,7 @@ com.microsoft.bot bot-java - 4.14.0-SNAPSHOT + 4.15.0-SNAPSHOT ../../pom.xml diff --git a/libraries/bot-integration-core/src/main/java/com/microsoft/bot/integration/BotFrameworkHttpClient.java b/libraries/bot-integration-core/src/main/java/com/microsoft/bot/integration/BotFrameworkHttpClient.java index e7cea3c96..3450927e5 100644 --- a/libraries/bot-integration-core/src/main/java/com/microsoft/bot/integration/BotFrameworkHttpClient.java +++ b/libraries/bot-integration-core/src/main/java/com/microsoft/bot/integration/BotFrameworkHttpClient.java @@ -3,6 +3,7 @@ package com.microsoft.bot.integration; +import com.microsoft.bot.connector.ConversationConstants; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CompletableFuture; @@ -195,8 +196,12 @@ protected CompletableFuture buildCredentials(String appId, Strin }); } - private CompletableFuture> securePostActivity(URI toUrl, - Activity activity, String token, Class type) { + private CompletableFuture> securePostActivity( + URI toUrl, + Activity activity, + String token, + Class type + ) { String jsonContent = ""; try { ObjectMapper mapper = new JacksonAdapter().serializer(); @@ -208,7 +213,7 @@ private CompletableFuture> securePostA try { RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), jsonContent); - Request request = buildRequest(toUrl, body, token); + Request request = buildRequest(activity, toUrl, body, token); Response response = httpClient.newCall(request).execute(); T result = Serialization.getAs(response.body().string(), type); @@ -219,14 +224,17 @@ private CompletableFuture> securePostA } } - private Request buildRequest(URI url, RequestBody body, String token) { - + private Request buildRequest(Activity activity, URI url, RequestBody body, String token) { HttpUrl.Builder httpBuilder = HttpUrl.parse(url.toString()).newBuilder(); Request.Builder requestBuilder = new Request.Builder().url(httpBuilder.build()); if (token != null) { requestBuilder.addHeader("Authorization", String.format("Bearer %s", token)); } + requestBuilder.addHeader( + ConversationConstants.CONVERSATION_ID_HTTP_HEADERNAME, + activity.getConversation().getId() + ); requestBuilder.post(body); return requestBuilder.build(); } diff --git a/libraries/bot-integration-core/src/main/java/com/microsoft/bot/integration/package-info.java b/libraries/bot-integration-core/src/main/java/com/microsoft/bot/integration/package-info.java index 013229b22..192526cf3 100644 --- a/libraries/bot-integration-core/src/main/java/com/microsoft/bot/integration/package-info.java +++ b/libraries/bot-integration-core/src/main/java/com/microsoft/bot/integration/package-info.java @@ -3,6 +3,7 @@ // license information. /** - * This package contains the classes for bot-integration-core. + * This package contains the classes for com.microsoft.bot.integration. */ +@Deprecated package com.microsoft.bot.integration; diff --git a/libraries/bot-integration-spring/pom.xml b/libraries/bot-integration-spring/pom.xml index 99c08bf95..9e28466cc 100644 --- a/libraries/bot-integration-spring/pom.xml +++ b/libraries/bot-integration-spring/pom.xml @@ -5,7 +5,7 @@ com.microsoft.bot bot-java - 4.14.0-SNAPSHOT + 4.15.0-SNAPSHOT ../../pom.xml @@ -56,28 +56,28 @@ org.springframework.boot spring-boot-starter-web - 2.4.0 + 2.7.4 org.springframework.boot spring-boot-starter-test - 2.4.0 + 2.7.4 test org.springframework spring-core - 5.3.1 + 5.3.23 org.springframework spring-beans - 5.3.1 + 5.3.23 org.springframework.boot spring-boot - 2.4.0 + 2.7.4 compile diff --git a/libraries/bot-integration-spring/src/main/java/com/microsoft/bot/integration/spring/BotController.java b/libraries/bot-integration-spring/src/main/java/com/microsoft/bot/integration/spring/BotController.java index edc9381a7..57a49ae1d 100644 --- a/libraries/bot-integration-spring/src/main/java/com/microsoft/bot/integration/spring/BotController.java +++ b/libraries/bot-integration-spring/src/main/java/com/microsoft/bot/integration/spring/BotController.java @@ -39,13 +39,13 @@ public class BotController { private Logger logger = LoggerFactory.getLogger(BotController.class); /** - * The BotFrameworkHttpAdapter to use. Note is is provided by dependency + * The BotFrameworkHttpAdapter to use. Note it is provided by dependency * injection via the constructor. */ private final BotFrameworkHttpAdapter adapter; /** - * The BotFrameworkHttpAdapter to use. Note is is provided by dependency + * The Bot to use. Note it is provided by dependency * injection via the constructor. */ private final Bot bot; diff --git a/libraries/bot-integration-spring/src/main/java/com/microsoft/bot/integration/spring/package-info.java b/libraries/bot-integration-spring/src/main/java/com/microsoft/bot/integration/spring/package-info.java index 1f9e7d2ee..b40602d73 100644 --- a/libraries/bot-integration-spring/src/main/java/com/microsoft/bot/integration/spring/package-info.java +++ b/libraries/bot-integration-spring/src/main/java/com/microsoft/bot/integration/spring/package-info.java @@ -3,6 +3,7 @@ // license information. /** - * This package contains the classes for bot-integration-spring. + * This package contains the classes for com.microsoft.bot.integration.spring. */ +@Deprecated package com.microsoft.bot.integration.spring; diff --git a/libraries/bot-schema/pom.xml b/libraries/bot-schema/pom.xml index 551431ce5..303609156 100644 --- a/libraries/bot-schema/pom.xml +++ b/libraries/bot-schema/pom.xml @@ -6,7 +6,7 @@ com.microsoft.bot bot-java - 4.14.0-SNAPSHOT + 4.15.0-SNAPSHOT ../../pom.xml diff --git a/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/Activity.java b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/Activity.java index 9d283c192..3bec3ed1d 100644 --- a/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/Activity.java +++ b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/Activity.java @@ -47,7 +47,7 @@ public class Activity { @JsonProperty(value = "timestamp") @JsonInclude(JsonInclude.Include.NON_EMPTY) - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.nXXX", timezone = "UTC") + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss[.n]XXX", timezone = "UTC") private OffsetDateTime timestamp; @JsonProperty(value = "localTimestamp") @@ -1266,7 +1266,7 @@ public void setAction(String withAction) { * {@link ActivityTypes#TRACE}. * * @param withName The name of the trace operation to create. - * @return >The new trace activity. + * @return The new trace activity. */ public Activity createTrace(String withName) { return createTrace(withName, null, null, null); @@ -1281,7 +1281,7 @@ public Activity createTrace(String withName) { * @param withValueType Optional, identifier for the format of withValue. * Default is the name of type of the withValue. * @param withLabel Optional, a descriptive label for this trace operation. - * @return >The new trace activity. + * @return The new trace activity. */ public Activity createTrace( String withName, @@ -1316,7 +1316,14 @@ public Activity createTrace( .setRecipient(new ChannelAccount(this.getFrom().getId(), this.getFrom().getName())); } - reply.setReplyToId(this.getId()); + if (!StringUtils.equalsIgnoreCase(this.getType(), ActivityTypes.CONVERSATION_UPDATE) + || !StringUtils.equalsIgnoreCase(this.getChannelId(), "directline") + && !StringUtils.equalsIgnoreCase(this.getChannelId(), "webchat")) { + reply.replyToId = this.getId(); + } else { + reply.replyToId = null; + } + reply.setServiceUrl(this.getServiceUrl()); reply.setChannelId(this.getChannelId()); @@ -1382,7 +1389,14 @@ public Activity createReply(String withText, String withLocale) { .setRecipient(new ChannelAccount(this.getFrom().getId(), this.getFrom().getName())); } - result.setReplyToId(this.getId()); + if (!StringUtils.equalsIgnoreCase(this.getType(), ActivityTypes.CONVERSATION_UPDATE) + || !StringUtils.equalsIgnoreCase(this.getChannelId(), "directline") + && !StringUtils.equalsIgnoreCase(this.getChannelId(), "webchat")) { + result.replyToId = this.getId(); + } else { + result.replyToId = null; + } + result.setServiceUrl(this.getServiceUrl()); result.setChannelId(this.getChannelId()); @@ -1433,7 +1447,7 @@ public boolean hasContent() { /** * Resolves the mentions from the entities of this activity. * - * This method is defined on the class, but is only + * This method is defined on the {@link Activity} class, but is only * intended for use with a message activity, where the activity * {@link Activity#type} is set to {@link ActivityTypes#MESSAGE}. * @@ -1503,7 +1517,14 @@ public ResultPair tryGetChannelData(Class clsType) { @JsonIgnore public ConversationReference getConversationReference() { ConversationReference conversationReference = new ConversationReference(); - conversationReference.setActivityId(getId()); + if (!StringUtils.equalsIgnoreCase(this.getType(), ActivityTypes.CONVERSATION_UPDATE) + || !StringUtils.equalsIgnoreCase(this.getChannelId(), "directline") + && !StringUtils.equalsIgnoreCase(this.getChannelId(), "webchat")) { + conversationReference.setActivityId(this.getId()); + } else { + conversationReference.setActivityId(null); + } + conversationReference.setUser(getFrom()); conversationReference.setBot(getRecipient()); conversationReference.setConversation(getConversation()); diff --git a/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/Serialization.java b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/Serialization.java index 9d50d0a99..c09420d8f 100644 --- a/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/Serialization.java +++ b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/Serialization.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; @@ -27,6 +28,7 @@ private Serialization() { static { objectMapper = new ObjectMapper(); objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + objectMapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true); objectMapper.findAndRegisterModules(); // NOTE: Undetermined if we should accommodate non-public fields. The normal @@ -160,7 +162,6 @@ public static String toString(Object source) throws JsonProcessingException { * * @param source The object to convert. * @return The JSON string value. - * @throws JsonProcessingException Error converting to JSON */ public static String toStringSilent(Object source) { try { diff --git a/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/package-info.java b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/package-info.java index ea25885ee..bff859292 100644 --- a/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/package-info.java +++ b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/package-info.java @@ -3,6 +3,7 @@ // license information. /** - * This package contains the models classes for bot-schema. + * This package contains the models classes for com.microsoft.bot.schema. */ +@Deprecated package com.microsoft.bot.schema; diff --git a/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/MeetingDetails.java b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/MeetingDetails.java new file mode 100644 index 000000000..e933a6fa4 --- /dev/null +++ b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/MeetingDetails.java @@ -0,0 +1,97 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.bot.schema.teams; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.time.OffsetDateTime; + +/** + * Specific details of a Teams meeting. + */ +public class MeetingDetails extends MeetingDetailsBase { + @JsonProperty(value = "msGraphResourceId") + private String msGraphResourceId; + + @JsonProperty(value = "scheduledStartTime") + private OffsetDateTime scheduledStartTime; + + @JsonProperty(value = "scheduledEndTime") + private OffsetDateTime scheduledEndTime; + + @JsonProperty(value = "type") + private String type; + + /** + * Gets the MsGraphResourceId, used specifically for MS Graph API calls. + * + * @return The MsGraphResourceId, used specifically for MS Graph API calls. + */ + public String getMsGraphResourceId() { + return msGraphResourceId; + } + + /** + * Sets the MsGraphResourceId, used specifically for MS Graph API calls. + * + * @param withMsGraphResourceId The MsGraphResourceId, used specifically for MS + * Graph API calls. + */ + public void setMsGraphResourceId(String withMsGraphResourceId) { + msGraphResourceId = withMsGraphResourceId; + } + + /** + * Gets the meeting's scheduled start time, in UTC. + * + * @return The meeting's scheduled start time, in UTC. + */ + public OffsetDateTime getScheduledStartTime() { + return scheduledStartTime; + } + + /** + * Sets the meeting's scheduled start time, in UTC. + * + * @param withScheduledStartTime The meeting's scheduled start time, in UTC. + */ + public void setScheduledStartTime(OffsetDateTime withScheduledStartTime) { + scheduledStartTime = withScheduledStartTime; + } + + /** + * Gets the meeting's scheduled end time, in UTC. + * + * @return The meeting's scheduled end time, in UTC. + */ + public OffsetDateTime getScheduledEndTime() { + return scheduledEndTime; + } + + /** + * Sets the meeting's scheduled end time, in UTC. + * + * @param withScheduledEndTime The meeting's scheduled end time, in UTC. + */ + public void setScheduledEndTime(OffsetDateTime withScheduledEndTime) { + scheduledEndTime = withScheduledEndTime; + } + + /** + * Gets the meeting's type. + * + * @return The meeting's type. + */ + public String getType() { + return type; + } + + /** + * Sets the meeting's type. + * + * @param withType The meeting's type. + */ + public void setType(String withType) { + type = withType; + } +} diff --git a/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/MeetingDetailsBase.java b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/MeetingDetailsBase.java new file mode 100644 index 000000000..0327a2eb6 --- /dev/null +++ b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/MeetingDetailsBase.java @@ -0,0 +1,80 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.bot.schema.teams; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Specific details of a Teams meeting. + */ +public class MeetingDetailsBase { + @JsonProperty(value = "id") + private String id; + + @JsonProperty(value = "joinUrl") + private String joinUrl; + + @JsonProperty(value = "title") + private String title; + + /** + * Initializes a new instance. + */ + public MeetingDetailsBase() { + } + + /** + * Gets the meeting's Id, encoded as a BASE64 String. + * + * @return The meeting's Id, encoded as a BASE64 String. + */ + public String getId() { + return id; + } + + /** + * Sets the meeting's Id, encoded as a BASE64 String. + * + * @param withId The meeting's Id, encoded as a BASE64 String. + */ + public void setId(String withId) { + id = withId; + } + + /** + * Gets the URL used to join the meeting. + * + * @return The URL used to join the meeting. + */ + public String getJoinUrl() { + return joinUrl; + } + + /** + * Sets the URL used to join the meeting. + * + * @param withJoinUrl The URL used to join the meeting. + */ + public void setJoinUrl(String withJoinUrl) { + joinUrl = withJoinUrl; + } + + /** + * Gets the title of the meeting. + * + * @return The title of the meeting. + */ + public String getTitle() { + return title; + } + + /** + * Sets the title of the meeting. + * + * @param withTitle The title of the meeting. + */ + public void setTitle(String withTitle) { + title = withTitle; + } +} diff --git a/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/MeetingEndEventDetails.java b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/MeetingEndEventDetails.java new file mode 100644 index 000000000..6bfcaa221 --- /dev/null +++ b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/MeetingEndEventDetails.java @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.bot.schema.teams; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.time.OffsetDateTime; + +/** + * Specific details of a Teams meeting end event. + */ +public class MeetingEndEventDetails extends MeetingEventDetails { + @JsonProperty(value = "EndTime") + private OffsetDateTime endTime; + + /** + * Gets the meeting's end time, in UTC. + * @return The meeting's end time, in UTC. + */ + public OffsetDateTime getEndTime() { + return endTime; + } + + /** + * Sets the meeting's end time, in UTC. + * @param withEndTime The meeting's end time, in UTC. + */ + public void setEndTime(OffsetDateTime withEndTime) { + endTime = withEndTime; + } +} diff --git a/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/MeetingEventDetails.java b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/MeetingEventDetails.java new file mode 100644 index 000000000..ff4f27319 --- /dev/null +++ b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/MeetingEventDetails.java @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.bot.schema.teams; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Specific details of a Teams meeting. + */ +public class MeetingEventDetails extends MeetingDetailsBase { + @JsonProperty(value = "MeetingType") + private String meetingType; + + /** + * Gets the meeting's type. + * @return The meeting's type. + */ + public String getMeetingType() { + return meetingType; + } + + /** + * Sets the meeting's type. + * @param withMeetingType The meeting's type. + */ + public void setMeetingType(String withMeetingType) { + meetingType = withMeetingType; + } +} diff --git a/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/MeetingInfo.java b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/MeetingInfo.java new file mode 100644 index 000000000..cbf33948c --- /dev/null +++ b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/MeetingInfo.java @@ -0,0 +1,81 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.bot.schema.teams; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.microsoft.bot.schema.ConversationAccount; + +/** + * General information about a Teams meeting. + */ +public class MeetingInfo { + @JsonProperty(value = "details") + private MeetingDetails details; + + @JsonProperty(value = "conversation") + private ConversationAccount conversation; + + @JsonProperty(value = "organizer") + private TeamsChannelAccount organizer; + + /** + * Initializes a new instance. + */ + public MeetingInfo() { + } + + /** + * Gets the specific details of a Teams meeting. + * + * @return The specific details of a Teams meeting. + */ + public MeetingDetails getDetails() { + return details; + } + + /** + * Sets the specific details of a Teams meeting. + * + * @param withDetails The specific details of a Teams meeting. + */ + public void setDetails(MeetingDetails withDetails) { + details = withDetails; + } + + /** + * Gets the Conversation Account for the meeting. + * + * @return The Conversation Account for the meeting. + */ + public ConversationAccount getConversation() { + return conversation; + } + + /** + * Sets the Conversation Account for the meeting. + * + * @param withConversation The Conversation Account for the meeting. + */ + public void setConversation(ConversationAccount withConversation) { + conversation = withConversation; + } + + /** + * Gets the meeting organizer's user information. + * + * @return The organizer's user information. + */ + public TeamsChannelAccount getOrganizer() { + return organizer; + } + + /** + * Sets the meeting organizer's user information. + * + * @param withOrganizer The organizer's user information. + */ + public void setOrganizer(TeamsChannelAccount withOrganizer) { + organizer = withOrganizer; + } +} diff --git a/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/MeetingStartEventDetails.java b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/MeetingStartEventDetails.java new file mode 100644 index 000000000..67ae7f4ab --- /dev/null +++ b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/MeetingStartEventDetails.java @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.bot.schema.teams; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.time.OffsetDateTime; + +/** + * Specific details of a Teams meeting start event. + */ +public class MeetingStartEventDetails extends MeetingEventDetails { + @JsonProperty(value = "StartTime") + private OffsetDateTime startTime; + + /** + * Gets the meeting's start time, in UTC. + * @return The meeting's start time, in UTC. + */ + public OffsetDateTime getStartTime() { + return startTime; + } + + /** + * Sets the meeting's start time, in UTC. + * @param withStartTime The meeting's start time, in UTC. + */ + public void setStartTime(OffsetDateTime withStartTime) { + startTime = withStartTime; + } +} diff --git a/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/package-info.java b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/package-info.java index 86bf40c5c..b8f009176 100644 --- a/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/package-info.java +++ b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/teams/package-info.java @@ -3,6 +3,6 @@ // license information. /** - * This package contains the models classes for bot-schema. + * This package contains the models classes for com.microsoft.bot.schema.teams. */ package com.microsoft.bot.schema.teams; diff --git a/libraries/bot-schema/src/test/java/com/microsoft/bot/schema/ActivityTest.java b/libraries/bot-schema/src/test/java/com/microsoft/bot/schema/ActivityTest.java index 883f44a3a..61744ddef 100644 --- a/libraries/bot-schema/src/test/java/com/microsoft/bot/schema/ActivityTest.java +++ b/libraries/bot-schema/src/test/java/com/microsoft/bot/schema/ActivityTest.java @@ -34,6 +34,11 @@ public void GetConversationReference() { Assert.assertEquals(activity.getLocale(), conversationReference.getLocale()); Assert.assertEquals(activity.getChannelId(), conversationReference.getChannelId()); Assert.assertEquals(activity.getServiceUrl(), conversationReference.getServiceUrl()); + + activity.setType(ActivityTypes.CONVERSATION_UPDATE); + conversationReference = activity.getConversationReference(); + Assert.assertNull(conversationReference.getActivityId()); + } @Test @@ -229,7 +234,7 @@ private Activity createActivity() { activity.setFrom(account1); activity.setRecipient(account2); activity.setConversation(conversationAccount); - activity.setChannelId("ChannelId123"); + activity.setChannelId("directline"); // Intentionally oddly-cased to check that it isn't defaulted somewhere, but // tests stay in English activity.setLocale("en-uS"); @@ -701,6 +706,22 @@ public void GetMentionsNull() { Assert.assertTrue(activity.getMentions() != null); } + @Test + public void CreateTraceForConversationUpdateActivity() { + Activity activity = createActivity(); + activity.setType(ActivityTypes.CONVERSATION_UPDATE); + Activity trace = activity.createTrace("test"); + Assert.assertNull(trace.getReplyToId()); + } + + @Test + public void CreateReplyForConversationUpdateActivity() { + Activity activity = createActivity(); + activity.setType(ActivityTypes.CONVERSATION_UPDATE); + Activity reply = activity.createReply("test"); + Assert.assertNull(reply.getReplyToId()); + } + @Test public void CreateTrace() { Activity activity = createActivity(); diff --git a/libraries/bot-schema/src/test/java/com/microsoft/bot/schema/SerializationTest.java b/libraries/bot-schema/src/test/java/com/microsoft/bot/schema/SerializationTest.java index b9f6ff4d1..754ce84e1 100644 --- a/libraries/bot-schema/src/test/java/com/microsoft/bot/schema/SerializationTest.java +++ b/libraries/bot-schema/src/test/java/com/microsoft/bot/schema/SerializationTest.java @@ -177,13 +177,13 @@ private Activity createActivity() { ChannelAccount account1 = new ChannelAccount(); account1.setId("ChannelAccount_Id_1"); account1.setName("ChannelAccount_Name_1"); - account1.setProperties("Name", JsonNodeFactory.instance.objectNode().put("Name", "Value")); + account1.setProperties("TestName", JsonNodeFactory.instance.objectNode().put("Name", "Value")); account1.setRole(RoleTypes.USER); ChannelAccount account2 = new ChannelAccount(); account2.setId("ChannelAccount_Id_2"); account2.setName("ChannelAccount_Name_2"); - account2.setProperties("Name", JsonNodeFactory.instance.objectNode().put("Name", "Value")); + account2.setProperties("TestName", JsonNodeFactory.instance.objectNode().put("Name", "Value")); account2.setRole(RoleTypes.USER); ConversationAccount conversationAccount = new ConversationAccount(); @@ -191,7 +191,7 @@ private Activity createActivity() { conversationAccount.setId("123"); conversationAccount.setIsGroup(true); conversationAccount.setName("Name"); - conversationAccount.setProperties("Name", JsonNodeFactory.instance.objectNode().put("Name", "Value")); + conversationAccount.setProperties("TestName", JsonNodeFactory.instance.objectNode().put("Name", "Value")); Activity activity = new Activity(); activity.setId("123"); diff --git a/pipelines/BotBuilder-Java-4.0-daily.yml b/pipelines/BotBuilder-Java-4.0-daily.yml new file mode 100644 index 000000000..8b9922b14 --- /dev/null +++ b/pipelines/BotBuilder-Java-4.0-daily.yml @@ -0,0 +1,197 @@ +# 'Allow scripts to access the OAuth token' was selected in pipeline. Add the following YAML to any steps requiring access: +# env: +# MY_ACCESS_TOKEN: $(System.AccessToken) +# Variable 'PackageVersion' was defined in the Variables tab +# Variable 'PassPhrase' was defined in the Variables tab +# Variable Group 'JavaVariableGroup' was defined in the Variables tab +# Variable Group 'JavaSigningVariableGroup' was defined in the Variables tab +# Cron Schedules have been converted using UTC Time Zone and may need to be updated for your location + +# "name" here defines the build number format. Build number is accessed via $(Build.BuildNumber) +name: $(Build.BuildId) + +pool: + vmImage: windows-2019 + +trigger: none # ci trigger is set in ADO +pr: none # pr trigger is set in ADO + +jobs: +- job: Phase_1 + displayName: Phase 1 + + steps: + - powershell: | + # Replace {DateStamp} and {CommitHash} tokens with the actual values in var PackageVersion + $dateStamp = (Get-Date -format "yyyyMMdd"); + $commitHash = "$(Build.SourceVersion)".SubString(0,7); + + "Raw PackageVersion = $(PackageVersion)"; + $v = "$(PackageVersion)".Replace("{DateStamp}",$dateStamp).Replace("{CommitHash}",$commitHash); + Write-Host "##vso[task.setvariable variable=PackageVersion;]$v"; + "Resolved PackageVersion = $v"; + displayName: 'Resolve variable PackageVersion' + + - task: tagBuildOrRelease@0 + displayName: Tag Build with package version + inputs: + tags: PackageVersion=$(PackageVersion) + + - powershell: | + Write-Host "Setting module versions"; + Write-Host "version = $(PackageVersion)"; + + mvn versions:set -DnewVersion="$(PackageVersion)" --no-transfer-progress; + + Write-Host "Completed"; + displayName: Maven set versions in pom files + + - powershell: | + gpg --version + + Write-Host "Public key:"; + $file = "public.pgp"; + $key = "$(PgpSigningPublicKey)"; + + # Restore the missing newlines to the key + $cut1 = $key.Substring(0, 80).LastIndexOf('-') + 2; + $cut2 = $key.Substring($cut1).IndexOf('-') + $cut1; + $firstLine = $key.Substring(0, $cut1); + $lastLine = $key.Substring($cut2); + $middleLine = $key.Substring($cut1, $cut2 - $cut1); + $middleLine = $middleLine.Replace(" ", "`r`n"); + $key2 = -Join ($firstLine, "`r`n", $middleLine, $lastLine); + New-Item -Path $file -ItemType "file" -Value $key2; + + #'-------------'; get-content "$file"; '===================' + + gpg --import $file; + Remove-Item $file; + + Write-Host "Private key:" + $file = "private.pgp"; + $key = "$(PgpSigningPrivateKey)"; + $passPhrase = "$(PassPhrase)"; + + $cut1 = $key.Substring(0, 80).LastIndexOf('-') + 2; + $cut2 = $key.Substring($cut1).IndexOf('-') + $cut1; + $firstLine = $key.Substring(0, $cut1); + $lastLine = $key.Substring($cut2); + $middleLine = $key.Substring($cut1, $cut2 - $cut1); + $middleLine = $middleLine.Replace(" ", "`r`n"); + $key2 = -Join ($firstLine, "`r`n", $middleLine, $lastLine); + New-Item -Path $file -ItemType "file" -Value $key2; + gpg --allow-secret-key-import --import $file; + Remove-Item $file; + displayName: Import signing keys + + - powershell: | + $file = "$(Build.SourcesDirectory)\pom.xml"; + $xml = New-Object XML; + $xml.Load($file); + $nsm = New-Object Xml.XmlNamespaceManager($xml.NameTable); + $nsm.AddNamespace('ns', $xml.DocumentElement.NamespaceURI); + $element = $xml.SelectSingleNode('//ns:version', $nsm); + #$element.InnerText = "$(PackageVersion)" + #$xml.Save($file); + Write-Host $file '+++++++++++++++++++++++++++++++++++'; + Write-Host $element.InnerText; + get-content $file; + + $file = "$(Build.SourcesDirectory)\libraries\bot-builder\pom.xml"; + $xml = New-Object XML; + $xml.Load($file); + $nsm = New-Object Xml.XmlNamespaceManager($xml.NameTable); + $nsm.AddNamespace('ns', $xml.DocumentElement.NamespaceURI); + $element = $xml.SelectSingleNode('//ns:version', $nsm); + #$element.InnerText = "$(PackageVersion)" + #$xml.Save($file); + Write-Host $file '+++++++++++++++++++++++++++++++++++'; + Write-Host $element.InnerText; + get-content $file; + + $file = "$(Build.SourcesDirectory)\libraries\bot-schema\pom.xml"; + $xml = New-Object XML; + $xml.Load($file); + $nsm = New-Object Xml.XmlNamespaceManager($xml.NameTable); + $nsm.AddNamespace('ns', $xml.DocumentElement.NamespaceURI); + $element = $xml.SelectSingleNode('//ns:version', $nsm); + #$element.InnerText = "$(PackageVersion)" + #$xml.Save($file); + Write-Host $file '+++++++++++++++++++++++++++++++++++'; + Write-Host $element.InnerText; + get-content $file; + + $file = "$(Build.SourcesDirectory)\libraries\bot-connector\pom.xml"; + $xml = New-Object XML; + $xml.Load($file); + $nsm = New-Object Xml.XmlNamespaceManager($xml.NameTable); + $nsm.AddNamespace('ns', $xml.DocumentElement.NamespaceURI); + $element = $xml.SelectSingleNode('//ns:version', $nsm); + #$element.InnerText = "$(PackageVersion)" + #$xml.Save($file); + Write-Host $file '+++++++++++++++++++++++++++++++++++'; + Write-Host $element.InnerText; + get-content $file; + displayName: '[Diagnostics] Show pom files' + + - task: Maven@3 + displayName: Maven Install (this signs) + inputs: + goals: 'install -P devops ' + options: -e --no-transfer-progress -Drepo.id=ConversationalAI -Drepo.url=https://pkgs.dev.azure.com/ConversationalAI/BotFramework/_packaging/SDK/maven/v1 -Dexclude.tests= + codeCoverageTool: JaCoCo + findbugsAnalysisEnabled: true + + - task: Maven@3 + displayName: 'Maven Package ' + enabled: False + inputs: + goals: package -P devops + options: -e --no-transfer-progress + findbugsAnalysisEnabled: true + + - task: Maven@3 + displayName: Maven Deploy + condition: "and(succeeded(), or(eq(variables['Build.Reason'], 'Schedule'), eq(variables['publish'], 'true'))) " + enabled: False + inputs: + goals: deploy + options: --settings $(Build.SourcesDirectory)/settings.xml -Dinternal.repo.username=$(MavenUsername) -Dinternal.repo.password=$(MavenPassword) --no-transfer-progress + testRunTitle: Bot.Builder-Java Tests + checkstyleAnalysisEnabled: true + pmdAnalysisEnabled: true + findbugsAnalysisEnabled: true + + - task: Maven@3 + displayName: Maven deploy to scratch + enabled: False + inputs: + goals: deploy + options: --settings $(Build.SourcesDirectory)/settings.xml -Dinternal.repo.username=$(JavaMygetUserName) -Dinternal.repo.password=$(JavaMygetApiKey) -e --no-transfer-progress + publishJUnitResults: false + + - task: CopyFiles@2 + displayName: 'Copy deployment files to staging packages directory' + inputs: + SourceFolder: '$(Build.SourcesDirectory)' + Contents: | + libraries/*/target/*.jar + libraries/*/target/*.jar.asc + libraries/*/pom.xml + settings.xml + pom.xml + TargetFolder: '$(Build.ArtifactStagingDirectory)/packages' + + - task: PublishBuildArtifacts@1 + displayName: Publish deployment files to artifacts + inputs: + PathtoPublish: $(Build.ArtifactStagingDirectory)/packages + + - powershell: | + pushd ..; + Get-ChildItem -Recurse -Force; + displayName: Dir workspace + continueOnError: true + condition: succeededOrFailed() +... diff --git a/pom.xml b/pom.xml index b5b363ae3..95c92d755 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.microsoft.bot bot-java - 4.14.0-SNAPSHOT + 4.15.0-SNAPSHOT pom Microsoft BotBuilder Java SDK Parent @@ -21,6 +21,7 @@ 3.1.2 3.14.0 %regex[.*recognizers.*] + -Dfile.encoding=UTF-8 @@ -267,111 +268,116 @@ junit junit - 4.13.1 + 4.13.2 test org.mockito mockito-core - 3.8.0 + 3.12.4 test com.fasterxml.jackson.module jackson-module-parameter-names - 2.12.1 + 2.13.3 com.fasterxml.jackson.datatype jackson-datatype-jdk8 - 2.12.1 + 2.13.4 com.fasterxml.jackson.datatype jackson-datatype-jsr310 - 2.12.1 + 2.13.4 com.fasterxml.jackson.core jackson-databind - 2.12.1 + 2.13.4.1 com.codepoetics protonpack - 1.13 + 1.16 com.auth0 java-jwt - 3.13.0 + 3.19.2 com.auth0 jwks-rsa - 0.15.0 + 0.21.2 org.slf4j slf4j-api - 1.7.22 + 1.7.36 org.apache.commons commons-lang3 - 3.9 + 3.12.0 commons-io commons-io - 2.8.0 + 2.11.0 com.google.guava guava - 30.1-jre + 31.1-jre + + + net.minidev + json-smart + 2.4.8 org.apache.logging.log4j log4j-api - 2.11.0 + 2.19.0 test org.slf4j slf4j-log4j12 - 1.7.25 + 1.7.36 test org.apache.logging.log4j log4j-core - 2.13.2 + 2.19.0 test com.squareup.okhttp3 okhttp - 3.12.2 + 4.9.2 com.squareup.okhttp3 logging-interceptor - 3.12.2 + 4.9.2 com.squareup.okhttp3 okhttp-urlconnection - 3.12.2 + 4.9.2 com.squareup.okhttp3 mockwebserver - 3.12.2 + 4.9.2 test @@ -595,6 +601,9 @@ org.apache.maven.plugins maven-javadoc-plugin 3.1.1 + + false + attach-javadocs