diff --git a/.eslintrc.json b/.eslintrc.json index 42fdf78bd6..d21952753f 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,6 +1,7 @@ { "env": { - "es6": true, + "es2017": true, + "mocha": true, "node": true }, "extends": ["eslint:recommended"], diff --git a/.github/ISSUE_TEMPLATE/-net-sdk-bug.md b/.github/ISSUE_TEMPLATE/-net-sdk-bug.md deleted file mode 100644 index eaefeb99f1..0000000000 --- a/.github/ISSUE_TEMPLATE/-net-sdk-bug.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -name: "Bug" -about: Create a bug report for a bug you found in the Bot Framework Skills projects -title: "" -labels: "needs-triage, bug" -assignees: "" ---- - -### [Github issues](https://github.com/Microsoft/botframework-skills/issues) should be used for bugs and feature requests. Use [Stack Overflow](https://stackoverflow.com/questions/tagged/botframework) for general "how-to" questions. - -## Version -What package version of the SDK are you using. - -## Describe the bug -Give a clear and concise description of what the bug is. - -## To Reproduce -Steps to reproduce the behavior: - -## Expected behavior -Give a clear and concise description of what you expected to happen. - -## Screenshots -If applicable, add screenshots to help explain your problem. - -## Additional context -Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/-net-sdk-feature-request.md b/.github/ISSUE_TEMPLATE/-net-sdk-feature-request.md deleted file mode 100644 index 136ba70fbb..0000000000 --- a/.github/ISSUE_TEMPLATE/-net-sdk-feature-request.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -name: "Feature request" -about: Suggest a feature for the Bot Framework Skills projects -title: "" -labels: "needs-triage, feature-request" -assignees: "" ---- - -### Use this [query](https://github.com/Microsoft/botbuilder-dotnet/issues?q=is%3Aissue+is%3Aopen++label%3Afeature-request+) to search for the most popular feature requests. - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - -**Additional context** -Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/-net-sdk-question.md b/.github/ISSUE_TEMPLATE/-net-sdk-question.md deleted file mode 100644 index 58c7985686..0000000000 --- a/.github/ISSUE_TEMPLATE/-net-sdk-question.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -name: "Question" -about: The issue tracker is not for questions. Please ask questions on https://stackoverflow.com/questions/tagged/botframework - ---- - -🚨 The issue tracker is not for questions 🚨 - -If you have a question, please ask it on https://stackoverflow.com/questions/tagged/botframework - -[question] diff --git a/.github/ISSUE_TEMPLATE/calendar-skill-v2-bug.md b/.github/ISSUE_TEMPLATE/calendar-skill-v2-bug.md deleted file mode 100644 index 8f3f7566d1..0000000000 --- a/.github/ISSUE_TEMPLATE/calendar-skill-v2-bug.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -name: "Calendar Skill V2 Bug" -about: Create a bug report for a bug you found during Calendar Skill V2 testing -title: "[Calendar Skill V2] " -labels: "needs-triage, bug, Area: Calendar Skill" -assignees: "" ---- - -## Describe the bug -Give a clear and concise description of what the bug is. - -## To Reproduce -Steps to reproduce the behavior. What utterances or buttons didn't work? - -## Expected behavior -Give a clear and concise description of what you expected to happen. - -## Screenshots -If applicable, add screenshots to help explain your problem. - -## Additional context -Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/components-bug-report.md b/.github/ISSUE_TEMPLATE/components-bug-report.md new file mode 100644 index 0000000000..ba82285f25 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/components-bug-report.md @@ -0,0 +1,35 @@ +--- +name: "Bug" +about: Create a report for a bug you found in the Bot Framework Components projects (including documentation). +title: "" +labels: "needs-triage, bug" +assignees: "" +--- + + + +## Describe the bug + + + +## Version + + + + +## To Reproduce + + + +## Expected behavior + + + +## Screenshots + + + +## Additional context + + diff --git a/.github/ISSUE_TEMPLATE/components-feature-request.md b/.github/ISSUE_TEMPLATE/components-feature-request.md new file mode 100644 index 0000000000..c25d90f782 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/components-feature-request.md @@ -0,0 +1,25 @@ +--- +name: "Feature Request" +about: Suggest an idea a Bot Framework Components project. +title: "" +labels: "needs-triage, feature-request" +assignees: "" +--- + + + +## Is your feature request related to a problem? Please describe. + + + +## Describe the solution you'd like + + + +## Describe alternatives you've considered + + + +## Additional context + + diff --git a/.github/ISSUE_TEMPLATE/components-other.md b/.github/ISSUE_TEMPLATE/components-other.md new file mode 100644 index 0000000000..981db40f73 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/components-other.md @@ -0,0 +1,7 @@ +--- +name: Other +about: An empty issue template. +title: "" +labels: "needs-triage" +assignees: "" +--- diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000..1064705749 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Question + url: https://stackoverflow.com/questions/tagged/botframework + about: The issue tracker is not for questions, please ask general "how-to" questions on Stack Overflow. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/docs-bug-report.md b/.github/ISSUE_TEMPLATE/docs-bug-report.md deleted file mode 100644 index a7fca37239..0000000000 --- a/.github/ISSUE_TEMPLATE/docs-bug-report.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -name: "Documentation Issue" -about: Create a report to help us improve our documentation -labels: "needs-triage, Area: Docs" ---- - -**Please describe your request** - -**Is this an issue with current documentation? If yes, please provide the link.** diff --git a/.github/ISSUE_TEMPLATE/feature-spec.md b/.github/ISSUE_TEMPLATE/prebuilt-experience-user-story.md similarity index 76% rename from .github/ISSUE_TEMPLATE/feature-spec.md rename to .github/ISSUE_TEMPLATE/prebuilt-experience-user-story.md index 3116722960..a06650bcf7 100644 --- a/.github/ISSUE_TEMPLATE/feature-spec.md +++ b/.github/ISSUE_TEMPLATE/prebuilt-experience-user-story.md @@ -1,8 +1,8 @@ --- -name: "Feature spec" -about: Detailed feature definition. +name: "Prebuilt Experiences User Story" +about: Detailed user story spec for prebuilt experiences. title: "" -labels: "" +labels: "Owner: PBX" assignees: "" --- @@ -14,22 +14,22 @@ assignees: "" ## Description ## Conversation Design -Links to conversation design diagrams and documents + ## Language Understanding | Intent | Sample Utterances | Prebuilt Entities | Custom Entities | Patterns | | ------ | ----------------------| ----------------- | ----------------- | --------- | ## Dialogs -Describe the different dialogs to be created as part of the feature and what each will do + ## Custom Actions -Describe any custom actions that will need to be created to support the feature + | Name | Input | Output | Notes | | ------ | ------ | -------- | ------- | ## Channels -Describe any channel specific functionality or limitations that will need to be taken into consideration + ## Acceptance Criteria - [ ] Flow should work in both Direct mode (connecting to the skill bot directly diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index f397c0b377..e25a784ea7 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,20 +1,20 @@ - + + + + ### Purpose -*What is the context of this pull request? Why is it being done?* + + ### Changes -*Are there any changes that need to be called out as significant or particularly difficult to grasp? (Include illustrative screenshots for context if applicable.)* + + ### Tests -*Is this covered by existing tests or new ones? If no, why not?* + + ### Feature Plan -*Are there any remaining steps or dependencies before this issue can be fully resolved? If so, describe and link to any relevant pull requests or issues.* -### Checklist + -#### General -- [ ] I have commented my code, particularly in hard-to-understand areas -- [ ] I have added or updated the appropriate tests -- [ ] I have updated related documentation diff --git a/.github/labeler.yml b/.github/labeler.yml index c401582ebc..c28435f13e 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -11,10 +11,10 @@ - generators/generator-bot-core-assistant/**/* "Area: Generators: Core Language": - - generators/generator-core-language/**/* + - generators/generator-bot-core-language/**/* "Area: Generators: Core QnA": - - generators/generator-bot-qna/**/* + - generators/generator-bot-core-qna/**/* "Area: Generators: Empty": - generators/generator-bot-empty/**/* @@ -25,6 +25,9 @@ "Area: Generators: Enterprise Calendar": - generators/generator-bot-enterprise-calendar/**/* +"Area: Generators: Enterprise People": + - generators/generator-bot-enterprise-people/**/* + "Area: Packages: Adaptive Cards": - packages/AdaptiveCards/**/* diff --git a/README.md b/README.md index db8d7706ad..57de73eeec 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ You'll primarily use components through [**Bot Framework Composer**](https://git ## Creating your own components -You can also create your own packages and templates for use from Composer. We document creating components [here](/docs/overview.md). You can use this repository as examples for building your own components. +You can also create your own packages and templates for use from Composer. We document creating components [here](https://docs.microsoft.com/composer/concept-packages). You can also check out [our samples](https://github.com/microsoft/BotBuilder-Samples/tree/main/composer_samples). ## Index of Content @@ -18,13 +18,13 @@ Templates are pre-built bot projects designed for specific scenarios. We use [ye | Name | npm | Description | |:------------:|:---:|:------------| -|[Empty Bot](/generators/generator-bot-empty) | [![npm version](https://badge.fury.io/js/%40microsoft%2Fgenerator-bot-empty.svg)](https://badge.fury.io/js/%40microsoft%2Fgenerator-bot-empty) | The base empty bot. | -|[Core Bot with Language](/generators/generator-bot-core-language) | [![npm version](https://badge.fury.io/js/%40microsoft%2Fgenerator-bot-core-language.svg)](https://badge.fury.io/js/%40microsoft%2Fgenerator-bot-core-language) | Basic conversational bot with NLP. | -|[Core Assistant Bot](/generators/generator-bot-core-assistant) | [![npm version](https://badge.fury.io/js/%40microsoft%2Fgenerator-bot-core-assistant.svg)](https://badge.fury.io/js/%40microsoft%2Fgenerator-bot-core-assistant) | Basic assistant bot with no skills. | -|[Enterprise Assistant Bot](/generators/generator-bot-enterprise-assistant) | [![npm version](https://badge.fury.io/js/%40microsoft%2Fgenerator-bot-enterprise-assistant.svg)](https://badge.fury.io/js/%40microsoft%2Fgenerator-bot-enterprise-assistant) | Assistant Core + Calendar & People as skills. | -|[Enterprise Calendar Bot](/generators/generator-bot-enterprise-calendar) | [![npm version](https://badge.fury.io/js/%40microsoft%2Fgenerator-bot-enterprise-calendar.svg)](https://badge.fury.io/js/%40microsoft%2Fgenerator-bot-enterprise-calendar) | A bot for working with Calendars. | -|[Enterprise People Bot](/generators/generator-bot-enterprise-people) | [![npm version](https://badge.fury.io/js/%40microsoft%2Fgenerator-bot-enterprise-people.svg)](https://badge.fury.io/js/%40microsoft%2Fgenerator-bot-enterprise-people) | A bot for searching for people on the MS Graph. | -|[Adaptive](/generators/generator-bot-adaptive) | [![npm version](https://badge.fury.io/js/%40microsoft%2Fgenerator-bot-adaptive.svg)](https://badge.fury.io/js/%40microsoft%2Fgenerator-bot-adaptive) | Used by other generators to scaffold web app or functions project. | +|[Empty Bot](/generators/generator-bot-empty) | [![npm version](https://badge.fury.io/js/%40microsoft%2Fgenerator-bot-empty.svg)](https://badge.fury.io/js/%40microsoft%2Fgenerator-bot-empty) | A simple bot with a root dialog and greeting dialog. | +|[Core Bot with Azure Language Understanding](/generators/generator-bot-core-language) | [![npm version](https://badge.fury.io/js/%40microsoft%2Fgenerator-bot-core-language.svg)](https://badge.fury.io/js/%40microsoft%2Fgenerator-bot-core-language) | A simple bot with Azure Language Understanding (LUIS) and common trigger phrases used to direct the conversation flow. | +|[Core Assistant Bot](/generators/generator-bot-core-assistant) | [![npm version](https://badge.fury.io/js/%40microsoft%2Fgenerator-bot-core-assistant.svg)](https://badge.fury.io/js/%40microsoft%2Fgenerator-bot-core-assistant) | A bot with Azure Language Understanding (LUIS) and common trigger phrases used to direct the conversation flow and help customers accomplish basic tasks. Designed to be extended with skills. | +|[Enterprise Assistant Bot](/generators/generator-bot-enterprise-assistant) | [![npm version](https://badge.fury.io/js/%40microsoft%2Fgenerator-bot-enterprise-assistant.svg)](https://badge.fury.io/js/%40microsoft%2Fgenerator-bot-enterprise-assistant) | A Core Assistant Bot with Calendar & People as skills. | +|[Enterprise Calendar Bot](/generators/generator-bot-enterprise-calendar) | [![npm version](https://badge.fury.io/js/%40microsoft%2Fgenerator-bot-enterprise-calendar.svg)](https://badge.fury.io/js/%40microsoft%2Fgenerator-bot-enterprise-calendar) | A bot with the ability to interact with M365 Calendar using Microsoft Graph. | +|[Enterprise People Bot](/generators/generator-bot-enterprise-people) | [![npm version](https://badge.fury.io/js/%40microsoft%2Fgenerator-bot-enterprise-people.svg)](https://badge.fury.io/js/%40microsoft%2Fgenerator-bot-enterprise-people) | A bot with the ability to search for people within Azure Active Directory using Microsoft Graph.| +|[Adaptive Bot Generator](/generators/generator-bot-adaptive) | [![npm version](https://badge.fury.io/js/%40microsoft%2Fgenerator-bot-adaptive.svg)](https://badge.fury.io/js/%40microsoft%2Fgenerator-bot-adaptive) | Used by other generators to scaffold web app or functions project. | ### Packages diff --git a/assets/packageManager.PNG b/assets/packageManager.PNG deleted file mode 100644 index 414d6d4797..0000000000 Binary files a/assets/packageManager.PNG and /dev/null differ diff --git a/build/templates/template-app-insights-resources.json b/build/templates/template-app-insights-resources.json new file mode 100644 index 0000000000..0fa26fa2e3 --- /dev/null +++ b/build/templates/template-app-insights-resources.json @@ -0,0 +1,35 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "appInsightsName": { + "type": "string", + "defaultValue": "bfcfnappinsights", + "metadata": { + "description": "The name of the Application Insights instance." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Specifies the Azure location where the key vault should be created." + } + } + }, + "resources": [ + { + "type": "Microsoft.Insights/components", + "apiVersion": "2020-02-02-preview", + "name": "[parameters('appInsightsName')]", + "location": "[parameters('location')]", + "kind": "web", + "properties": { + "Application_Type":"web", + "IngestionMode": "ApplicationInsights", + "publicNetworkAccessForIngestion": "Enabled", + "publicNetworkAccessForQuery": "Enabled" + } + } + ] +} diff --git a/build/templates/template-bot-resources.json b/build/templates/template-bot-resources.json new file mode 100644 index 0000000000..de5faada36 --- /dev/null +++ b/build/templates/template-bot-resources.json @@ -0,0 +1,210 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-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." + } + }, + "botName": { + "type": "string" + }, + "botLocation": { + "type": "string" + }, + "appInsightsName": { + "type": "string", + "defaultValue": "" + }, + "appServicePlanName": { + "type": "string" + }, + "appServicePlanResourceGroup": { + "type": "string" + }, + "botSku": { + "type": "string", + "defaultValue": "F0", + "metadata": { + "description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1." + } + } + }, + "variables": { + "siteHost": "[concat(parameters('botName'), '.azurewebsites.net')]", + "botEndpoint": "[concat('https://', variables('siteHost'), '/api/messages')]", + "publishingUsername": "[concat('$', parameters('botName'))]" + }, + "resources": [ + { + "type": "Microsoft.Web/sites", + "apiVersion": "2020-09-01", + "name": "[parameters('botName')]", + "location": "[parameters('botLocation')]", + "kind": "app", + "properties": { + "enabled": true, + "hostNameSslStates": [ + { + "name": "[concat(parameters('botName'), '.azurewebsites.net')]", + "sslState": "Disabled", + "hostType": "Standard" + }, + { + "name": "[concat(parameters('botName'), '.scm.azurewebsites.net')]", + "sslState": "Disabled", + "hostType": "Repository" + } + ], + "serverFarmId": "[concat('/subscriptions/', subscription().id,'/resourcegroups/', parameters('appServicePlanResourceGroup'), '/providers/Microsoft.Web/serverfarms/', parameters('appServicePlanName'))]", + "reserved": true, + "hyperV": false, + "siteConfig": { + "appSettings": [ + { + "name": "WEBSITE_NODE_DEFAULT_VERSION", + "value": "10.14.1" + }, + { + "name": "MicrosoftAppId", + "value": "[parameters('appId')]" + }, + { + "name": "MicrosoftAppPassword", + "value": "[parameters('appSecret')]" + }, + { + "name": "APPINSIGHTS_INSTRUMENTATIONKEY", + "value": "[if(empty(parameters('appInsightsName')), '', reference(resourceId(parameters('appServicePlanResourceGroup'),'Microsoft.Insights/components', parameters('appInsightsName')), '2015-05-01', 'Full').properties.InstrumentationKey)]" + }, + { + "name": "APPLICATIONINSIGHTS_CONNECTION_STRING", + "value": "[if(empty(parameters('appInsightsName')), '', reference(resourceId(parameters('appServicePlanResourceGroup'),'Microsoft.Insights/components', parameters('appInsightsName')), '2015-05-01', 'Full').properties.ConnectionString)]" + }, + { + "name": "ApplicationInsightsAgent_EXTENSION_VERSION", + "value": "~2" + } + ], + "webSocketsEnabled": true, + "scmType": "None", + "use32BitWorkerProcess": true, + "alwaysOn": true, + "managedPipelineMode": "Integrated", + "virtualApplications": [ + { + "virtualPath": "/", + "physicalPath": "site\\wwwroot", + "preloadEnabled": true + } + ], + "loadBalancing": "LeastRequests", + "experiments": { + "rampUpRules": [] + }, + "autoHealEnabled": false, + "cors": { + "allowedOrigins": [ + "https://botservice.hosting.portal.azure.net", + "https://hosting.onecloud.azure-test.net/" + ], + "supportCredentials": 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", + "numberOfWorkers": 1, + "defaultDocuments": [ + "Default.htm", + "Default.html", + "Default.asp", + "index.htm", + "index.html", + "iisstart.htm", + "default.aspx", + "index.php", + "hostingstart.html" + ], + "netFrameworkVersion": "v4.0", + "phpVersion": "5.6", + "requestTracingEnabled": false, + "remoteDebuggingEnabled": false, + "httpLoggingEnabled": true, + "logsDirectorySizeLimit": 35, + "detailedErrorLoggingEnabled": false, + "publishingUsername": "[variables('publishingUsername')]" + }, + "scmSiteAlsoStopped": false, + "clientAffinityEnabled": true, + "hostNamesDisabled": false, + "clientCertEnabled": false, + "httpsOnly": false, + "redundancyMode": "None" + } + }, + { + "type": "Microsoft.Web/sites/hostNameBindings", + "apiVersion": "2020-09-01", + "name": "[concat(parameters('botName'), '/', parameters('botName'), '.azurewebsites.net')]", + "location": "[parameters('botLocation')]", + "dependsOn": [ + "[resourceId('Microsoft.Web/sites', parameters('botName'))]" + ], + "properties": { + "siteName": "[parameters('botName')]", + "hostNameType": "Verified" + } + }, + { + "type": "Microsoft.BotService/botServices", + "apiVersion": "2020-06-02", + "name": "[parameters('botName')]", + "location": "global", + "kind": "bot", + "sku": { + "name": "[parameters('botSku')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Web/sites', parameters('botName'))]" + ], + "properties": { + "name": "[parameters('botName')]", + "displayName": "[parameters('botName')]", + "endpoint": "[variables('botEndpoint')]", + "msaAppId": "[parameters('appId')]", + "developerAppInsightsApplicationId": "", + "developerAppInsightKey": "", + "publishingCredentials": null, + "storageResourceId": null + } + } + ] +} \ No newline at end of file diff --git a/build/templates/template-cosmosdb-resources.json b/build/templates/template-cosmosdb-resources.json new file mode 100644 index 0000000000..152fb99a29 --- /dev/null +++ b/build/templates/template-cosmosdb-resources.json @@ -0,0 +1,77 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "accountName": { + "type": "string", + "metadata": { + "description": "Cosmos DB account name" + } + }, + "databaseName": { + "type": "string", + "metadata": { + "description": "The name for the Core (SQL) database" + } + } + }, + "variables": { + "accountName": "[toLower(parameters('accountName'))]" + }, + "resources": [ + { + "apiVersion": "2020-04-01", + "kind": "GlobalDocumentDB", + "type": "Microsoft.DocumentDB/databaseAccounts", + "name": "[parameters('accountName')]", + "location": "westus", + "properties": { + "databaseAccountOfferType": "Standard", + "locations": [ + { + "id": "[concat(variables('accountName'), '-westus')]", + "failoverPriority": 0, + "locationName": "West US" + } + ], + "backupPolicy": { + "type": "Periodic", + "periodicModeProperties": { + "backupIntervalInMinutes": 240, + "backupRetentionIntervalInHours": 8 + } + }, + "isVirtualNetworkFilterEnabled": false, + "virtualNetworkRules": [], + "ipRules": [], + "dependsOn": [], + "enableMultipleWriteLocations": false, + "capabilities": [], + "enableFreeTier": false + }, + "tags": { + "defaultExperience": "Core (SQL)", + "hidden-cosmos-mmspecial": "", + "CosmosAccountType": "Non-Production" + } + }, + { + "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases", + "apiVersion": "2020-03-01", + "name": "[concat(variables('accountName'), '/', parameters('databaseName'))]", + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts', variables('accountName'))]" + ], + "properties": { + "mode": "Complete", + "resource": { + "id": "[parameters('databaseName')]" + }, + "options": { + "throughput": "400" + } + } + } + ], + "outputs": {} +} diff --git a/build/templates/template-key-vault-resources.json b/build/templates/template-key-vault-resources.json new file mode 100644 index 0000000000..3c3849c5f3 --- /dev/null +++ b/build/templates/template-key-vault-resources.json @@ -0,0 +1,129 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "keyVaultName": { + "type": "string", + "metadata": { + "description": "Specifies the name of the key vault." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Specifies the Azure location where the key vault should be created." + } + }, + "enabledForDeployment": { + "type": "bool", + "defaultValue": false, + "allowedValues": [ + true, + false + ], + "metadata": { + "description": "Specifies whether Azure Virtual Machines are permitted to retrieve certificates stored as secrets from the key vault." + } + }, + "enabledForDiskEncryption": { + "type": "bool", + "defaultValue": false, + "allowedValues": [ + true, + false + ], + "metadata": { + "description": "Specifies whether Azure Disk Encryption is permitted to retrieve secrets from the vault and unwrap keys." + } + }, + "enabledForTemplateDeployment": { + "type": "bool", + "defaultValue": true, + "allowedValues": [ + true, + false + ], + "metadata": { + "description": "Specifies whether Azure Resource Manager is permitted to retrieve secrets from the key vault." + } + }, + "tenantId": { + "type": "string", + "defaultValue": "[subscription().tenantId]", + "metadata": { + "description": "Specifies the Azure Active Directory tenant ID that should be used for authenticating requests to the key vault. Get it by using Get-AzSubscription cmdlet." + } + }, + "objectId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Specifies the object ID of a user, service principal or security group in the Azure Active Directory tenant for the vault. The object ID must be unique for the list of access policies. Get it by using Get-AzADUser or Get-AzADServicePrincipal cmdlets." + } + }, + "keysPermissions": { + "type": "array", + "defaultValue": [ + "list" + ], + "metadata": { + "description": "Specifies the permissions to keys in the vault. Valid values are: all, encrypt, decrypt, wrapKey, unwrapKey, sign, verify, get, list, create, update, import, delete, backup, restore, recover, and purge." + } + }, + "secretsPermissions": { + "type": "array", + "defaultValue": [ + "list", + "set", + "get" + ], + "metadata": { + "description": "Specifies the permissions to secrets in the vault. Valid values are: all, get, list, set, delete, backup, restore, recover, and purge." + } + }, + "skuName": { + "type": "string", + "defaultValue": "Standard", + "allowedValues": [ + "Standard", + "Premium" + ], + "metadata": { + "description": "Specifies whether the key vault is a standard vault or a premium vault." + } + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2019-09-01", + "name": "[parameters('keyVaultName')]", + "location": "[parameters('location')]", + "properties": { + "enabledForDeployment": "[parameters('enabledForDeployment')]", + "enabledForDiskEncryption": "[parameters('enabledForDiskEncryption')]", + "enabledForTemplateDeployment": "[parameters('enabledForTemplateDeployment')]", + "tenantId": "[parameters('tenantId')]", + "accessPolicies": [ + { + "objectId": "[parameters('objectId')]", + "tenantId": "[parameters('tenantId')]", + "permissions": { + "keys": "[parameters('keysPermissions')]", + "secrets": "[parameters('secretsPermissions')]" + } + } + ], + "sku": { + "name": "[parameters('skuName')]", + "family": "A" + }, + "networkAcls": { + "defaultAction": "Allow", + "bypass": "AzureServices" + } + } + } + ] +} diff --git a/build/templates/template-python-bot-resources.json b/build/templates/template-python-bot-resources.json new file mode 100644 index 0000000000..d704aabd80 --- /dev/null +++ b/build/templates/template-python-bot-resources.json @@ -0,0 +1,195 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-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." + } + }, + "botName": { + "type": "string" + }, + "botLocation": { + "type": "string" + }, + "appServicePlanName": { + "type": "string" + }, + "appServicePlanResourceGroup": { + "type": "string" + }, + "botSku": { + "type": "string", + "defaultValue": "F0", + "metadata": { + "description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1." + } + } + }, + "variables": { + "siteHost": "[concat(parameters('botName'), '.azurewebsites.net')]", + "botEndpoint": "[concat('https://', variables('siteHost'), '/api/messages')]", + "publishingUsername": "[concat('$', parameters('botName'))]" + }, + "resources": [ + { + "type": "Microsoft.Web/sites", + "apiVersion": "2020-09-01", + "name": "[parameters('botName')]", + "location": "[parameters('botLocation')]", + "kind": "app,linux", + "properties": { + "enabled": true, + "hostNameSslStates": [ + { + "name": "[concat(parameters('botName'), '.azurewebsites.net')]", + "sslState": "Disabled", + "hostType": "Standard" + }, + { + "name": "[concat(parameters('botName'), '.scm.azurewebsites.net')]", + "sslState": "Disabled", + "hostType": "Repository" + } + ], + "serverFarmId": "[concat('/subscriptions/', subscription().id,'/resourcegroups/', parameters('appServicePlanResourceGroup'), '/providers/Microsoft.Web/serverfarms/', parameters('appServicePlanName'))]", + "reserved": true, + "hyperV": false, + "siteConfig": { + "appSettings": [ + { + "name": "WEBSITE_RUN_FROM_PACKAGE", + "value": "1" + }, + { + "name": "MicrosoftAppId", + "value": "[parameters('appId')]" + }, + { + "name": "MicrosoftAppPassword", + "value": "[parameters('appSecret')]" + } + ], + "webSocketsEnabled": true, + "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": "PYTHON|3.8", + "requestTracingEnabled": false, + "remoteDebuggingEnabled": false, + "httpLoggingEnabled": true, + "logsDirectorySizeLimit": 35, + "detailedErrorLoggingEnabled": false, + "publishingUsername": "[variables('publishingUsername')]", + "scmType": "None", + "use32BitWorkerProcess": true, + "alwaysOn": true, + "appCommandLine": "gunicorn --bind 0.0.0.0 --worker-class aiohttp.worker.GunicornWebWorker --timeout 600 app:APP", + "managedPipelineMode": "Integrated", + "virtualApplications": [ + { + "virtualPath": "/", + "physicalPath": "site\\wwwroot", + "preloadEnabled": true + } + ], + "loadBalancing": "LeastRequests", + "experiments": { + "rampUpRules": [] + }, + "autoHealEnabled": false, + "cors": { + "allowedOrigins": [ + "https://botservice.hosting.portal.azure.net", + "https://hosting.onecloud.azure-test.net/" + ], + "supportCredentials": 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" + }, + "scmSiteAlsoStopped": false, + "clientAffinityEnabled": true, + "hostNamesDisabled": false, + "clientCertEnabled": false, + "httpsOnly": false, + "redundancyMode": "None" + } + }, + { + "type": "Microsoft.Web/sites/hostNameBindings", + "apiVersion": "2020-09-01", + "name": "[concat(parameters('botName'), '/', parameters('botName'), '.azurewebsites.net')]", + "location": "[parameters('botLocation')]", + "dependsOn": [ + "[resourceId('Microsoft.Web/sites', parameters('botName'))]" + ], + "properties": { + "siteName": "[parameters('botName')]", + "hostNameType": "Verified" + } + }, + { + "type": "Microsoft.BotService/botServices", + "apiVersion": "2020-06-02", + "name": "[parameters('botName')]", + "location": "global", + "kind": "bot", + "sku": { + "name": "[parameters('botSku')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.Web/sites', parameters('botName'))]" + ], + "properties": { + "name": "[parameters('botName')]", + "displayName": "[parameters('botName')]", + "endpoint": "[variables('botEndpoint')]", + "msaAppId": "[parameters('appId')]", + "developerAppInsightsApplicationId": "", + "developerAppInsightKey": "", + "publishingCredentials": null, + "storageResourceId": null + } + } + ] +} \ No newline at end of file diff --git a/build/templates/template-service-plan-linux-resources.json b/build/templates/template-service-plan-linux-resources.json new file mode 100644 index 0000000000..f52af0cec3 --- /dev/null +++ b/build/templates/template-service-plan-linux-resources.json @@ -0,0 +1,61 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "newAppServicePlanName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the new App Service Plan." + } + }, + "newAppServicePlanSku": { + "type": "string", + "defaultValue": "F1", + "metadata": { + "description": "The SKU of the App Service Plan. Defaults to Standard values." + } + }, + "appServicePlanLocation": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "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." + } + } + }, + "variables": { + "defaultAppServicePlanName": "[if(empty(parameters('existingAppServicePlan')), 'createNewAppServicePlan', parameters('existingAppServicePlan'))]", + "useExistingAppServicePlan": "[not(equals(variables('defaultAppServicePlanName'), 'createNewAppServicePlan'))]", + "servicePlanName": "[if(variables('useExistingAppServicePlan'), parameters('existingAppServicePlan'), parameters('newAppServicePlanName'))]", + "resourcesLocation": "[parameters('appServicePlanLocation')]" + }, + "resources": [ + { + "type": "Microsoft.Web/serverfarms", + "apiVersion": "2018-02-01", + "name": "[variables('servicePlanName')]", + "location": "[variables('resourcesLocation')]", + "comments": "Create a new Linux App Service Plan if no existing App Service Plan name was passed in.", + "condition": "[not(variables('useExistingAppServicePlan'))]", + "sku": { + "name": "[parameters('newAppServicePlanSku')]" + }, + "kind": "linux", + "properties": { + "name": "[variables('servicePlanName')]", + "perSiteScaling": false, + "reserved": true, + "targetWorkerCount": 0, + "targetWorkerSizeId": 0 + } + } + ] +} diff --git a/build/templates/template-service-plan-windows-resources.json b/build/templates/template-service-plan-windows-resources.json new file mode 100644 index 0000000000..996fc7d078 --- /dev/null +++ b/build/templates/template-service-plan-windows-resources.json @@ -0,0 +1,61 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "newAppServicePlanName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the new App Service Plan." + } + }, + "newAppServicePlanSku": { + "type": "string", + "defaultValue": "F1", + "metadata": { + "description": "The SKU of the App Service Plan. Defaults to Standard values." + } + }, + "appServicePlanLocation": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "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." + } + } + }, + "variables": { + "defaultAppServicePlanName": "[if(empty(parameters('existingAppServicePlan')), 'createNewAppServicePlan', parameters('existingAppServicePlan'))]", + "useExistingAppServicePlan": "[not(equals(variables('defaultAppServicePlanName'), 'createNewAppServicePlan'))]", + "servicePlanName": "[if(variables('useExistingAppServicePlan'), parameters('existingAppServicePlan'), parameters('newAppServicePlanName'))]", + "resourcesLocation": "[parameters('appServicePlanLocation')]" + }, + "resources": [ + { + "type": "Microsoft.Web/serverfarms", + "apiVersion": "2018-02-01", + "name": "[variables('servicePlanName')]", + "location": "[variables('resourcesLocation')]", + "comments": "Create a new Windows App Service Plan if no existing App Service Plan name was passed in.", + "condition": "[not(variables('useExistingAppServicePlan'))]", + "sku": { + "name": "[parameters('newAppServicePlanSku')]" + }, + "kind": "app", + "properties": { + "name": "[variables('servicePlanName')]", + "perSiteScaling": false, + "reserved": false, + "targetWorkerCount": 0, + "targetWorkerSizeId": 0 + } + } + ] +} diff --git a/build/templates/template-storage-resources.json b/build/templates/template-storage-resources.json new file mode 100644 index 0000000000..5991d5cd5e --- /dev/null +++ b/build/templates/template-storage-resources.json @@ -0,0 +1,70 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "storageAccountsName": { + "defaultValue": "bfcfnstorage", + "type": "String" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Specifies the Azure location where the storage account should be created." + } + } + }, + "variables": {}, + "resources": [ + { + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2021-04-01", + "name": "[parameters('storageAccountsName')]", + "location": "[parameters('location')]", + "sku": { + "name": "Standard_LRS", + "tier": "Standard" + }, + "kind": "StorageV2", + "properties": { + "networkAcls": { + "bypass": "AzureServices", + "virtualNetworkRules": [], + "ipRules": [], + "defaultAction": "Allow" + }, + "supportsHttpsTrafficOnly": true, + "encryption": { + "services": { + "blob": { + "keyType": "Account", + "enabled": true + } + }, + "keySource": "Microsoft.Storage" + }, + "accessTier": "Hot" + } + }, + { + "type": "Microsoft.Storage/storageAccounts/blobServices", + "apiVersion": "2021-04-01", + "name": "[concat(parameters('storageAccountsName'), '/default')]", + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountsName'))]" + ], + "sku": { + "name": "Standard_LRS", + "tier": "Standard" + }, + "properties": { + "cors": { + "corsRules": [] + }, + "deleteRetentionPolicy": { + "enabled": false + } + } + } + ] +} \ No newline at end of file diff --git a/build/yaml/templates/component-template.yml b/build/yaml/templates/component-template.yml index 85cc47a367..cce084d0c1 100644 --- a/build/yaml/templates/component-template.yml +++ b/build/yaml/templates/component-template.yml @@ -6,20 +6,24 @@ stages: condition: and(or(eq(variables.ComponentType, 'declarativeAsset'), eq(variables.ComponentType, 'generator'), and(eq(variables.ComponentType, 'codeExtension'), eq(variables.Language, 'js'))), not(eq(variables.PublishPackageArtifacts, true))) jobs: - job: job_build_npm - displayName: Build Node project + displayName: Build & Test Node project steps: - - template: npm-build-steps.yml + - template: yarn-build-steps.yml + - template: yarn-test-steps.yml + - template: debug-workspace-steps.yml - stage: stage_package_node - displayName: 'Node: Build, Version & Pack' + displayName: 'Node: Build, Test, Version & Pack' condition: and(or(eq(variables.ComponentType, 'declarativeAsset'), eq(variables.ComponentType, 'generator'), and(eq(variables.ComponentType, 'codeExtension'), eq(variables.Language, 'js'))), eq(variables.PublishPackageArtifacts, true)) jobs: - job: pack_npm displayName: 'Build & Pack Node project' steps: - - template: npm-build-steps.yml + - template: yarn-build-steps.yml + - template: yarn-test-steps.yml - template: npm-versioning-steps.yml - - template: npm-package-steps.yml + - template: yarn-package-steps.yml + - template: debug-workspace-steps.yml - stage: stage_build_nuget displayName: '.NET: Build & Test' @@ -29,6 +33,7 @@ stages: - job: build_test_dotnet displayName: Build and test .csproj with dotnet steps: + - template: dotnet-install-sdk-steps.yml - template: dotnet-build-test-steps.yml - stage: stage_package_nuget @@ -41,6 +46,7 @@ stages: condition: eq(variables.ComponentType, 'codeExtension') steps: - template: nuget-versioning-steps.yml + - template: dotnet-install-sdk-steps.yml - template: dotnet-package-steps.yml - template: nuget-signing-steps.yml - job: job_pack_nuspec diff --git a/build/yaml/templates/dotnet-build-test-steps.yml b/build/yaml/templates/dotnet-build-test-steps.yml index 22a9134b0c..cf6901e5b2 100644 --- a/build/yaml/templates/dotnet-build-test-steps.yml +++ b/build/yaml/templates/dotnet-build-test-steps.yml @@ -1,14 +1,4 @@ steps: -- task: UseDotNet@2 - displayName: 'Use .Net Core sdk 2.1.x' - inputs: - version: 2.1.x - -- task: UseDotNet@2 - displayName: 'Use .Net Core sdk 3.1.x' - inputs: - version: 3.1.x - - task: DotNetCoreCLI@2 displayName: 'Run `dotnet restore`' inputs: @@ -28,5 +18,5 @@ steps: displayName: 'Run `dotnet test`' inputs: command: test - projects: 'tests\**\*.csproj' + projects: 'tests\unit\**\*.csproj' arguments: '--configuration $(BuildConfiguration)' diff --git a/build/yaml/templates/dotnet-install-sdk-steps.yml b/build/yaml/templates/dotnet-install-sdk-steps.yml new file mode 100644 index 0000000000..772e09f578 --- /dev/null +++ b/build/yaml/templates/dotnet-install-sdk-steps.yml @@ -0,0 +1,10 @@ +steps: +- task: UseDotNet@2 + displayName: 'Use .Net Core sdk 2.1.x' + inputs: + version: 2.1.x + +- task: UseDotNet@2 + displayName: 'Use .Net Core sdk 3.1.x' + inputs: + version: 3.1.x diff --git a/build/yaml/templates/npm-versioning-steps.yml b/build/yaml/templates/npm-versioning-steps.yml index 9cb7e95fb1..a028d41658 100644 --- a/build/yaml/templates/npm-versioning-steps.yml +++ b/build/yaml/templates/npm-versioning-steps.yml @@ -61,5 +61,3 @@ steps: tags: | NpmPackageVersion: $(NpmPackageVersion) continueOnError: true - -- template: debug-workspace-steps.yml \ No newline at end of file diff --git a/build/yaml/templates/npm-build-steps.yml b/build/yaml/templates/yarn-build-steps.yml similarity index 79% rename from build/yaml/templates/npm-build-steps.yml rename to build/yaml/templates/yarn-build-steps.yml index b899f99240..6aa80571f1 100644 --- a/build/yaml/templates/npm-build-steps.yml +++ b/build/yaml/templates/yarn-build-steps.yml @@ -11,7 +11,5 @@ steps: - script: | yarn exec npm run --if-present build - displayName: 'Run build if it exists' + displayName: 'Run `yarn build` if it exists' workingDirectory: '$(WorkingDirectory)' - -- template: debug-workspace-steps.yml diff --git a/build/yaml/templates/npm-package-steps.yml b/build/yaml/templates/yarn-package-steps.yml similarity index 63% rename from build/yaml/templates/npm-package-steps.yml rename to build/yaml/templates/yarn-package-steps.yml index 4a0b9a71b8..4befed4fc2 100644 --- a/build/yaml/templates/npm-package-steps.yml +++ b/build/yaml/templates/yarn-package-steps.yml @@ -1,10 +1,19 @@ steps: - powershell: | if (Test-Path -Path package.json) { - # Get package.json ojbect + # Get package.json object $packageJson = Get-Content package.json; $packageJsonData = $packageJson | ConvertFrom-Json; + # Get name, strip invalid filename characters up to '/' and save as pipeline variable + $packageName = $packageJsonData.name + "Package name = $packageName"; + + $packageName = $packageName -replace ".*\/" + "Package file name = $packageName"; + + Write-Host "##vso[task.setvariable variable=NpmPackageFileName;]$packageName" + # Get variable passed from Setup stage $packageVersion = "$(NpmPackageVersion)"; @@ -21,22 +30,20 @@ steps: workingDirectory: '$(WorkingDirectory)' - script: | - yarn pack - displayName: 'Generate package' + yarn pack --filename $(NpmPackageFileName)-$(NpmPackageVersion).tgz + displayName: 'Run `yarn pack`' workingDirectory: '$(WorkingDirectory)' - task: CopyFiles@2 - displayName: 'Copy packages' + displayName: 'Copy packages to staging directory' inputs: SourceFolder: '$(WorkingDirectory)' Contents: '*.tgz' TargetFolder: '$(Build.ArtifactStagingDirectory)\npm' - task: PublishBuildArtifacts@1 - displayName: 'Publish the npm package' + displayName: 'Publish the npm package to artifacts' inputs: PathtoPublish: '$(Build.ArtifactStagingDirectory)\npm' ArtifactName: 'npm' publishLocation: 'Container' - -- template: debug-workspace-steps.yml diff --git a/build/yaml/templates/yarn-test-steps.yml b/build/yaml/templates/yarn-test-steps.yml new file mode 100644 index 0000000000..477fcbcca6 --- /dev/null +++ b/build/yaml/templates/yarn-test-steps.yml @@ -0,0 +1,26 @@ +steps: +# You are probably wondering "Why not just run 'yarn exec npm run --if-present test'?". +# On Azure Pipelines, yarn install by default restores modules to a global node_modules cache +# instead of a local package cache. When running the npm CLI, it is unable to resolve required +# dependencies as a result, whereas yarn run