diff --git a/tests/functional/.editorconfig b/tests/functional/.editorconfig deleted file mode 100644 index 6e13120105..0000000000 --- a/tests/functional/.editorconfig +++ /dev/null @@ -1,162 +0,0 @@ -# EditorConfig is awesome:http://EditorConfig.org - -############################### -# Core EditorConfig Options # -############################### -root = true - -# All files -[*] -indent_style = space -# (Please don't specify an indent_size here; that has too many unintended consequences.) - -# Xml project files -[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] -indent_size = 2 - -# Xml config files -[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}] -indent_size = 2 - -# JSON files -[*.json] -indent_size = 2 - -# Schema files -[*.{schema,uischema}] -indent_size = 4 - -# Code files -[*.{cs,csx,vb,vbx}] -indent_size = 4 -insert_final_newline = true -charset = utf-8-bom - -############################### -# .NET Coding Conventions # -############################### -[*.{cs,vb}] -# Organize usings -dotnet_sort_system_directives_first = true -dotnet_separate_import_directive_groups = false - -# this. preferences -dotnet_style_qualification_for_field = false:suggestion -dotnet_style_qualification_for_property = false:suggestion -dotnet_style_qualification_for_method = false:suggestion -dotnet_style_qualification_for_event = false:suggestion - -# Language keywords vs BCL types preferences -dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion -dotnet_style_predefined_type_for_member_access = true:suggestion - -# Parentheses preferences -dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent -dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent -dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent -dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent -# Modifier preferences -dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion -dotnet_style_readonly_field = true:suggestion - -# Expression-level preferences -dotnet_style_object_initializer = true:suggestion -dotnet_style_collection_initializer = true:suggestion -dotnet_style_explicit_tuple_names = true:suggestion -dotnet_style_null_propagation = true:suggestion -dotnet_style_coalesce_expression = true:suggestion -dotnet_style_prefer_is_null_check_over_reference_equality_method = true:none -dotnet_prefer_inferred_tuple_names = true:suggestion -dotnet_prefer_inferred_anonymous_type_member_names = true:suggestion -dotnet_style_prefer_auto_properties = true:suggestion -dotnet_style_prefer_conditional_expression_over_assignment = true:silent -dotnet_style_prefer_conditional_expression_over_return = true:silent - -############################### -# Naming Conventions # -############################### - -# Style Definitions -dotnet_naming_style.pascal_case_style.capitalization = pascal_case - -# Use PascalCase for constant fields -dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion -dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields -dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style -dotnet_naming_symbols.constant_fields.applicable_kinds = field -dotnet_naming_symbols.constant_fields.applicable_accessibilities = * -dotnet_naming_symbols.constant_fields.required_modifiers = const - -############################### -# C# Code Style Rules # -############################### -[*.cs] -# var preferences -csharp_style_var_for_built_in_types = true:suggestion -csharp_style_var_when_type_is_apparent = true:suggestion -csharp_style_var_elsewhere = true:suggestion - -# Expression-bodied members -csharp_style_expression_bodied_methods = false:none -csharp_style_expression_bodied_constructors = false:none -csharp_style_expression_bodied_operators = true:suggestion -csharp_style_expression_bodied_properties = true:suggestion -csharp_style_expression_bodied_indexers = true:suggestion -csharp_style_expression_bodied_accessors = true:suggestion - -# Pattern-matching preferences -csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion -csharp_style_pattern_matching_over_as_with_null_check = true:suggestion - -# Null-checking preferences -csharp_style_throw_expression = true:suggestion -csharp_style_conditional_delegate_call = true:suggestion - -# Modifier preferences -csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion - -# Expression-level preferences -csharp_prefer_braces = true:suggestion -csharp_style_deconstructed_variable_declaration = true:suggestion -csharp_prefer_simple_default_expression = true:suggestion -csharp_style_pattern_local_over_anonymous_function = true:suggestion -csharp_style_inlined_variable_declaration = true:suggestion - -############################### -# C# Formatting Rules # -############################### -# New line preferences -csharp_new_line_before_open_brace = all -csharp_new_line_before_else = true -csharp_new_line_before_catch = true -csharp_new_line_before_finally = true -csharp_new_line_before_members_in_object_initializers = true -csharp_new_line_before_members_in_anonymous_types = true -csharp_new_line_between_query_expression_clauses = true - -# Indentation preferences -csharp_indent_case_contents = true -csharp_indent_switch_labels = true -csharp_indent_labels = flush_left - -# Space preferences -csharp_space_after_cast = false -csharp_space_after_keywords_in_control_flow_statements = true -csharp_space_between_method_call_parameter_list_parentheses = false -csharp_space_between_method_declaration_parameter_list_parentheses = false -csharp_space_between_parentheses = false -csharp_space_before_colon_in_inheritance_clause = true -csharp_space_after_colon_in_inheritance_clause = true -csharp_space_around_binary_operators = before_and_after -csharp_space_between_method_declaration_empty_parameter_list_parentheses = false -csharp_space_between_method_call_name_and_opening_parenthesis = false -csharp_space_between_method_call_empty_parameter_list_parentheses = false -csharp_space_after_comma = true -csharp_space_after_dot = false - -# Wrapping preferences -csharp_preserve_single_line_statements = true -csharp_preserve_single_line_blocks = true - -# CS0618: Type or member is obsolete -dotnet_diagnostic.CS0618.severity = none diff --git a/tests/functional/.gitignore b/tests/functional/.gitignore deleted file mode 100644 index 70b79471d0..0000000000 --- a/tests/functional/.gitignore +++ /dev/null @@ -1,377 +0,0 @@ -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. -## -## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore - -# User-specific files -*.rsuser -*.suo -*.user -*.userosscache -*.sln.docstates - -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs - -# Mono auto generated files -mono_crash.* - -# Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -x64/ -x86/ -[Aa][Rr][Mm]/ -[Aa][Rr][Mm]64/ -bld/ -[Bb]in/ -[Oo]bj/ -[Ll]og/ -[Ll]ogs/ - -# Visual Studio 2015/2017 cache/options directory -.vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot -#wwwroot/ - -# Visual Studio 2017 auto generated files -Generated\ Files/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -# NUnit -*.VisualState.xml -TestResult.xml -nunit-*.xml - -# Build Results of an ATL Project -[Dd]ebugPS/ -[Rr]eleasePS/ -dlldata.c - -# Benchmark Results -BenchmarkDotNet.Artifacts/ - -# .NET Core -project.lock.json -project.fragment.lock.json -artifacts/ - -# StyleCop -StyleCopReport.xml - -# Files built by Visual Studio -*_i.c -*_p.c -*_h.h -*.ilk -*.meta -*.obj -*.iobj -*.pch -*.pdb -*.ipdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*_wpftmp.csproj -*.log -*.vspscc -*.vssscc -.builds -*.pidb -*.svclog -*.scc - -# Chutzpah Test files -_Chutzpah* - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opendb -*.opensdf -*.sdf -*.cachefile -*.VC.db -*.VC.VC.opendb - -# Visual Studio profiler -*.psess -*.vsp -*.vspx -*.sap - -# Visual Studio Trace Files -*.e2e - -# TFS 2012 Local Workspace -$tf/ - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# AxoCover is a Code Coverage Tool -.axoCover/* -!.axoCover/settings.json - -# Visual Studio code coverage results -*.coverage -*.coveragexml - -# NCrunch -_NCrunch_* -.*crunch*.local.xml -nCrunchTemp_* - -# MightyMoose -*.mm.* -AutoTest.Net/ - -# Web workbench (sass) -.sass-cache/ - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.[Pp]ublish.xml -*.azurePubxml -# Note: Comment the next line if you want to checkin your web deploy settings, -# but database connection strings (with potential passwords) will be unencrypted -*.pubxml -*.publishproj -PublishProfiles/ -**/Properties/ServiceDependencies/ -**/.config - -# Microsoft Azure Web App publish settings. Comment the next line if you want to -# checkin your Azure Web App publish settings, but sensitive information contained -# in these scripts will be unencrypted -PublishScripts/ - -# NuGet Packages -*.nupkg -# NuGet Symbol Packages -*.snupkg -# The packages folder can be ignored because of Package Restore -**/[Pp]ackages/* -# except build/, which is used as an MSBuild target. -!**/[Pp]ackages/build/ -# Uncomment if necessary however generally it will be regenerated when needed -#!**/[Pp]ackages/repositories.config -# NuGet v3's project.json files produces more ignorable files -*.nuget.props -*.nuget.targets - -# Microsoft Azure Build Output -csx/ -*.build.csdef - -# Microsoft Azure Emulator -ecf/ -rcf/ - -# Windows Store app package directories and files -AppPackages/ -BundleArtifacts/ -Package.StoreAssociation.xml -_pkginfo.txt -*.appx -*.appxbundle -*.appxupload - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!?*.[Cc]ache/ - -# Others -ClientBin/ -~$* -*~ -*.dbmdl -*.dbproj.schemaview -*.jfm -*.pfx -*.publishsettings -orleans.codegen.cs - -# Including strong name files can present a security risk -# (https://github.com/github/gitignore/pull/2483#issue-259490424) -#*.snk - -# Since there are multiple workflows, uncomment next line to ignore bower_components -# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) -#bower_components/ - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file -# to a newer Visual Studio version. Backup files are not needed, -# because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm -ServiceFabricBackup/ -*.rptproj.bak - -# SQL Server files -*.mdf -*.ldf -*.ndf - -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings -*.rptproj.rsuser -*- [Bb]ackup.rdl -*- [Bb]ackup ([0-9]).rdl -*- [Bb]ackup ([0-9][0-9]).rdl - -# Microsoft Fakes -FakesAssemblies/ - -# GhostDoc plugin setting file -*.GhostDoc.xml - -# Node.js Tools for Visual Studio -.ntvs_analysis.dat -node_modules/ - -/**/package-lock.json - -# Visual Studio 6 build log -*.plg - -# Visual Studio 6 workspace options file -*.opt - -# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) -*.vbw - -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions - -# Paket dependency manager -.paket/paket.exe -paket-files/ - -# FAKE - F# Make -.fake/ - -# CodeRush personal settings -.cr/personal - -# Python Tools for Visual Studio (PTVS) -__pycache__/ -*.pyc - -# Cake - Uncomment if you are using it -# tools/** -# !tools/packages.config - -# Tabs Studio -*.tss - -# Telerik's JustMock configuration file -*.jmconfig - -# BizTalk build output -*.btp.cs -*.btm.cs -*.odx.cs -*.xsd.cs - -# OpenCover UI analysis results -OpenCover/ - -# Azure Stream Analytics local run output -ASALocalRun/ - -# MSBuild Binary and Structured Log -*.binlog - -# NVidia Nsight GPU debugger configuration file -*.nvuser - -# MFractors (Xamarin productivity tool) working folder -.mfractor/ - -# Local History for Visual Studio -.localhistory/ - -# BeatPulse healthcheck temp database -healthchecksdb - -# Backup folder for Package Reference Convert tool in Visual Studio 2017 -MigrationBackup/ - -# Ionide (cross platform F# VS Code tools) working folder -.ionide/ - -# Python virtual env -venv/ -virtualenv/ - -# Local appsettings -appsettings.Development.json - -# VS code settings -.vscode/ - -# PyCharm settings -.idea/ - -# Ignore correct yarn files (no zero install support) -.yarn/* -!.yarn/patches -!.yarn/releases -!.yarn/plugins -!.yarn/sdks -!.yarn/versions -.pnp.* diff --git a/tests/functional/.yarnrc.yml b/tests/functional/.yarnrc.yml deleted file mode 100644 index b758837b73..0000000000 --- a/tests/functional/.yarnrc.yml +++ /dev/null @@ -1 +0,0 @@ -yarnPath: ".yarn/releases/yarn-berry.cjs" diff --git a/tests/functional/Bots/.gitignore b/tests/functional/Bots/.gitignore deleted file mode 100644 index 6e87fc983e..0000000000 --- a/tests/functional/Bots/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# Exclude ComposerDialogs folder created for local deployment -**/azurewebapp/ComposerDialogs \ No newline at end of file diff --git a/tests/functional/Bots/JavaScript/.gitignore b/tests/functional/Bots/JavaScript/.gitignore deleted file mode 100644 index 52b57b278e..0000000000 --- a/tests/functional/Bots/JavaScript/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ - -# Ignore web.config files generated to deploy JS to IIS on Windows -**/web.config \ No newline at end of file diff --git a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/SimpleHostBot/.env b/tests/functional/Bots/JavaScript/Consumers/CodeFirst/SimpleHostBot/.env deleted file mode 100644 index 34470cb8a8..0000000000 --- a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/SimpleHostBot/.env +++ /dev/null @@ -1,24 +0,0 @@ -MicrosoftAppId= -MicrosoftAppPassword= -SkillHostEndpoint=http://localhost:36000/api/skills/ - -skill_EchoSkillBotComposerDotNet_appId= -skill_EchoSkillBotComposerDotNet_endpoint=http://localhost:35410/api/messages - -skill_EchoSkillBotDotNet_appId= -skill_EchoSkillBotDotNet_endpoint=http://localhost:35400/api/messages - -skill_EchoSkillBotDotNet21_appId= -skill_EchoSkillBotDotNet21_endpoint=http://localhost:35405/api/messages - -skill_EchoSkillBotDotNetV3_appId= -skill_EchoSkillBotDotNetV3_endpoint=http://localhost:35407/api/messages - -skill_EchoSkillBotJS_appId= -skill_EchoSkillBotJS_endpoint=http://localhost:36400/api/messages - -skill_EchoSkillBotJSV3_appId= -skill_EchoSkillBotJSV3_endpoint=http://localhost:36407/api/messages - -skill_EchoSkillBotPython_appId= -skill_EchoSkillBotPython_endpoint=http://localhost:37400/api/messages diff --git a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/SimpleHostBot/authentication/allowedSkillsClaimsValidator.js b/tests/functional/Bots/JavaScript/Consumers/CodeFirst/SimpleHostBot/authentication/allowedSkillsClaimsValidator.js deleted file mode 100644 index 39435af7c7..0000000000 --- a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/SimpleHostBot/authentication/allowedSkillsClaimsValidator.js +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const { JwtTokenValidation, SkillValidation } = require('botframework-connector'); -const { SkillsConfiguration } = require('../skillsConfiguration'); - -const skillsConfig = new SkillsConfiguration(); -// Load the appIds for the configured skills (we will only allow responses from skills we have configured). -const allowedSkills = Object.values(skillsConfig.skills).map(skill => skill.appId); - -/** - * Sample claims validator that loads an allowed list from configuration if present - * and checks that responses are coming from configured skills. - */ -const allowedSkillsClaimsValidator = async (claims) => { - if (SkillValidation.isSkillClaim(claims)) { - // Check that the appId claim in the skill request is in the list of skills configured for this bot. - const appId = JwtTokenValidation.getAppIdFromClaims(claims); - if (!allowedSkills.includes(appId)) { - throw new Error(`Received a request from an application with an appID of "${appId}". To enable requests from this skill, add the skill to your configuration file.`); - } - } -}; - -module.exports.allowedSkillsClaimsValidator = allowedSkillsClaimsValidator; diff --git a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/SimpleHostBot/bots/hostBot.js b/tests/functional/Bots/JavaScript/Consumers/CodeFirst/SimpleHostBot/bots/hostBot.js deleted file mode 100644 index 11c5cebf4a..0000000000 --- a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/SimpleHostBot/bots/hostBot.js +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const { ActivityHandler, ActivityTypes, DeliveryModes, MessageFactory } = require('botbuilder'); - -class HostBot extends ActivityHandler { - constructor (dialog, conversationState, skillsConfig, skillClient) { - super(); - if (!conversationState) throw new Error('[HostBot]: Missing parameter. conversationState is required'); - if (!skillsConfig) throw new Error('[HostBot]: Missing parameter. skillsConfig is required'); - if (!skillClient) throw new Error('[HostBot]: Missing parameter. skillClient is required'); - - this.conversationState = conversationState; - this.skillsConfig = skillsConfig; - this.skillClient = skillClient; - this.dialog = dialog; - this.dialogStateProperty = this.conversationState.createProperty('DialogState'); - - this.botId = process.env.MicrosoftAppId; - - // Create state property to track the delivery mode and active skill. - this.deliveryModeProperty = this.conversationState.createProperty(HostBot.DeliveryModePropertyName); - this.activeSkillProperty = this.conversationState.createProperty(HostBot.ActiveSkillPropertyName); - - this.onTurn(async (context, next) => { - // Forward all activities except EndOfConversation to the active skill. - if (context.activity.type !== ActivityTypes.EndOfConversation) { - // Try to get the active skill. - const activeSkill = await this.activeSkillProperty.get(context); - - if (activeSkill) { - const deliveryMode = await this.deliveryModeProperty.get(context); - - // Send the activity to the skill - await this.sendToSkill(context, deliveryMode, activeSkill); - return; - } - } - - await next(); - }); - - // See https://aka.ms/about-bot-activity-message to learn more about the message and other activity types. - this.onMessage(async (context, next) => { - const deliveryMode = await this.deliveryModeProperty.get(context); - const selectedSkill = this.skillsConfig.skills[context.activity.text]; - const v3Bots = ['EchoSkillBotDotNetV3', 'EchoSkillBotJSV3']; - - if (selectedSkill && deliveryMode === DeliveryModes.ExpectReplies && v3Bots.includes(selectedSkill.id)) { - const message = MessageFactory.text("V3 Bots do not support 'expectReplies' delivery mode."); - await context.sendActivity(message); - - // Forget delivery mode and skill invocation. - await this.deliveryModeProperty.delete(context); - await this.activeSkillProperty.delete(context); - - // Restart setup dialog - await this.conversationState.delete(context); - } - - await this.dialog.run(context, this.dialogStateProperty); - }); - - this.onEndOfConversation(async (context, next) => { - // Handle EndOfConversation returned by the skill. - await this.EndConversation(context.activity, context); - - // By calling next() you ensure that the next BotHandler is run. - await next(); - }); - - this.onMembersAdded(async (context, next) => { - const membersAdded = context.activity.membersAdded; - for (let cnt = 0; cnt < membersAdded.length; ++cnt) { - if (membersAdded[cnt].id !== context.activity.recipient.id) { - await context.sendActivity('Hello and welcome!'); - await this.dialog.run(context, this.dialogStateProperty); - } - } - - // By calling next() you ensure that the next BotHandler is run. - await next(); - }); - } - - async EndConversation (activity, context) { - if (activity.type === ActivityTypes.EndOfConversation) { - // Forget delivery mode and skill invocation. - await this.deliveryModeProperty.delete(context); - await this.activeSkillProperty.delete(context); - - // Show status message, text and value returned by the skill. - let eocActivityMessage = `Received ${ActivityTypes.EndOfConversation}.\n\nCode: ${activity.code}.`; - if (activity.text) { - eocActivityMessage += `\n\nText: ${activity.text}`; - } - - if (activity.value) { - eocActivityMessage += `\n\nValue: ${activity.value}`; - } - - await context.sendActivity(eocActivityMessage); - - // We are back at the host. - await context.sendActivity('Back in the host bot.'); - - // Restart setup dialog - await this.dialog.run(context, this.dialogStateProperty); - - await this.conversationState.saveChanges(context); - } - } - - async sendToSkill (context, deliveryMode, targetSkill) { - // NOTE: Always SaveChanges() before calling a skill so that any activity generated by the skill - // will have access to current accurate state. - await this.conversationState.saveChanges(context, true); - - if (deliveryMode === DeliveryModes.ExpectReplies) { - // Clone activity and update its delivery mode. - const activity = JSON.parse(JSON.stringify(context.activity)); - activity.deliveryMode = deliveryMode; - - // Route the activity to the skill. - const expectRepliesResponse = await this.skillClient.postToSkill(this.botId, targetSkill, this.skillsConfig.skillHostEndpoint, activity); - - // Check response status. - if (!(expectRepliesResponse.status >= 200 && expectRepliesResponse.status <= 299)) { - throw new Error(`[HostBot]: Error invoking the skill id: "${targetSkill.id}" at "${targetSkill.skillEndpoint}" (status is ${expectRepliesResponse.status}). \r\n ${expectRepliesResponse.body}`); - } - - if (expectRepliesResponse.body && expectRepliesResponse.body.activities) { - // Route response activities back to the channel. - const responseActivities = expectRepliesResponse.body.activities; - - for (let index = 0; index < responseActivities.length; index++) { - if (responseActivities[index].type === ActivityTypes.EndOfConversation) { - await this.EndConversation(responseActivities[index], context); - } else { - await context.sendActivity(responseActivities[index]); - } - } - } - } else { - // Route the activity to the skill. - const response = await this.skillClient.postToSkill(this.botId, targetSkill, this.skillsConfig.skillHostEndpoint, context.activity); - - // Check response status. - if (!(response.status >= 200 && response.status <= 299)) { - throw new Error(`[HostBot]: Error invoking the skill id: "${targetSkill.id}" at "${targetSkill.skillEndpoint}" (status is ${response.status}). \r\n ${response.body}`); - } - } - } - - /** - * Override the ActivityHandler.run() method to save state changes after the bot logic completes. - */ - async run (context) { - await super.run(context); - - // Save any state changes. The load happened during the execution of the Dialog. - await this.conversationState.saveChanges(context, false); - } -} - -module.exports.HostBot = HostBot; -HostBot.ActiveSkillPropertyName = 'activeSkillProperty'; -HostBot.DeliveryModePropertyName = 'deliveryModeProperty'; diff --git a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/SimpleHostBot/dialogs/setupDialog.js b/tests/functional/Bots/JavaScript/Consumers/CodeFirst/SimpleHostBot/dialogs/setupDialog.js deleted file mode 100644 index 77e25eee63..0000000000 --- a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/SimpleHostBot/dialogs/setupDialog.js +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const { InputHints, MessageFactory, DeliveryModes } = require('botbuilder'); -const { ChoicePrompt, ComponentDialog, DialogSet, DialogTurnStatus, WaterfallDialog, ListStyle } = require('botbuilder-dialogs'); -const { HostBot } = require('../bots/hostBot'); - -const SETUP_DIALOG = 'SetupDialog'; -const CHOICE_PROMPT = 'ChoicePrompt'; -const WATERFALL_DIALOG = 'WaterfallDialog'; - -/** - * The setup dialog for this bot. - */ -class SetupDialog extends ComponentDialog { - constructor (conversationState, skillsConfig) { - super(SETUP_DIALOG); - - this.deliveryModeProperty = conversationState.createProperty(HostBot.DeliveryModePropertyName); - this.activeSkillProperty = conversationState.createProperty(HostBot.ActiveSkillPropertyName); - this.skillsConfig = skillsConfig; - this.deliveryMode = ''; - - // Define the setup dialog and its related components. - // Add ChoicePrompt to render available skills. - this.addDialog(new ChoicePrompt(CHOICE_PROMPT)) - // Add main waterfall dialog for this bot. - .addDialog(new WaterfallDialog(WATERFALL_DIALOG, [ - this.selectDeliveryModeStep.bind(this), - this.selectSkillStep.bind(this), - this.finalStep.bind(this) - ])); - - this.initialDialogId = WATERFALL_DIALOG; - } - - /** - * The run method handles the incoming activity (in the form of a TurnContext) and passes it through the dialog system. - * If no dialog is active, it will start the default dialog. - * @param {*} turnContext - * @param {*} accessor - */ - async run (turnContext, accessor) { - const dialogSet = new DialogSet(accessor); - dialogSet.add(this); - - const dialogContext = await dialogSet.createContext(turnContext); - const results = await dialogContext.continueDialog(); - if (results.status === DialogTurnStatus.empty) { - await dialogContext.beginDialog(this.id); - } - } - - /** - * Render a prompt to select the delivery mode to use. - */ - async selectDeliveryModeStep (stepContext) { - // Create the PromptOptions with the delivery modes supported. - const messageText = 'What delivery mode would you like to use?'; - const repromptMessageText = 'That was not a valid choice, please select a valid delivery mode.'; - const options = { - prompt: MessageFactory.text(messageText, messageText, InputHints.ExpectingInput), - retryPrompt: MessageFactory.text(repromptMessageText, repromptMessageText, InputHints.ExpectingInput), - choices: [DeliveryModes.Normal, DeliveryModes.ExpectReplies] - }; - - // Prompt the user to select a delivery mode. - return await stepContext.prompt(CHOICE_PROMPT, options); - } - - /** - * Render a prompt to select the skill to call. - */ - async selectSkillStep (stepContext) { - // Set delivery mode. - this.deliveryMode = stepContext.result.value; - await this.deliveryModeProperty.set(stepContext.context, stepContext.result.value); - - // Create the PromptOptions from the skill configuration which contains the list of configured skills. - const messageText = 'What skill would you like to call?'; - const repromptMessageText = 'That was not a valid choice, please select a valid skill.'; - const options = { - prompt: MessageFactory.text(messageText, messageText, InputHints.ExpectingInput), - retryPrompt: MessageFactory.text(repromptMessageText, repromptMessageText, InputHints.ExpectingInput), - choices: Object.keys(this.skillsConfig.skills), - style: ListStyle.suggestedAction - }; - - // Prompt the user to select a skill. - return await stepContext.prompt(CHOICE_PROMPT, options); - } - - /** - * The SetupDialog has ended, we go back to the HostBot to connect with the selected skill. - */ - async finalStep (stepContext) { - const selectedSkill = this.skillsConfig.skills[stepContext.result.value]; - const v3Bots = ['EchoSkillBotDotNetV3', 'EchoSkillBotJSV3']; - - // Set active skill - await this.activeSkillProperty.set(stepContext.context, selectedSkill); - - if (this.deliveryMode === DeliveryModes.ExpectReplies && v3Bots.includes(selectedSkill.id)) { - const message = MessageFactory.text("V3 Bots do not support 'expectReplies' delivery mode."); - await stepContext.context.sendActivity(message); - - // Forget delivery mode and skill invocation. - await this.deliveryModeProperty.delete(stepContext.context); - await this.activeSkillProperty.delete(stepContext.context); - - // Restart setup dialog - return await stepContext.replaceDialog(this.initialDialogId); - } - - const message = MessageFactory.text('Type anything to send to the skill.', 'Type anything to send to the skill.', InputHints.ExpectingInput); - await stepContext.context.sendActivity(message); - - return await stepContext.endDialog(); - } -} - -module.exports.SetupDialog = SetupDialog; diff --git a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/SimpleHostBot/index.js b/tests/functional/Bots/JavaScript/Consumers/CodeFirst/SimpleHostBot/index.js deleted file mode 100644 index e6e0a329e0..0000000000 --- a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/SimpleHostBot/index.js +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -// index.js is used to setup and configure your bot - -// Import required packages -const http = require('http'); -const https = require('https'); -const path = require('path'); -const restify = require('restify'); - -// Import required bot services. -// See https://aka.ms/bot-services to learn more about the different parts of a bot. -const { BotFrameworkAdapter, TurnContext, ActivityTypes, ChannelServiceRoutes, ConversationState, InputHints, MemoryStorage, SkillHandler, SkillHttpClient, MessageFactory, SkillConversationIdFactory } = require('botbuilder'); -const { AuthenticationConfiguration, SimpleCredentialProvider } = require('botframework-connector'); - -// Import required bot configuration. -const ENV_FILE = path.join(__dirname, '.env'); -require('dotenv').config({ path: ENV_FILE }); - -// This bot's main dialog. -const { HostBot } = require('./bots/hostBot'); -const { SkillsConfiguration } = require('./skillsConfiguration'); -const { allowedSkillsClaimsValidator } = require('./authentication/allowedSkillsClaimsValidator'); -const { SetupDialog } = require('./dialogs/setupDialog'); - -const maxTotalSockets = (preallocatedSnatPorts, procCount = 1, weight = 0.5, overcommit = 1.1) => - Math.min( - Math.floor((preallocatedSnatPorts / procCount) * weight * overcommit), - preallocatedSnatPorts - ); - -// Create adapter. -// See https://aka.ms/about-bot-adapter to learn more about adapters. -const adapter = new BotFrameworkAdapter({ - appId: process.env.MicrosoftAppId, - appPassword: process.env.MicrosoftAppPassword, - authConfig: new AuthenticationConfiguration([], allowedSkillsClaimsValidator), - clientOptions: { - agentSettings: { - http: new http.Agent({ - keepAlive: true, - maxTotalSockets: maxTotalSockets(1024, 4, 0.3) - }), - https: new https.Agent({ - keepAlive: true, - maxTotalSockets: maxTotalSockets(1024, 4, 0.7) - }) - } - } -}); - -// Catch-all for errors. -adapter.onTurnError = async (context, error) => { - // This check writes out errors to console log .vs. app insights. - // NOTE: In production environment, you should consider logging this to Azure - // application insights. - console.error(`\n [onTurnError] unhandled error: ${error}`); - - try { - const { message, stack } = error; - - // Send a message to the user. - let errorMessageText = 'The bot encountered an error or bug.'; - let errorMessage = MessageFactory.text(errorMessageText, errorMessageText, InputHints.IgnoringInput); - errorMessage.value = { message, stack }; - await context.sendActivity(errorMessage); - - await context.sendActivity(`Exception: ${message}`); - await context.sendActivity(stack); - - errorMessageText = 'To continue to run this bot, please fix the bot source code.'; - errorMessage = MessageFactory.text(errorMessageText, errorMessageText, InputHints.ExpectingInput); - await context.sendActivity(errorMessage); - - // Send a trace activity, which will be displayed in Bot Framework Emulator - await context.sendTraceActivity( - 'OnTurnError Trace', - `${error}`, - 'https://www.botframework.com/schemas/error', - 'TurnError' - ); - } catch (err) { - console.error(`\n [onTurnError] Exception caught in onTurnError : ${err}`); - } - - try { - // Inform the active skill that the conversation is ended so that it has - // a chance to clean up. - // Note: ActiveSkillPropertyName is set by the RooBot while messages are being - // forwarded to a Skill. - const activeSkill = await conversationState.createProperty(HostBot.ActiveSkillPropertyName).get(context); - if (activeSkill) { - const botId = process.env.MicrosoftAppId; - - let endOfConversation = { - type: ActivityTypes.EndOfConversation, - code: 'RootSkillError' - }; - endOfConversation = TurnContext.applyConversationReference( - endOfConversation, TurnContext.getConversationReference(context.activity), true); - - await conversationState.saveChanges(context, true); - await skillClient.postToSkill(botId, activeSkill, skillsConfig.skillHostEndpoint, endOfConversation); - } - } catch (err) { - console.error(`\n [onTurnError] Exception caught on attempting to send EndOfConversation : ${err}`); - } - - try { - // Clear out state - await conversationState.delete(context); - } catch (err) { - console.error(`\n [onTurnError] Exception caught on attempting to Delete ConversationState : ${err}`); - } -}; - -// Define a state store for your bot. See https://aka.ms/about-bot-state to learn more about using MemoryStorage. -// A bot requires a state store to persist the dialog and user state between messages. - -// For local development, in-memory storage is used. -// CAUTION: The Memory Storage used here is for local bot debugging only. When the bot -// is restarted, anything stored in memory will be gone. -const memoryStorage = new MemoryStorage(); -const conversationState = new ConversationState(memoryStorage); - -// Create the conversationIdFactory -const conversationIdFactory = new SkillConversationIdFactory(memoryStorage); - -// Load skills configuration -const skillsConfig = new SkillsConfiguration(); - -// Create the credential provider; -const credentialProvider = new SimpleCredentialProvider(process.env.MicrosoftAppId, process.env.MicrosoftAppPassword); - -// Create the skill client -const skillClient = new SkillHttpClient(credentialProvider, conversationIdFactory); - -// Create the main dialog. -const dialog = new SetupDialog(conversationState, skillsConfig); -const bot = new HostBot(dialog, conversationState, skillsConfig, skillClient); - -// Create HTTP server -const server = restify.createServer(); -server.listen(process.env.port || process.env.PORT || 36000, function () { - console.log(`\n${server.name} listening to ${server.url}`); - console.log('\nGet Bot Framework Emulator: https://aka.ms/botframework-emulator'); - console.log('\nTo talk to your bot, open the emulator select "Open Bot"'); -}); - -// Listen for incoming activities and route them to your bot main dialog. -server.post('/api/messages', (req, res) => { - // Route received a request to adapter for processing - adapter.processActivity(req, res, async (turnContext) => { - // route to bot activity handler. - await bot.run(turnContext); - }); -}); - -// Create and initialize the skill classes -const authConfig = new AuthenticationConfiguration([], allowedSkillsClaimsValidator); -const handler = new SkillHandler(adapter, bot, conversationIdFactory, credentialProvider, authConfig); -const skillEndpoint = new ChannelServiceRoutes(handler); -skillEndpoint.register(server, '/api/skills'); diff --git a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/SimpleHostBot/package.json b/tests/functional/Bots/JavaScript/Consumers/CodeFirst/SimpleHostBot/package.json deleted file mode 100644 index 2f072f539a..0000000000 --- a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/SimpleHostBot/package.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "simple-root-bot", - "version": "1.0.0", - "description": "A simple root bot for testing skills", - "author": "Microsoft", - "license": "MIT", - "main": "index.js", - "scripts": { - "start": "node ./index.js", - "watch": "nodemon ./index.js", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "repository": { - "type": "git", - "url": "https://github.com" - }, - "dependencies": { - "botbuilder": "4.14.0", - "botbuilder-dialogs": "~4.14.0", - "dotenv": "~8.2.0", - "restify": "~8.5.1" - }, - "devDependencies": { - "nodemon": "~2.0.4" - } -} diff --git a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/SimpleHostBot/skillsConfiguration.js b/tests/functional/Bots/JavaScript/Consumers/CodeFirst/SimpleHostBot/skillsConfiguration.js deleted file mode 100644 index 69d7bd19fe..0000000000 --- a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/SimpleHostBot/skillsConfiguration.js +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -/** - * A helper class that loads Skills information from configuration. - */ -class SkillsConfiguration { - constructor () { - this.skillsData = {}; - - const skillVariables = Object.keys(process.env).filter(prop => prop.startsWith('skill_')); - - for (const val of skillVariables) { - const names = val.split('_'); - const id = names[1]; - const attr = names[2]; - let propName; - if (!(id in this.skillsData)) { - this.skillsData[id] = { id: id }; - } - switch (attr.toLowerCase()) { - case 'appid': - propName = 'appId'; - break; - case 'endpoint': - propName = 'skillEndpoint'; - break; - case 'group': - propName = 'group'; - break; - default: - throw new Error(`[SkillsConfiguration]: Invalid environment variable declaration ${val}`); - } - - this.skillsData[id][propName] = process.env[val]; - } - - this.skillHostEndpointValue = process.env.SkillHostEndpoint; - if (!this.skillHostEndpointValue) { - throw new Error('[SkillsConfiguration]: Missing configuration parameter. SkillHostEndpoint is required'); - } - } - - get skills () { - return this.skillsData; - } - - get skillHostEndpoint () { - return this.skillHostEndpointValue; - } -} - -module.exports.SkillsConfiguration = SkillsConfiguration; diff --git a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/.env b/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/.env deleted file mode 100644 index cfef59e0a9..0000000000 --- a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/.env +++ /dev/null @@ -1,52 +0,0 @@ -MicrosoftAppId= -MicrosoftAppPassword= -SkillHostEndpoint=http://localhost:36020/api/skills/ -SsoConnectionName= - -skill_EchoSkillBotDotNet_group=Echo -skill_EchoSkillBotDotNet_appId= -skill_EchoSkillBotDotNet_endpoint=http://localhost:35400/api/messages - -skill_EchoSkillBotDotNet21_group=Echo -skill_EchoSkillBotDotNet21_appId= -skill_EchoSkillBotDotNet21_endpoint=http://localhost:35405/api/messages - -skill_EchoSkillBotDotNetV3_group=Echo -skill_EchoSkillBotDotNetV3_appId= -skill_EchoSkillBotDotNetV3_endpoint=http://localhost:35407/api/messages - -skill_EchoSkillBotJS_group=Echo -skill_EchoSkillBotJS_appId= -skill_EchoSkillBotJS_endpoint=http://localhost:36400/api/messages - -skill_EchoSkillBotJSV3_group=Echo -skill_EchoSkillBotJSV3_appId= -skill_EchoSkillBotJSV3_endpoint=http://localhost:36407/api/messages - -skill_EchoSkillBotPython_group=Echo -skill_EchoSkillBotPython_appId= -skill_EchoSkillBotPython_endpoint=http://localhost:37400/api/messages - -skill_WaterfallSkillBotDotNet_group=Waterfall -skill_WaterfallSkillBotDotNet_appId= -skill_WaterfallSkillBotDotNet_endpoint=http://localhost:35420/api/messages - -skill_WaterfallSkillBotJS_group=Waterfall -skill_WaterfallSkillBotJS_appId= -skill_WaterfallSkillBotJS_endpoint=http://localhost:36420/api/messages - -skill_WaterfallSkillBotPython_group=Waterfall -skill_WaterfallSkillBotPython_appId= -skill_WaterfallSkillBotPython_endpoint=http://localhost:37420/api/messages - -skill_TeamsSkillBotDotNet_group=Teams -skill_TeamsSkillBotDotNet_appId= -skill_TeamsSkillBotDotNet_endpoint=http://localhost:35430/api/messages - -skill_TeamsSkillBotJS_group=Teams -skill_TeamsSkillBotJS_appId= -skill_TeamsSkillBotJS_endpoint=http://localhost:36430/api/messages - -skill_TeamsSkillBotPython_group=Teams -skill_TeamsSkillBotPython_appId= -skill_TeamsSkillBotPython_endpoint=http://localhost:37430/api/messages diff --git a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/Cards/welcomeCard.json b/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/Cards/welcomeCard.json deleted file mode 100644 index 1fc98a2237..0000000000 --- a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/Cards/welcomeCard.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", - "type": "AdaptiveCard", - "version": "1.0", - "body": [ - { - "type": "Image", - "url": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQtB3AwMUeNoq4gUBGe6Ocj8kyh3bXa9ZbV7u1fVKQoyKFHdkqU", - "size": "stretch" - }, - { - "type": "TextBlock", - "spacing": "Medium", - "size": "Medium", - "weight": "Bolder", - "text": "Welcome to the Skill Dialog Sample!", - "wrap": true, - "maxLines": 0, - "color": "Accent" - }, - { - "type": "TextBlock", - "size": "default", - "text": "This sample allows you to connect to a skill using a SkillDialog and invoke several actions.", - "wrap": true, - "maxLines": 0 - } - ] -} \ No newline at end of file diff --git a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/TokenExchangeSkillHandler.js b/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/TokenExchangeSkillHandler.js deleted file mode 100644 index 6f320128f0..0000000000 --- a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/TokenExchangeSkillHandler.js +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const { v4 } = require('uuid'); -const { ActivityEx, ActivityTypes, CardFactory, SkillHandler, tokenExchangeOperationName, TurnContext } = require('botbuilder'); -const { JwtTokenValidation } = require('botframework-connector'); - -const WATERFALL_SKILL_BOT = 'WaterfallSkillBot'; - -/** - * A SkillHandler specialized to support SSO Token exchanges. - */ -class TokenExchangeSkillHandler extends SkillHandler { - constructor (adapter, bot, conversationIdFactory, skillsConfig, skillClient, - credentialProvider, authConfig, channelProvider = null, logger = null) { - super(adapter, bot, conversationIdFactory, credentialProvider, authConfig, channelProvider); - this.adapter = adapter; - this.tokenExchangeProvider = adapter; - - if (!this.tokenExchangeProvider) { - throw new Error(`${adapter} does not support token exchange`); - } - - this.skillsConfig = skillsConfig; - this.skillClient = skillClient; - this.conversationIdFactory = conversationIdFactory; - this.logger = logger; - this.botId = process.env.MicrosoftAppId; - } - - async onSendToConversation (claimsIdentity, conversationId, activity) { - if (await this.interceptOAuthCards(claimsIdentity, activity)) { - return { id: v4() }; - } - - return await super.onSendToConversation(claimsIdentity, conversationId, activity); - } - - async onReplyToActivity (claimsIdentity, conversationId, activityId, activity) { - if (await this.interceptOAuthCards(claimsIdentity, activity)) { - return { id: v4() }; - } - - return await super.onReplyToActivity(claimsIdentity, conversationId, activityId, activity); - } - - getCallingSkill (claimsIdentity) { - const appId = JwtTokenValidation.getAppIdFromClaims(claimsIdentity.claims); - - if (!appId) { - return null; - } - - return Object.values(this.skillsConfig.skills).find(skill => skill.appId === appId); - } - - async interceptOAuthCards (claimsIdentity, activity) { - const oauthCardAttachment = activity.attachments ? activity.attachments.find(attachment => attachment.contentType === CardFactory.contentTypes.oauthCard) : null; - if (oauthCardAttachment) { - const targetSkill = this.getCallingSkill(claimsIdentity); - if (targetSkill) { - const oauthCard = oauthCardAttachment.content; - - if (oauthCard && oauthCard.tokenExchangeResource && oauthCard.tokenExchangeResource.uri) { - const context = new TurnContext(this.adapter, activity); - context.turnState.push('BotIdentity', claimsIdentity); - - // We need to know what connection name to use for the token exchange so we figure that out here - const connectionName = targetSkill.id.includes(WATERFALL_SKILL_BOT) ? process.env.SsoConnectionName : process.env.SsoConnectionNameTeams; - - if (!connectionName) { - throw new Error('The connection name cannot be null.'); - } - - // AAD token exchange - try { - const result = await this.tokenExchangeProvider.exchangeToken( - context, - connectionName, - activity.recipient.id, - { uri: oauthCard.tokenExchangeResource.uri } - ); - - if (result.token) { - // If token above is null, then SSO has failed and hence we return false. - // If not, send an invoke to the skill with the token. - return await this.sendTokenExchangeInvokeToSkill(activity, oauthCard.tokenExchangeResource.id, result.token, oauthCard.connectionName, targetSkill); - } - } catch (exception) { - // Show oauth card if token exchange fails. - this.logger.log('Unable to exchange token.', exception); - return false; - } - } - } - } - - return false; - } - - async sendTokenExchangeInvokeToSkill (incomingActivity, id, token, connectionName, targetSkill) { - const activity = ActivityEx.createReply(incomingActivity); - activity.type = ActivityTypes.Invoke; - activity.name = tokenExchangeOperationName; - activity.value = { - id: id, - token: token, - connectionName: connectionName - }; - - const skillConversationReference = await this.conversationIdFactory.getSkillConversationReference(incomingActivity.conversation.id); - activity.conversation = incomingActivity.conversation; - activity.serviceUrl = skillConversationReference.conversationReference.serviceUrl; - - // Route the activity to the skill - const response = await this.skillClient.postActivity(this.botId, targetSkill.appId, targetSkill.skillEndpoint, this.skillsConfig.skillHostEndpoint, activity.conversation.id, activity); - - // Check response status: true if success, false if failure - return response.status >= 200 && response.status <= 299; - } -} - -module.exports.TokenExchangeSkillHandler = TokenExchangeSkillHandler; diff --git a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/authentication/allowedSkillsClaimsValidator.js b/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/authentication/allowedSkillsClaimsValidator.js deleted file mode 100644 index 39435af7c7..0000000000 --- a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/authentication/allowedSkillsClaimsValidator.js +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const { JwtTokenValidation, SkillValidation } = require('botframework-connector'); -const { SkillsConfiguration } = require('../skillsConfiguration'); - -const skillsConfig = new SkillsConfiguration(); -// Load the appIds for the configured skills (we will only allow responses from skills we have configured). -const allowedSkills = Object.values(skillsConfig.skills).map(skill => skill.appId); - -/** - * Sample claims validator that loads an allowed list from configuration if present - * and checks that responses are coming from configured skills. - */ -const allowedSkillsClaimsValidator = async (claims) => { - if (SkillValidation.isSkillClaim(claims)) { - // Check that the appId claim in the skill request is in the list of skills configured for this bot. - const appId = JwtTokenValidation.getAppIdFromClaims(claims); - if (!allowedSkills.includes(appId)) { - throw new Error(`Received a request from an application with an appID of "${appId}". To enable requests from this skill, add the skill to your configuration file.`); - } - } -}; - -module.exports.allowedSkillsClaimsValidator = allowedSkillsClaimsValidator; diff --git a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/bots/rootBot.js b/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/bots/rootBot.js deleted file mode 100644 index 9fe1b3dbb3..0000000000 --- a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/bots/rootBot.js +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const { ActivityHandler, ActivityTypes, CardFactory, MessageFactory } = require('botbuilder'); -const { runDialog } = require('botbuilder-dialogs'); -const WelcomeCard = require('../cards/welcomeCard.json'); - -class RootBot extends ActivityHandler { - /** - * @param {import('botbuilder').ConversationState} conversationState - * @param {import('botbuilder').SkillHttpClient} skillClient - * @param {import('botbuilder-dialogs').Dialog} mainDialog - */ - constructor (conversationState, skillClient, mainDialog) { - super(); - if (!conversationState) throw new Error('[RootBot]: Missing parameter. conversationState is required'); - if (!mainDialog) throw new Error('[RootBot]: Missing parameter. mainDialog is required'); - - this.conversationState = conversationState; - this.skillClient = skillClient; - this.mainDialog = mainDialog; - this.botId = process.env.MicrosoftAppId; - - // Create state property to track the active skill - this.activeSkillProperty = this.conversationState.createProperty(RootBot.ActiveSkillPropertyName); - - this.onTurn(async (turnContext, next) => { - if (turnContext.activity.type !== ActivityTypes.ConversationUpdate) { - // Run the Dialog with the activity. - await runDialog(this.mainDialog, turnContext, this.conversationState.createProperty('DialogState')); - } - - await next(); - }); - - this.onMembersAdded(async (context, next) => { - const membersAdded = context.activity.membersAdded; - for (const member of membersAdded) { - if (member.id !== context.activity.recipient.id) { - const welcomeCard = CardFactory.adaptiveCard(WelcomeCard); - const activity = MessageFactory.attachment(welcomeCard); - activity.speak = 'Welcome to the waterfall host bot'; - await context.sendActivity(activity); - await runDialog(this.mainDialog, context, conversationState.createProperty('DialogState')); - } - } - - // By calling next() you ensure that the next BotHandler is run. - await next(); - }); - } - - /** - * Override the ActivityHandler.run() method to save state changes after the bot logic completes. - * @param {import('botbuilder').TurnContext} turnContext - */ - async run (context) { - await super.run(context); - - // Save any state changes. The load happened during the execution of the Dialog. - await this.conversationState.saveChanges(context, false); - } - - /** - * @param {import('botbuilder').TurnContext} turnContext - * @param {*} targetSkill - */ - async sendToSkill (context, targetSkill) { - // NOTE: Always SaveChanges() before calling a skill so that any activity generated by the skill - // will have access to current accurate state. - await this.conversationState.saveChanges(context, true); - - // route the activity to the skill - const response = await this.skillClient.postToSkill(this.botId, targetSkill, this.skillsConfig.skillHostEndpoint, context.activity); - - // Check response status - if (!(response.status >= 200 && response.status <= 299)) { - throw new Error(`[RootBot]: Error invoking the skill id: "${targetSkill.id}" at "${targetSkill.skillEndpoint}" (status is ${response.status}). \r\n ${response.body}`); - } - } -} - -module.exports.RootBot = RootBot; diff --git a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/dialogs/mainDialog.js b/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/dialogs/mainDialog.js deleted file mode 100644 index 8f3fb81e4c..0000000000 --- a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/dialogs/mainDialog.js +++ /dev/null @@ -1,314 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const { ActivityTypes, DeliveryModes, InputHints, MessageFactory } = require('botbuilder'); -const { ChoicePrompt, ChoiceFactory, ComponentDialog, DialogSet, SkillDialog, WaterfallDialog, DialogTurnStatus, ListStyle } = require('botbuilder-dialogs'); -const { RootBot } = require('../bots/rootBot'); -const { SsoDialog } = require('./sso/ssoDialog'); -const { TangentDialog } = require('./tangentDialog'); - -const MAIN_DIALOG = 'MainDialog'; -const DELIVERY_PROMPT = 'DeliveryModePrompt'; -const SKILL_GROUP_PROMPT = 'SkillGroupPrompt'; -const SKILL_PROMPT = 'SkillPrompt'; -const SKILL_ACTION_PROMPT = 'SkillActionPrompt'; -const TANGENT_DIALOG = 'TangentDialog'; -const WATERFALL_DIALOG = 'WaterfallDialog'; -const SSO_DIALOG_PREFIX = 'Sso'; - -class MainDialog extends ComponentDialog { - /** - * @param {import('botbuilder').ConversationState} conversationState - * @param {import('../skillsConfiguration').SkillsConfiguration} skillsConfig - * @param {import('botbuilder').SkillHttpClient} skillClient - * @param {import('../skillConversationIdFactory').SkillConversationIdFactory} conversationIdFactory - */ - constructor (conversationState, skillsConfig, skillClient, conversationIdFactory) { - super(MAIN_DIALOG); - - const botId = process.env.MicrosoftAppId; - - if (!conversationState) throw new Error('[MainDialog]: Missing parameter \'conversationState\' is required'); - if (!skillsConfig) throw new Error('[MainDialog]: Missing parameter \'skillsConfig\' is required'); - if (!skillClient) throw new Error('[MainDialog]: Missing parameter \'skillClient\' is required'); - if (!conversationIdFactory) throw new Error('[MainDialog]: Missing parameter \'conversationIdFactory\' is required'); - - this.deliveryModeProperty = conversationState.createProperty(RootBot.DeliveryModePropertyName); - this.activeSkillProperty = conversationState.createProperty(RootBot.ActiveSkillPropertyName); - this.skillsConfig = skillsConfig; - this.deliveryMode = ''; - - // Register the tangent dialog for testing tangents and resume. - this.addDialog(new TangentDialog(TANGENT_DIALOG)); - - // Create and add SkillDialog instances for the configured skills. - this.addSkillDialogs(conversationState, conversationIdFactory, skillClient, skillsConfig, botId); - - // Add ChoicePrompt to render available delivery modes. - this.addDialog(new ChoicePrompt(DELIVERY_PROMPT)); - - // Add ChoicePrompt to render available groups of skills. - this.addDialog(new ChoicePrompt(SKILL_GROUP_PROMPT)); - - // Add ChoicePrompt to render available skills. - this.addDialog(new ChoicePrompt(SKILL_PROMPT)); - - // Add ChoicePrompt to render skill actions. - this.addDialog(new ChoicePrompt(SKILL_ACTION_PROMPT)); - - // Special case: register SSO dialogs for skills that support SSO actions. - this.addSsoDialogs(); - - this.addDialog(new WaterfallDialog(WATERFALL_DIALOG, [ - this.selectDeliveryModeStep.bind(this), - this.selectSkillGroupStep.bind(this), - this.selectSkillStep.bind(this), - this.selectSkillActionStep.bind(this), - this.callSkillActionStep.bind(this), - this.finalStep.bind(this) - ])); - - this.initialDialogId = WATERFALL_DIALOG; - } - - /** - * The run method handles the incoming activity (in the form of a TurnContext) and passes it through the dialog system. - * If no dialog is active, it will start the default dialog. - * @param {import('botbuilder').TurnContext} turnContext - * @param {*} accessor - */ - async run (turnContext, accessor) { - const dialogSet = new DialogSet(accessor); - dialogSet.add(this); - - const dialogContext = await dialogSet.createContext(turnContext); - const results = await dialogContext.continueDialog(); - if (results.status === DialogTurnStatus.empty) { - await dialogContext.beginDialog(this.id); - } - } - - /** - * @param {import('botbuilder-dialogs').DialogContext} innerDc - */ - async onContinueDialog (innerDc) { - const activeSkill = await this.activeSkillProperty.get(innerDc.context, () => null); - const activity = innerDc.context.activity; - if (activeSkill != null && activity.type === ActivityTypes.Message && activity.text != null && activity.text.toLowerCase() === 'abort') { - // Cancel all dialogs when the user says abort. - // The SkillDialog automatically sends an EndOfConversation message to the skill to let the - // skill know that it needs to end its current dialogs, too. - await innerDc.cancelAllDialogs(); - return innerDc.replaceDialog(this.initialDialogId, { text: 'Canceled! \n\n What delivery mode would you like to use?' }); - } - // Sample to test a tangent when in the middle of a skill conversation. - if (activeSkill != null && activity.type === ActivityTypes.Message && activity.text != null && activity.text.toLowerCase() === 'tangent') { - // Start tangent. - return innerDc.beginDialog(TANGENT_DIALOG); - } - - return super.onContinueDialog(innerDc); - } - - /** - * Render a prompt to select the delivery mode to use. - * @param {import('botbuilder-dialogs').WaterfallStepContext} stepContext - */ - async selectDeliveryModeStep (stepContext) { - // Create the PromptOptions with the delivery modes supported. - const messageText = stepContext.options && stepContext.options.text ? stepContext.options.text : 'What delivery mode would you like to use?'; - const retryMessageText = 'That was not a valid choice, please select a valid delivery mode.'; - - return stepContext.prompt(DELIVERY_PROMPT, { - prompt: MessageFactory.text(messageText, messageText, InputHints.ExpectingInput), - retryPrompt: MessageFactory.text(retryMessageText, retryMessageText, InputHints.ExpectingInput), - choices: ChoiceFactory.toChoices([DeliveryModes.Normal, DeliveryModes.ExpectReplies]) - }); - } - - /** - * Render a prompt to select the group of skills to use. - * @param {import('botbuilder-dialogs').WaterfallStepContext} stepContext - */ - async selectSkillGroupStep (stepContext) { - // Set delivery mode. - this.deliveryMode = stepContext.result.value; - await this.deliveryModeProperty.set(stepContext.context, stepContext.result.value); - - const messageText = 'What group of skills would you like to use?'; - const retryMessageText = 'That was not a valid choice, please select a valid skill group.'; - - // Get a list of the groups for the skills in skillsConfig. - const groups = Object.values(this.skillsConfig.skills) - .map(skill => skill.group); - // Remove duplicates - const choices = [...new Set(groups)]; - - // Create the PromptOptions from the skill configuration which contains the list of configured skills. - return stepContext.prompt(SKILL_GROUP_PROMPT, { - prompt: MessageFactory.text(messageText, messageText, InputHints.ExpectingInput), - retryPrompt: MessageFactory.text(retryMessageText, retryMessageText, InputHints.ExpectingInput), - choices: ChoiceFactory.toChoices(choices) - }); - } - - /** - * Render a prompt to select the skill to call. - * @param {import('botbuilder-dialogs').WaterfallStepContext} stepContext - */ - async selectSkillStep (stepContext) { - const skillGroup = stepContext.result.value; - - // Create the PromptOptions from the skill configuration which contains the list of configured skills. - const messageText = 'What skill would you like to call?'; - const retryMessageText = 'That was not a valid choice, please select a valid skill.'; - - // Get skills for the selected group. - const choices = Object.entries(this.skillsConfig.skills) - .filter(([, skill]) => skill.group === skillGroup) - .map(([id]) => id); - - return stepContext.prompt(SKILL_PROMPT, { - prompt: MessageFactory.text(messageText, messageText, InputHints.ExpectingInput), - retryPrompt: MessageFactory.text(retryMessageText, retryMessageText, InputHints.ExpectingInput), - choices: ChoiceFactory.toChoices(choices), - style: ListStyle.list - }); - } - - /** - * Render a prompt to select the begin action for the skill. - * @param {import('botbuilder-dialogs').WaterfallStepContext} stepContext - */ - async selectSkillActionStep (stepContext) { - const selectedSkill = this.skillsConfig.skills[stepContext.result.value]; - const v3Bots = ['EchoSkillBotDotNetV3', 'EchoSkillBotJSV3']; - - // Set active skill. - await this.activeSkillProperty.set(stepContext.context, selectedSkill); - - // Exclude v3 bots from ExpectReplies. - if (this.deliveryMode === DeliveryModes.ExpectReplies && v3Bots.includes(selectedSkill.id)) { - await stepContext.context.SendActivityAsync(MessageFactory.text("V3 Bots do not support 'expectReplies' delivery mode.")); - - // Forget delivery mode and skill invocation. - await this.deliveryModeProperty.delete(stepContext.context); - await this.activeSkillProperty.delete(stepContext.context); - - // Restart setup dialog. - return stepContext.replaceDialog(this.initialDialogId); - } - - const skillActionChoices = selectedSkill.getActions(); - - if (skillActionChoices && skillActionChoices.length === 1) { - // The skill only supports one action (e.g. Echo), skip the prompt. - return stepContext.next({ value: skillActionChoices[0] }); - } - - // Create the PromptOptions with the actions supported by the selected skill. - const messageText = `Select an action to send to **${selectedSkill.id}**.`; - - return stepContext.prompt(SKILL_ACTION_PROMPT, { - prompt: MessageFactory.text(messageText, messageText, InputHints.ExpectingInput), - choices: ChoiceFactory.toChoices(skillActionChoices) - }); - } - - /** - * @param {import('botbuilder-dialogs').WaterfallStepContext} stepContext - */ - async callSkillActionStep (stepContext) { - const selectedSkill = await this.activeSkillProperty.get(stepContext.context); - - // Save active skill in state. - await this.activeSkillProperty.set(stepContext.context, selectedSkill); - - // Create the initial activity to call the skill. - const skillActivity = this.skillsConfig.skills[selectedSkill.id].createBeginActivity(stepContext.result.value); - - if (skillActivity.name === 'Sso') { - // Special case, we start the SSO dialog to prepare the host to call the skill. - return stepContext.beginDialog(`${SSO_DIALOG_PREFIX}${selectedSkill.id}`); - } - - // We are manually creating the activity to send to the skill; ensure we add the ChannelData and Properties - // from the original activity so the skill gets them. - // Note: this is not necessary if we are just forwarding the current activity from context. - skillActivity.channelData = stepContext.context.activity.channelData; - skillActivity.properties = stepContext.context.activity.properties; - - // Create the BeginSkillDialogOptions and assign the activity to send. - const skillDialogArgs = { activity: skillActivity }; - - if (this.deliveryMode === DeliveryModes.ExpectReplies) { - skillDialogArgs.activity.deliveryMode = DeliveryModes.ExpectReplies; - } - - // Start the skillDialog instance with the arguments. - return stepContext.beginDialog(selectedSkill.id, skillDialogArgs); - } - - /** - * The SkillDialog has ended, render the results (if any) and restart MainDialog. - * @param {import('botbuilder-dialogs').WaterfallStepContext} stepContext - */ - async finalStep (stepContext) { - const activeSkill = await this.activeSkillProperty.get(stepContext.context); - - if (stepContext.result) { - let message = `Skill "${activeSkill.id}" invocation complete.`; - message += ` Result: ${JSON.SerializeObject(stepContext.result)}`; - await stepContext.context.sendActivity(message); - } - - // Forget delivery mode and skill invocation. - await this.deliveryModeProperty.delete(stepContext.context); - await this.activeSkillProperty.delete(stepContext.context); - - // Restart setup dialog - return stepContext.replaceDialog(this.initialDialogId, { text: `Done with "${activeSkill.id}". \n\n What delivery mode would you like to use?` }); - } - - /** - * Helper method that creates and adds SkillDialog instances for the configured skills. - * @param {import('botbuilder').ConversationState} conversationState - * @param {import('../skillConversationIdFactory').SkillConversationIdFactory} conversationIdFactory - * @param {import('botbuilder').SkillHttpClient} skillClient - * @param {import('../skillsConfiguration').SkillsConfiguration} skillsConfig - * @param {string} botId - */ - addSkillDialogs (conversationState, conversationIdFactory, skillClient, skillsConfig, botId) { - Object.keys(skillsConfig.skills).forEach((skillId) => { - const skillInfo = skillsConfig.skills[skillId]; - - const skillDialogOptions = { - botId: botId, - conversationIdFactory, - conversationState, - skill: skillInfo, - skillHostEndpoint: process.env.SkillHostEndpoint, - skillClient - }; - - // Add a SkillDialog for the selected skill. - this.addDialog(new SkillDialog(skillDialogOptions, skillInfo.id)); - }); - } - - /** - * Special case. - * SSO needs a dialog in the host to allow the user to sign in. - * We create and several SsoDialog instances for each skill that supports SSO. - */ - addSsoDialogs () { - const addDialogs = (name, connectionName) => Object.values(this.dialogs.dialogs) - .filter(({ id }) => id.startsWith(name)) - .forEach(skill => this.addDialog(new SsoDialog(`${SSO_DIALOG_PREFIX}${skill.id}`, skill, connectionName))); - - addDialogs('WaterfallSkillBot', process.env.SsoConnectionName); - addDialogs('TeamsSkillBot', process.env.SsoConnectionNameTeams); - } -} - -module.exports.MainDialog = MainDialog; diff --git a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/dialogs/sso/ssoDialog.js b/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/dialogs/sso/ssoDialog.js deleted file mode 100644 index 061d6ea2ad..0000000000 --- a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/dialogs/sso/ssoDialog.js +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const { ActivityTypes, EndOfConversationCodes, InputHints, MessageFactory } = require('botbuilder'); -const { ComponentDialog, ChoicePrompt, ChoiceFactory, DialogTurnStatus, WaterfallDialog } = require('botbuilder-dialogs'); -const { SsoSignInDialog } = require('./ssoSignInDialog'); - -const ACTION_STEP_PROMPT = 'ActionStepPrompt'; -const SSO_SIGNIN_DIALOG = 'SsoSignInDialog'; -const WATERFALL_DIALOG = 'WaterfallDialog'; - -/** - * Helps prepare the host for SSO operations and provides helpers to check the status and invoke the skill. - */ -class SsoDialog extends ComponentDialog { - /** - * @param {string} dialogId - * @param {*} ssoSkillDialog - * @param {string} connectionName - */ - constructor (dialogId, ssoSkillDialog, connectionName) { - super(dialogId); - - this.connectionName = connectionName; - this.skillDialogId = ssoSkillDialog.id; - - this.addDialog(new ChoicePrompt(ACTION_STEP_PROMPT)); - this.addDialog(new SsoSignInDialog(this.connectionName)); - this.addDialog(ssoSkillDialog); - - this.addDialog(new WaterfallDialog(WATERFALL_DIALOG, [ - this.promptActionStep.bind(this), - this.handleActionStep.bind(this), - this.promptFinalStep.bind(this) - ])); - - this.initialDialogId = WATERFALL_DIALOG; - } - - /** - * @param {import('botbuilder-dialogs').WaterfallStepContext} stepContext - */ - async promptActionStep (stepContext) { - const messageText = 'What SSO action do you want to perform?'; - const repromptMessageText = 'That was not a valid choice, please select a valid choice.'; - - return stepContext.prompt(ACTION_STEP_PROMPT, { - prompt: MessageFactory.text(messageText, messageText, InputHints.ExpectingInput), - retryPrompt: MessageFactory.text(repromptMessageText, repromptMessageText, InputHints.ExpectingInput), - choices: await this.getPromptChoices(stepContext) - }); - } - - /** - * @param {import('botbuilder-dialogs').WaterfallStepContext} stepContext - */ - async getPromptChoices (stepContext) { - const promptChoices = []; - const adapter = stepContext.context.adapter; - const token = await adapter.getUserToken(stepContext.context, this.connectionName); - - if (!token) { - promptChoices.push('Login'); - // Token exchange will fail when the host is not logged on and the skill should - // show a regular OAuthPrompt. - promptChoices.push('Call Skill (without SSO)'); - } else { - promptChoices.push('Logout'); - promptChoices.push('Show token'); - promptChoices.push('Call Skill (with SSO)'); - } - - promptChoices.push('Back'); - - return ChoiceFactory.toChoices(promptChoices); - } - - /** - * @param {import('botbuilder-dialogs').WaterfallStepContext} stepContext - */ - async handleActionStep (stepContext) { - const action = stepContext.result.value; - - switch (action.toLowerCase()) { - case 'login': - return stepContext.beginDialog(SSO_SIGNIN_DIALOG); - - case 'logout': { - const adapter = stepContext.context.adapter; - await adapter.signOutUser(stepContext.context, this.connectionName); - await stepContext.context.sendActivity('You have been signed out.'); - return stepContext.next(); - } - - case 'show token': { - const tokenProvider = stepContext.context.adapter; - const token = await tokenProvider.getUserToken(stepContext.context, this.connectionName); - if (!token) { - await stepContext.context.sendActivity('User has no cached token.'); - } else { - await stepContext.context.sendActivity(`Here is your current SSO token: ${token.token}`); - } - return stepContext.next(); - } - - case 'call skill (with sso)': - case 'call skill (without sso)': { - const beginSkillActivity = { - type: ActivityTypes.Event, - name: 'Sso' - }; - return stepContext.beginDialog(this.skillDialogId, { activity: beginSkillActivity }); - } - - case 'back': - await stepContext.context.sendActivity({ - type: ActivityTypes.EndOfConversation, - code: EndOfConversationCodes.CompletedSuccessfully - }); - return { status: DialogTurnStatus.complete }; - - default: - // This should never be hit since the previous prompt validates the choice. - throw new Error(`[SsoDialog]: Unrecognized action: ${action}`); - } - } - - /** - * @param {import('botbuilder-dialogs').WaterfallStepContext} stepContext - */ - async promptFinalStep (stepContext) { - // Restart the dialog (we will exit when the user says end). - return stepContext.replaceDialog(this.initialDialogId); - } -} - -module.exports.SsoDialog = SsoDialog; diff --git a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/dialogs/sso/ssoSignInDialog.js b/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/dialogs/sso/ssoSignInDialog.js deleted file mode 100644 index 205c0db38a..0000000000 --- a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/dialogs/sso/ssoSignInDialog.js +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const { ComponentDialog, WaterfallDialog, OAuthPrompt } = require('botbuilder-dialogs'); - -const SSO_SIGNIN_DIALOG = 'SsoSignInDialog'; -const OAUTH_PROMPT = 'OAuthPrompt'; -const WATERFALL_DIALOG = 'WaterfallDialog'; - -/** - * Helps prepare the host for SSO operations and provides helpers to check the status and invoke the skill. - */ -class SsoSignInDialog extends ComponentDialog { - /** - * @param {string} connectionName - */ - constructor (connectionName) { - super(SSO_SIGNIN_DIALOG); - - this.addDialog(new OAuthPrompt(OAUTH_PROMPT, { - connectionName: connectionName, - text: `Sign in to the host bot using AAD for SSO and connection ${connectionName}`, - title: 'Sign In', - timeout: 60000 - })); - - this.addDialog(new WaterfallDialog(WATERFALL_DIALOG, [ - this.signInStep.bind(this), - this.displayTokenStep.bind(this) - ])); - - this.initialDialogId = WATERFALL_DIALOG; - } - - /** - * @param {import('botbuilder-dialogs').WaterfallStepContext} stepContext - */ - async signInStep (stepContext) { - return stepContext.beginDialog(OAUTH_PROMPT); - } - - /** - * @param {import('botbuilder-dialogs').WaterfallStepContext} stepContext - */ - async displayTokenStep (stepContext) { - if (!stepContext.result.token) { - await stepContext.context.sendActivity('No token was provided.'); - } else { - await stepContext.context.sendActivity(`Here is your token: ${stepContext.result.token}`); - } - return stepContext.endDialog(); - } -} - -module.exports.SsoSignInDialog = SsoSignInDialog; diff --git a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/dialogs/tangentDialog.js b/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/dialogs/tangentDialog.js deleted file mode 100644 index 42dc86b6cc..0000000000 --- a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/dialogs/tangentDialog.js +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const { InputHints, MessageFactory } = require('botbuilder'); -const { ComponentDialog, DialogSet, TextPrompt, WaterfallDialog, DialogTurnStatus } = require('botbuilder-dialogs'); - -const TANGENT_DIALOG = 'TangentDialog'; -const WATERFALL_DIALOG = 'WaterfallDialog'; -const TEXT_PROMPT = 'TextPrompt'; - -class TangentDialog extends ComponentDialog { - /** - * @param {string} dialogId - */ - constructor (dialogId = TANGENT_DIALOG) { - super(dialogId); - - this.addDialog(new TextPrompt(TEXT_PROMPT)); - this.addDialog(new WaterfallDialog(WATERFALL_DIALOG, [ - this.step1.bind(this), - this.step2.bind(this), - this.endStep.bind(this) - ])); - - this.initialDialogId = WATERFALL_DIALOG; - } - - /** - * The run method handles the incoming activity (in the form of a TurnContext) and passes it through the dialog system. - * If no dialog is active, it will start the default dialog. - * @param {import('botbuilder').TurnContext} turnContext - * @param {*} accessor - */ - async run (turnContext, accessor) { - const dialogSet = new DialogSet(accessor); - dialogSet.add(this); - - const dialogContext = await dialogSet.createContext(turnContext); - const results = await dialogContext.continueDialog(); - if (results.status === DialogTurnStatus.empty) { - await dialogContext.beginDialog(this.id); - } - } - - /** - * @param {import('botbuilder-dialogs').WaterfallStepContext} stepContext - */ - async step1 (stepContext) { - const messageText = 'Tangent step 1 of 2, say something.'; - const promptMessage = MessageFactory.text(messageText, messageText, InputHints.ExpectingInput); - - return stepContext.prompt(TEXT_PROMPT, promptMessage); - } - - /** - * @param {import('botbuilder-dialogs').WaterfallStepContext} stepContext - */ - async step2 (stepContext) { - const messageText = 'Tangent step 2 of 2, say something.'; - const promptMessage = MessageFactory.text(messageText, messageText, InputHints.ExpectingInput); - - return stepContext.prompt(TEXT_PROMPT, promptMessage); - } - - /** - * @param {import('botbuilder-dialogs').WaterfallStepContext} stepContext - */ - async endStep (stepContext) { - return stepContext.endDialog(); - } -} - -module.exports.TangentDialog = TangentDialog; diff --git a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/index.js b/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/index.js deleted file mode 100644 index 0082806e4b..0000000000 --- a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/index.js +++ /dev/null @@ -1,204 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -// index.js is used to setup and configure your bot - -// Import required packages -const http = require('http'); -const https = require('https'); -const path = require('path'); -const restify = require('restify'); - -// Import required bot services. -// See https://aka.ms/bot-services to learn more about the different parts of a bot. -const { BotFrameworkAdapter, TurnContext, ActivityTypes, ChannelServiceRoutes, ConversationState, InputHints, MemoryStorage, SkillHttpClient, MessageFactory, SkillConversationIdFactory } = require('botbuilder'); -const { AuthenticationConfiguration, SimpleCredentialProvider } = require('botframework-connector'); - -// Import required bot configuration. -const ENV_FILE = path.join(__dirname, '.env'); -require('dotenv').config({ path: ENV_FILE }); - -// This bot's main dialog. -const { RootBot } = require('./bots/rootBot'); -const { SkillsConfiguration } = require('./skillsConfiguration'); -const { allowedSkillsClaimsValidator } = require('./authentication/allowedSkillsClaimsValidator'); -const { MainDialog } = require('./dialogs/mainDialog'); -const { LoggerMiddleware } = require('./middleware/loggerMiddleware'); -const { TokenExchangeSkillHandler } = require('./TokenExchangeSkillHandler'); - -const maxTotalSockets = (preallocatedSnatPorts, procCount = 1, weight = 0.5, overcommit = 1.1) => - Math.min( - Math.floor((preallocatedSnatPorts / procCount) * weight * overcommit), - preallocatedSnatPorts - ); - -// Create adapter. -// See https://aka.ms/about-bot-adapter to learn more about adapters. -const adapter = new BotFrameworkAdapter({ - appId: process.env.MicrosoftAppId, - appPassword: process.env.MicrosoftAppPassword, - authConfig: new AuthenticationConfiguration([], allowedSkillsClaimsValidator), - clientOptions: { - agentSettings: { - http: new http.Agent({ - keepAlive: true, - maxTotalSockets: maxTotalSockets(1024, 4, 0.3) - }), - https: new https.Agent({ - keepAlive: true, - maxTotalSockets: maxTotalSockets(1024, 4, 0.7) - }) - } - } -}); - -// Use the logger middleware to log messages. The default logger argument for LoggerMiddleware is Node's console.log(). -adapter.use(new LoggerMiddleware()); - -// Catch-all for errors. -const onTurnErrorHandler = async (context, error) => { - // This check writes out errors to the console log, instead of to app insights. - // NOTE: In production environment, you should consider logging this to Azure - // application insights. See https://aka.ms/bottelemetry for telemetry - // configuration instructions. - console.error(`\n [onTurnError] unhandled error: ${error}`); - - await sendErrorMessage(context, error); - await endSkillConversation(context); - await clearConversationState(context); -}; - -async function sendErrorMessage (context, error) { - try { - const { message, stack } = error; - - // Send a message to the user. - let errorMessageText = 'The bot encountered an error or bug.'; - let errorMessage = MessageFactory.text(errorMessageText, errorMessageText, InputHints.IgnoringInput); - errorMessage.value = { message, stack }; - await context.sendActivity(errorMessage); - - await context.sendActivity(`Exception: ${message}`); - await context.sendActivity(stack); - - errorMessageText = 'To continue to run this bot, please fix the bot source code.'; - errorMessage = MessageFactory.text(errorMessageText, errorMessageText, InputHints.ExpectingInput); - await context.sendActivity(errorMessage); - - // Send a trace activity, which will be displayed in Bot Framework Emulator. - await context.sendTraceActivity( - 'OnTurnError Trace', - `${error}`, - 'https://www.botframework.com/schemas/error', - 'TurnError' - ); - } catch (err) { - console.error(`\n [onTurnError] Exception caught in sendErrorMessage: ${err}`); - } -} - -async function endSkillConversation (context) { - try { - // Inform the active skill that the conversation is ended so that it has - // a chance to clean up. - // Note: ActiveSkillPropertyName is set by the RooBot while messages are being - // forwarded to a Skill. - const activeSkill = await conversationState.createProperty(RootBot.ActiveSkillPropertyName).get(context); - if (activeSkill) { - const botId = process.env.MicrosoftAppId; - - let endOfConversation = { - type: ActivityTypes.EndOfConversation, - code: 'RootSkillError' - }; - endOfConversation = TurnContext.applyConversationReference( - endOfConversation, TurnContext.getConversationReference(context.activity), true); - - await conversationState.saveChanges(context, true); - await skillClient.postToSkill(botId, activeSkill, skillsConfig.skillHostEndpoint, endOfConversation); - } - } catch (err) { - console.error(`\n [onTurnError] Exception caught on attempting to send EndOfConversation : ${err}`); - } -} - -async function clearConversationState (context) { - try { - // Delete the conversationState for the current conversation to prevent the - // bot from getting stuck in a error-loop caused by being in a bad state. - // ConversationState should be thought of as similar to "cookie-state" in a Web page. - await conversationState.delete(context); - } catch (err) { - console.error(`\n [onTurnError] Exception caught on attempting to Delete ConversationState : ${err}`); - } -} - -// Set the onTurnError for the singleton BotFrameworkAdapter. -adapter.onTurnError = onTurnErrorHandler; - -// Define a state store for your bot. See https://aka.ms/about-bot-state to learn more about using MemoryStorage. -// A bot requires a state store to persist the dialog and user state between messages. - -// For local development, in-memory storage is used. -// CAUTION: The Memory Storage used here is for local bot debugging only. When the bot -// is restarted, anything stored in memory will be gone. -const memoryStorage = new MemoryStorage(); -const conversationState = new ConversationState(memoryStorage); - -// Create the conversationIdFactory -const conversationIdFactory = new SkillConversationIdFactory(memoryStorage); - -// Load skills configuration -const skillsConfig = new SkillsConfiguration(); - -// Create the credential provider; -const credentialProvider = new SimpleCredentialProvider(process.env.MicrosoftAppId, process.env.MicrosoftAppPassword); - -// Create the skill client -const skillClient = new SkillHttpClient(credentialProvider, conversationIdFactory); - -// Create the main dialog. -const mainDialog = new MainDialog(conversationState, skillsConfig, skillClient, conversationIdFactory); -const bot = new RootBot(conversationState, skillClient, mainDialog); - -// Create HTTP server. -// maxParamLength defaults to 100, which is too short for the conversationId created in skillConversationIdFactory. -// See: https://github.com/microsoft/BotBuilder-Samples/issues/2194. -const server = restify.createServer({ maxParamLength: 1000 }); -server.listen(process.env.port || process.env.PORT || 36020, function () { - console.log(`\n${server.name} listening to ${server.url}`); - console.log('\nGet Bot Framework Emulator: https://aka.ms/botframework-emulator'); - console.log('\nTo talk to your bot, open the emulator select "Open Bot"'); -}); - -// Listen for incoming activities and route them to your bot main dialog. -server.post('/api/messages', (req, res) => { - // Route received a request to adapter for processing - adapter.processActivity(req, res, async (turnContext) => { - // route to bot activity handler. - await bot.run(turnContext); - }); -}); - -// Create and initialize the skill classes -const authConfig = new AuthenticationConfiguration([], allowedSkillsClaimsValidator); -const handler = new TokenExchangeSkillHandler(adapter, bot, conversationIdFactory, skillsConfig, skillClient, credentialProvider, authConfig); -const skillEndpoint = new ChannelServiceRoutes(handler); -skillEndpoint.register(server, '/api/skills'); - -// Listen for Upgrade requests for Streaming. -server.on('upgrade', (req, socket, head) => { - // Create an adapter scoped to this WebSocket connection to allow storing session data. - const streamingAdapter = new BotFrameworkAdapter({ - appId: process.env.MicrosoftAppId, - appPassword: process.env.MicrosoftAppPassword - }); - // Set onTurnError for the BotFrameworkAdapter created for each connection. - streamingAdapter.onTurnError = onTurnErrorHandler; - - streamingAdapter.useWebSocket(req, socket, head, async (context) => { - // After connecting via WebSocket, run this logic for every request sent over - // the WebSocket connection. - await bot.run(context); - }); -}); diff --git a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/middleware/loggerMiddleware.js b/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/middleware/loggerMiddleware.js deleted file mode 100644 index 325e331c0d..0000000000 --- a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/middleware/loggerMiddleware.js +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const { ActivityTypes } = require('botbuilder'); - -/** - * Logs user and bot messages. It filters out ContinueConversation events coming from skill responses. - */ -class LoggerMiddleware { - // This defaults to using Node's console.log() method if a logger isn't passed in. - constructor (logger = console) { - this.logger = logger; - } - - /** - * @param {import('botbuilder').TurnContext} turnContext - * @param {Function} next - */ - async onTurn (turnContext, next) { - // Note: Skill responses will show as ContinueConversation events; we don't log those. - // We only log incoming messages from users. - if (turnContext.activity.type === ActivityTypes.Event && turnContext.activity.name !== 'continueConversation') { - const message = `User said: "${turnContext.activity.text}" Type: "${turnContext.activity.type}" Name: "${turnContext.activity.name}"`; - this.logger.log(message); - } - - // Register outgoing handler. - turnContext.onSendActivities(this.outgoingHandler.bind(this)); - - // Continue processing messages. - await next(); - } - - /** - * @param {import('botbuilder').TurnContext} turnContext - * @param {Partial[]} activities - * @param {Function} next - */ - async outgoingHandler (turnContext, activities, next) { - activities.forEach((activity) => { - const message = `Bot said: "${activity.text}" Type: "${activity.type}" Name: "${activity.name}"`; - this.logger.log(message); - }); - - await next(); - } -} - -module.exports.LoggerMiddleware = LoggerMiddleware; diff --git a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/package.json b/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/package.json deleted file mode 100644 index 333bab7282..0000000000 --- a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/package.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "waterfall-host-bot", - "version": "1.0.0", - "description": "A simple waterfall root bot for testing skills", - "author": "Microsoft", - "license": "MIT", - "main": "index.js", - "scripts": { - "start": "node ./index.js", - "watch": "nodemon ./index.js", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "repository": { - "type": "git", - "url": "https://github.com" - }, - "dependencies": { - "botbuilder": "~4.14.0", - "botbuilder-dialogs": "~4.14.0", - "dotenv": "~8.2.0", - "restify": "~8.5.1" - }, - "devDependencies": { - "nodemon": "~2.0.4" - } -} diff --git a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/skills/echoSkill.js b/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/skills/echoSkill.js deleted file mode 100644 index 7c9165a319..0000000000 --- a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/skills/echoSkill.js +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const { ActivityEx } = require('botbuilder-core'); -const { SkillDefinition } = require('./skillDefinition'); - -const SkillAction = { - Message: 'Message' -}; - -class EchoSkill extends SkillDefinition { - getActions () { - return Object.values(SkillAction); - } - - /** - * @param {string} actionId - */ - createBeginActivity (actionId) { - if (!this.getActions().includes(actionId)) { - throw new Error(`[EchoSkill]: Unable to create begin activity for "${actionId}".`); - } - - // We only support one activity for Echo so no further checks are needed - const activity = ActivityEx.createMessageActivity(); - activity.name = actionId; - activity.text = 'Begin the Echo Skill.'; - - return activity; - } -} - -module.exports.EchoSkill = EchoSkill; diff --git a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/skills/skillDefinition.js b/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/skills/skillDefinition.js deleted file mode 100644 index 73f731cbb4..0000000000 --- a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/skills/skillDefinition.js +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -class SkillDefinition { - getActions () { - throw new Error('[SkillDefinition]: Method not implemented'); - } - - createBeginActivity () { - throw new Error('[SkillDefinition]: Method not implemented'); - } -} - -module.exports.SkillDefinition = SkillDefinition; diff --git a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/skills/teamsSkill.js b/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/skills/teamsSkill.js deleted file mode 100644 index 74c4d4d73d..0000000000 --- a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/skills/teamsSkill.js +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const { ActivityEx } = require('botbuilder-core'); -const { SkillDefinition } = require('./skillDefinition'); - -const SkillAction = { - TeamsTaskModule: 'TeamsTaskModule', - TeamsCardAction: 'TeamsCardAction', - TeamsConversation: 'TeamsConversation', - Cards: 'Cards', - Proactive: 'Proactive', - Attachment: 'Attachment', - Auth: 'Auth', - Sso: 'Sso', - Echo: 'Echo', - FileUpload: 'FileUpload', - Delete: 'Delete', - Update: 'Update' -}; - -class TeamsSkill extends SkillDefinition { - getActions () { - return Object.values(SkillAction); - } - - /** - * @param {string} actionId - */ - createBeginActivity (actionId) { - if (!this.getActions().includes(actionId)) { - throw new Error(`[TeamsSkill]: Unable to create begin activity for "${actionId}".`); - } - - // We don't support special parameters in these skills so a generic event with the right name - // will do in this case. - const activity = ActivityEx.createEventActivity(); - activity.name = actionId; - - return activity; - } -} - -module.exports.TeamsSkill = TeamsSkill; diff --git a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/skills/waterfallSkill.js b/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/skills/waterfallSkill.js deleted file mode 100644 index 04e14a21d6..0000000000 --- a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/skills/waterfallSkill.js +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const { ActivityEx } = require('botbuilder-core'); -const { SkillDefinition } = require('./skillDefinition'); - -const SkillAction = { - Cards: 'Cards', - Proactive: 'Proactive', - Auth: 'Auth', - MessageWithAttachment: 'MessageWithAttachment', - Sso: 'Sso', - FileUpload: 'FileUpload', - Echo: 'Echo', - Delete: 'Delete', - Update: 'Update' -}; - -class WaterfallSkill extends SkillDefinition { - getActions () { - return Object.values(SkillAction); - } - - /** - * @param {string} actionId - */ - createBeginActivity (actionId) { - if (!this.getActions().includes(actionId)) { - throw new Error(`[WaterfallSkill]: Unable to create begin activity for "${actionId}".`); - } - - // We don't support special parameters in these skills so a generic event with the right name - // will do in this case. - const activity = ActivityEx.createEventActivity(); - activity.name = actionId; - - return activity; - } -} - -module.exports.WaterfallSkill = WaterfallSkill; diff --git a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/skillsConfiguration.js b/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/skillsConfiguration.js deleted file mode 100644 index e5f0f27644..0000000000 --- a/tests/functional/Bots/JavaScript/Consumers/CodeFirst/WaterfallHostBot/skillsConfiguration.js +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const { EchoSkill } = require('./skills/echoSkill'); -const { WaterfallSkill } = require('./skills/waterfallSkill'); -const { TeamsSkill } = require('./skills/teamsSkill'); - -/** - * A helper class that loads Skills information from configuration. - */ -class SkillsConfiguration { - constructor () { - this.skillsData = Object.entries(process.env) - .filter(([key]) => key.startsWith('skill_')) - .reduce((acc, [key, value]) => { - const [, id, attr] = key.split('_'); - acc[id] = acc[id] || {}; - const propName = { appid: 'appId', endpoint: 'skillEndpoint', group: 'group' }[attr.toLowerCase()]; - if (!propName) { throw new Error(`[SkillsConfiguration]: Invalid environment variable declaration ${key}`); } - acc[id][propName] = value; - if (propName === 'group') { - acc[id] = this.createSkillDefinition({ id, ...acc[id] }); - } - return acc; - }, {}); - - this.skillHostEndpointValue = process.env.SkillHostEndpoint; - if (!this.skillHostEndpointValue) { - throw new Error('[SkillsConfiguration]: Missing configuration parameter. SkillHostEndpoint is required'); - } - } - - get skills () { - return this.skillsData; - } - - get skillHostEndpoint () { - return this.skillHostEndpointValue; - } - - createSkillDefinition (skill) { - // Note: we hard code this for now, we should dynamically create instances based on the manifests. - switch (skill.group) { - case 'Echo': - return Object.assign(new EchoSkill(), skill); - - case 'Waterfall': - return Object.assign(new WaterfallSkill(), skill); - - case 'Teams': - return Object.assign(new TeamsSkill(), skill); - - default: - throw new Error(`[SkillsConfiguration]: Unable to find definition class for ${skill.id}`); - } - } -} - -module.exports.SkillsConfiguration = SkillsConfiguration; diff --git a/tests/functional/Bots/JavaScript/Skills/CodeFirst/EchoSkillBot-v3/.env b/tests/functional/Bots/JavaScript/Skills/CodeFirst/EchoSkillBot-v3/.env deleted file mode 100644 index 6a8719bd3a..0000000000 --- a/tests/functional/Bots/JavaScript/Skills/CodeFirst/EchoSkillBot-v3/.env +++ /dev/null @@ -1,3 +0,0 @@ -MicrosoftAppId= -MicrosoftAppPassword= -AllowedCallers=* \ No newline at end of file diff --git a/tests/functional/Bots/JavaScript/Skills/CodeFirst/EchoSkillBot-v3/index.js b/tests/functional/Bots/JavaScript/Skills/CodeFirst/EchoSkillBot-v3/index.js deleted file mode 100644 index 4a3d731311..0000000000 --- a/tests/functional/Bots/JavaScript/Skills/CodeFirst/EchoSkillBot-v3/index.js +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const restify = require('restify'); -const builder = require('botbuilder'); -require('dotenv').config(); - -// Setup Restify Server -const server = restify.createServer(); -server.listen(process.env.port || process.env.PORT || 36407, function () { - console.log('%s listening to %s', server.name, server.url); -}); - -// Expose the manifest -server.get('/manifests/*', restify.plugins.serveStatic({ directory: './manifests', appendRequestPath: false })); - -// Bot Storage: Here we register the state storage for your bot. -// Default store: volatile in-memory store - Only for prototyping! -// We provide adapters for Azure Table, CosmosDb, SQL Azure, or you can implement your own! -// For samples and documentation, see: https://github.com/Microsoft/BotBuilder-Azure -const inMemoryStorage = new builder.MemoryBotStorage(); - -// Create chat connector for communicating with the Bot Framework Service -const connector = new builder.ChatConnector({ - appId: process.env.MicrosoftAppId, - appPassword: process.env.MicrosoftAppPassword, - enableSkills: true, - allowedCallers: [process.env.allowedCallers] -}); - -// Listen for messages from users -server.post('/api/messages', connector.listen()); - -// Create your bot with a function to receive messages from the user -new builder.UniversalBot(connector, function (session) { - session.on('error', function (error) { - const { message, stack } = error; - - // Send a message to the user. - let errorMessageText = 'The skill encountered an error or bug.'; - let activity = new builder.Message() - .text(`${errorMessageText}\r\n${message}\r\n${stack}`) - .speak(errorMessageText) - .inputHint(builder.InputHint.ignoringInput) - .value({ message, stack }); - session.send(activity); - - errorMessageText = 'To continue to run this bot, please fix the bot source code.'; - activity = new builder.Message() - .text(errorMessageText) - .speak(errorMessageText) - .inputHint(builder.InputHint.expectingInput); - session.send(activity); - - activity = new builder.Message() - .code('SkillError') - .text(message); - session.endConversation(activity); - }); - - switch (session.message.text.toLowerCase()) { - case 'end': - case 'stop': - session.say('Ending conversation from the skill...', { - inputHint: builder.InputHint.acceptingInput - }); - session.endConversation(); - break; - default: - session.say('Echo: ' + session.message.text, { - inputHint: builder.InputHint.acceptingInput - }); - session.say('Say "end" or "stop" and I\'ll end the conversation and back to the parent.'); - } -}).set('storage', inMemoryStorage); // Register in memory storage diff --git a/tests/functional/Bots/JavaScript/Skills/CodeFirst/EchoSkillBot-v3/manifests/echoskillbotv3-manifest-1.0.json b/tests/functional/Bots/JavaScript/Skills/CodeFirst/EchoSkillBot-v3/manifests/echoskillbotv3-manifest-1.0.json deleted file mode 100644 index f99c248eb8..0000000000 --- a/tests/functional/Bots/JavaScript/Skills/CodeFirst/EchoSkillBot-v3/manifests/echoskillbotv3-manifest-1.0.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "$schema": "https://schemas.botframework.com/schemas/skills/v2.1/skill-manifest.json", - "$id": "EchoSkillBotJSV3", - "name": "EchoSkillBotJSV3", - "version": "1.0", - "description": "This is a skill for echoing what the user sent to the bot (implemented using JS and BF v3).", - "publisherName": "Microsoft", - "privacyUrl": "https://microsoft.com/privacy", - "copyright": "Copyright (c) Microsoft Corporation. All rights reserved.", - "license": "https://github.com/microsoft/BotFramework-FunctionalTests/blob/main/LICENSE", - "tags": [ - "echo" - ], - "endpoints": [ - { - "name": "default", - "protocol": "BotFrameworkV3", - "description": "Localhost endpoint for the skill (on port 36407)", - "endpointUrl": "http://localhost:36407/api/messages", - "msAppId": "00000000-0000-0000-0000-000000000000" - } - ] -} diff --git a/tests/functional/Bots/JavaScript/Skills/CodeFirst/EchoSkillBot-v3/package.json b/tests/functional/Bots/JavaScript/Skills/CodeFirst/EchoSkillBot-v3/package.json deleted file mode 100644 index dbc5fe99ae..0000000000 --- a/tests/functional/Bots/JavaScript/Skills/CodeFirst/EchoSkillBot-v3/package.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "name": "v3-skill-bot", - "version": "1.0.0", - "description": "An example of a V3 bot that can be used as a skill", - "author": "Microsoft Corp.", - "license": "MIT", - "scripts": { - "start": "node ./index.js" - }, - "dependencies": { - "async": "^1.5.2", - "base64url": "^3.0.0", - "botbuilder": "^3.30.0", - "busboy": "^0.2.13", - "chrono-node": "^1.2.5", - "dotenv": "^8.2.0", - "jsonwebtoken": "^8.2.2", - "node-uuid": "^1.4.7", - "promise": "^7.1.1", - "request": "^2.78.0", - "restify": "^8.5.1", - "rsa-pem-from-mod-exp": "^0.8.4", - "sprintf-js": "^1.0.3", - "url-join": "^1.1.0" - }, - "devDependencies": { - "mocha": "^2.4.5" - } -} diff --git a/tests/functional/Bots/JavaScript/Skills/CodeFirst/EchoSkillBot/.env b/tests/functional/Bots/JavaScript/Skills/CodeFirst/EchoSkillBot/.env deleted file mode 100644 index b48405cf7f..0000000000 --- a/tests/functional/Bots/JavaScript/Skills/CodeFirst/EchoSkillBot/.env +++ /dev/null @@ -1,3 +0,0 @@ -MicrosoftAppId= -MicrosoftAppPassword= -AllowedCallers= diff --git a/tests/functional/Bots/JavaScript/Skills/CodeFirst/EchoSkillBot/authentication/allowedCallersClaimsValidator.js b/tests/functional/Bots/JavaScript/Skills/CodeFirst/EchoSkillBot/authentication/allowedCallersClaimsValidator.js deleted file mode 100644 index a0146a3aca..0000000000 --- a/tests/functional/Bots/JavaScript/Skills/CodeFirst/EchoSkillBot/authentication/allowedCallersClaimsValidator.js +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const { JwtTokenValidation, SkillValidation } = require('botframework-connector'); - -// Load the AppIds for the configured callers (we will only allow responses from skills we have configured). -// process.env.AllowedCallers is the list of parent bot Ids that are allowed to access the skill -// to add a new parent bot simply go to the .env file and add -// the parent bot's Microsoft AppId to the list under AllowedCallers, e.g.: -// AllowedCallers=195bd793-4319-4a84-a800-386770c058b2,38c74e7a-3d01-4295-8e66-43dd358920f8 -const allowedCallers = process.env.AllowedCallers ? process.env.AllowedCallers.split(',') : undefined; - -/** - * Sample claims validator that loads an allowed list from configuration if present - * and checks that requests are coming from allowed parent bots. - * @param claims An array of Claims decoded from the HTTP request's auth header - */ -const allowedCallersClaimsValidator = async (claims) => { - // If allowedCallers is undefined we allow all calls - if (allowedCallers && SkillValidation.isSkillClaim(claims)) { - // Check that the appId claim in the skill request is in the list of skills configured for this bot. - const appId = JwtTokenValidation.getAppIdFromClaims(claims); - if (!allowedCallers.includes(appId)) { - throw new Error(`Received a request from a bot with an app ID of "${appId}". To enable requests from this caller, add the app ID to your configuration file.`); - } - } -}; - -module.exports.allowedCallersClaimsValidator = allowedCallersClaimsValidator; diff --git a/tests/functional/Bots/JavaScript/Skills/CodeFirst/EchoSkillBot/bot.js b/tests/functional/Bots/JavaScript/Skills/CodeFirst/EchoSkillBot/bot.js deleted file mode 100644 index bf9ce2f986..0000000000 --- a/tests/functional/Bots/JavaScript/Skills/CodeFirst/EchoSkillBot/bot.js +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const { ActivityHandler, ActivityTypes, EndOfConversationCodes } = require('botbuilder'); - -class EchoBot extends ActivityHandler { - constructor () { - super(); - // See https://aka.ms/about-bot-activity-message to learn more about the message and other activity types. - this.onMessage(async (context, next) => { - const activityText = context.activity.text.toLowerCase(); - if (activityText === 'end' || activityText === 'stop') { - await context.sendActivity('Ending conversation from the skill...'); - await context.sendActivity({ - type: ActivityTypes.EndOfConversation, - code: EndOfConversationCodes.CompletedSuccessfully - }); - } else { - await context.sendActivity(`Echo: ${context.activity.text}`); - await context.sendActivity('Say "end" or "stop" and I\'ll end the conversation and back to the parent.'); - } - - // By calling next() you ensure that the next BotHandler is run. - await next(); - }); - - this.onUnrecognizedActivityType(async (context, next) => { - // This will be called if the root bot is ending the conversation. Sending additional messages should be - // avoided as the conversation may have been deleted. - // Perform cleanup of resources if needed. - - // By calling next() you ensure that the next BotHandler is run. - await next(); - }); - } -} - -module.exports.EchoBot = EchoBot; diff --git a/tests/functional/Bots/JavaScript/Skills/CodeFirst/EchoSkillBot/index.js b/tests/functional/Bots/JavaScript/Skills/CodeFirst/EchoSkillBot/index.js deleted file mode 100644 index dbcf46b4f9..0000000000 --- a/tests/functional/Bots/JavaScript/Skills/CodeFirst/EchoSkillBot/index.js +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const dotenv = require('dotenv'); -const http = require('http'); -const https = require('https'); -const path = require('path'); -const restify = require('restify'); - -// Import required bot services. -// See https://aka.ms/bot-services to learn more about the different parts of a bot. -const { ActivityTypes, BotFrameworkAdapter, InputHints, MessageFactory } = require('botbuilder'); -const { AuthenticationConfiguration } = require('botframework-connector'); - -// Import required bot configuration. -const ENV_FILE = path.join(__dirname, '.env'); -dotenv.config({ path: ENV_FILE }); - -// This bot's main dialog. -const { EchoBot } = require('./bot'); -const { allowedCallersClaimsValidator } = require('./authentication/allowedCallersClaimsValidator'); - -// Create HTTP server -const server = restify.createServer(); -server.listen(process.env.port || process.env.PORT || 36400, () => { - console.log(`\n${server.name} listening to ${server.url}`); - console.log('\nGet Bot Framework Emulator: https://aka.ms/botframework-emulator'); - console.log('\nTo talk to your bot, open the emulator select "Open Bot"'); -}); - -// Expose the manifest -server.get('/manifests/*', restify.plugins.serveStatic({ directory: './manifests', appendRequestPath: false })); - -const maxTotalSockets = (preallocatedSnatPorts, procCount = 1, weight = 0.5, overcommit = 1.1) => - Math.min( - Math.floor((preallocatedSnatPorts / procCount) * weight * overcommit), - preallocatedSnatPorts - ); - -// Create adapter. -// See https://aka.ms/about-bot-adapter to learn more about how bots work. -const adapter = new BotFrameworkAdapter({ - appId: process.env.MicrosoftAppId, - appPassword: process.env.MicrosoftAppPassword, - authConfig: new AuthenticationConfiguration([], allowedCallersClaimsValidator), - clientOptions: { - agentSettings: { - http: new http.Agent({ - keepAlive: true, - maxTotalSockets: maxTotalSockets(1024, 4, 0.3) - }), - https: new https.Agent({ - keepAlive: true, - maxTotalSockets: maxTotalSockets(1024, 4, 0.7) - }) - } - } -}); - -// Catch-all for errors. -adapter.onTurnError = async (context, error) => { - // This check writes out errors to console log .vs. app insights. - // NOTE: In production environment, you should consider logging this to Azure - // application insights. - console.error(`\n [onTurnError] unhandled error: ${error}`); - - try { - const { message, stack } = error; - - // Send a message to the user. - let errorMessageText = 'The skill encountered an error or bug.'; - let errorMessage = MessageFactory.text(`${errorMessageText}\r\n${message}\r\n${stack}`, errorMessageText, InputHints.IgnoringInput); - errorMessage.value = { message, stack }; - await context.sendActivity(errorMessage); - - errorMessageText = 'To continue to run this bot, please fix the bot source code.'; - errorMessage = MessageFactory.text(errorMessageText, errorMessageText, InputHints.ExpectingInput); - await context.sendActivity(errorMessage); - - // Send a trace activity, which will be displayed in Bot Framework Emulator - await context.sendTraceActivity( - 'OnTurnError Trace', - `${error}`, - 'https://www.botframework.com/schemas/error', - 'TurnError' - ); - - // Send and EndOfConversation activity to the skill caller with the error to end the conversation - // and let the caller decide what to do. - await context.sendActivity({ - type: ActivityTypes.EndOfConversation, - code: 'SkillError', - text: error - }); - } catch (err) { - console.error(`\n [onTurnError] Exception caught in onTurnError : ${err}`); - } -}; - -// Create the bot that will handle incoming messages. -const myBot = new EchoBot(); - -// Listen for incoming requests. -server.post('/api/messages', (req, res) => { - adapter.processActivity(req, res, async (context) => { - // Route to main dialog. - await myBot.run(context); - }); -}); diff --git a/tests/functional/Bots/JavaScript/Skills/CodeFirst/EchoSkillBot/manifests/echoskillbot-manifest-1.0.json b/tests/functional/Bots/JavaScript/Skills/CodeFirst/EchoSkillBot/manifests/echoskillbot-manifest-1.0.json deleted file mode 100644 index 3ca4cfd1f2..0000000000 --- a/tests/functional/Bots/JavaScript/Skills/CodeFirst/EchoSkillBot/manifests/echoskillbot-manifest-1.0.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "$schema": "https://schemas.botframework.com/schemas/skills/v2.1/skill-manifest.json", - "$id": "EchoSkillBotJS", - "name": "EchoSkillBotJS", - "version": "1.0", - "description": "This is a skill for echoing what the user sent to the bot (implemented using JS).", - "publisherName": "Microsoft", - "privacyUrl": "https://microsoft.com/privacy", - "copyright": "Copyright (c) Microsoft Corporation. All rights reserved.", - "license": "https://github.com/microsoft/BotFramework-FunctionalTests/blob/main/LICENSE", - "tags": [ - "echo" - ], - "endpoints": [ - { - "name": "default", - "protocol": "BotFrameworkV3", - "description": "Localhost endpoint for the skill (on port 36400)", - "endpointUrl": "http://localhost:36400/api/messages", - "msAppId": "00000000-0000-0000-0000-000000000000" - } - ] -} \ No newline at end of file diff --git a/tests/functional/Bots/JavaScript/Skills/CodeFirst/EchoSkillBot/package.json b/tests/functional/Bots/JavaScript/Skills/CodeFirst/EchoSkillBot/package.json deleted file mode 100644 index 862c06e364..0000000000 --- a/tests/functional/Bots/JavaScript/Skills/CodeFirst/EchoSkillBot/package.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "echo-skill-bot", - "version": "1.0.0", - "description": "Bot Builder v4 echo skill bot sample", - "author": "Microsoft", - "license": "MIT", - "main": "index.js", - "scripts": { - "start": "node ./index.js", - "watch": "nodemon ./index.js", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "repository": { - "type": "git", - "url": "https://github.com" - }, - "dependencies": { - "botbuilder": "~4.14.0", - "dotenv": "^8.2.0", - "restify": "~8.5.1" - }, - "devDependencies": { - "nodemon": "~2.0.4" - } -} diff --git a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/.env b/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/.env deleted file mode 100644 index a9c638cff3..0000000000 --- a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/.env +++ /dev/null @@ -1,11 +0,0 @@ -MicrosoftAppId= -MicrosoftAppPassword= -ConnectionName=TestOAuthProvider -SsoConnectionName= -ChannelService= -AllowedCallers= -SkillHostEndpoint=http://localhost:36420/api/skills/ - -EchoSkillInfo_id=EchoSkillBot -EchoSkillInfo_appId= -EchoSkillInfo_skillEndpoint=http://localhost:36400/api/messages diff --git a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/authentication/allowedCallersClaimsValidator.js b/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/authentication/allowedCallersClaimsValidator.js deleted file mode 100644 index a0146a3aca..0000000000 --- a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/authentication/allowedCallersClaimsValidator.js +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const { JwtTokenValidation, SkillValidation } = require('botframework-connector'); - -// Load the AppIds for the configured callers (we will only allow responses from skills we have configured). -// process.env.AllowedCallers is the list of parent bot Ids that are allowed to access the skill -// to add a new parent bot simply go to the .env file and add -// the parent bot's Microsoft AppId to the list under AllowedCallers, e.g.: -// AllowedCallers=195bd793-4319-4a84-a800-386770c058b2,38c74e7a-3d01-4295-8e66-43dd358920f8 -const allowedCallers = process.env.AllowedCallers ? process.env.AllowedCallers.split(',') : undefined; - -/** - * Sample claims validator that loads an allowed list from configuration if present - * and checks that requests are coming from allowed parent bots. - * @param claims An array of Claims decoded from the HTTP request's auth header - */ -const allowedCallersClaimsValidator = async (claims) => { - // If allowedCallers is undefined we allow all calls - if (allowedCallers && SkillValidation.isSkillClaim(claims)) { - // Check that the appId claim in the skill request is in the list of skills configured for this bot. - const appId = JwtTokenValidation.getAppIdFromClaims(claims); - if (!allowedCallers.includes(appId)) { - throw new Error(`Received a request from a bot with an app ID of "${appId}". To enable requests from this caller, add the app ID to your configuration file.`); - } - } -}; - -module.exports.allowedCallersClaimsValidator = allowedCallersClaimsValidator; diff --git a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/bots/skillBot.js b/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/bots/skillBot.js deleted file mode 100644 index 51baad225e..0000000000 --- a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/bots/skillBot.js +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const { ActivityHandler, ActivityTypes } = require('botbuilder'); -const { runDialog } = require('botbuilder-dialogs'); - -class SkillBot extends ActivityHandler { - /** - * @param {import('botbuilder').ConversationState} conversationState - * @param {import('botbuilder-dialogs').Dialog} dialog - * @param {string} serverUrl - */ - constructor (conversationState, dialog, serverUrl) { - super(); - if (!conversationState) throw new Error('[SkillBot]: Missing parameter. conversationState is required'); - if (!dialog) throw new Error('[SkillBot]: Missing parameter. dialog is required'); - - this.conversationState = conversationState; - this.dialog = dialog; - this.serverUrl = serverUrl; - - this.onTurn(async (turnContext, next) => { - if (turnContext.activity.type !== ActivityTypes.ConversationUpdate) { - await runDialog(this.dialog, turnContext, this.conversationState.createProperty('DialogState')); - } - - await next(); - }); - - this.onMembersAdded(async (turnContext, next) => { - const text = 'Welcome to the waterfall skill bot. \n\nThis is a skill, you will need to call it from another bot to use it.'; - - for (const member of turnContext.activity.membersAdded) { - if (member.id !== turnContext.activity.recipient.id) { - await turnContext.sendActivity({ - type: ActivityTypes.Message, - text, - speak: text.replace('\n\n', '') - }); - await turnContext.sendActivity(`You can check the skill manifest to see what it supports here: ${this.serverUrl}/manifests/waterfallskillbot-manifest-1.0.json`); - } - } - - // By calling next() you ensure that the next BotHandler is run. - await next(); - }); - } - - /** - * Override the ActivityHandler.run() method to save state changes after the bot logic completes. - * @param {import('botbuilder').TurnContext} context - */ - async run (context) { - await super.run(context); - - // Save any state changes. The load happened during the execution of the Dialog. - await this.conversationState.saveChanges(context); - } -} - -module.exports.SkillBot = SkillBot; diff --git a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/activityRouterDialog.js b/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/activityRouterDialog.js deleted file mode 100644 index 881f557a48..0000000000 --- a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/activityRouterDialog.js +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const { ActivityTypes, InputHints, MessageFactory } = require('botbuilder'); -const { DialogTurnStatus, WaterfallDialog, ComponentDialog, SkillDialog } = require('botbuilder-dialogs'); -const { AuthDialog } = require('./auth/authDialog'); -const { CardDialog } = require('./cards/cardDialog'); -const { FileUploadDialog } = require('./fileUpload/fileUploadDialog'); -const { MessageWithAttachmentDialog } = require('./messageWithAttachment/messageWithAttachmentDialog'); -const { WaitForProactiveDialog } = require('./proactive/waitForProactiveDialog'); -const { SsoSkillDialog } = require('./sso/ssoSkillDialog'); -const { DeleteDialog } = require('./delete/deleteDialog'); -const { UpdateDialog } = require('./update/updateDialog'); - -const MAIN_DIALOG = 'ActivityRouterDialog'; -const WATERFALL_DIALOG = 'WaterfallDialog'; -const CARDS_DIALOG = 'Cards'; -const PROACTIVE_DIALOG = 'Proactive'; -const ATTACHMENT_DIALOG = 'MessageWithAttachment'; -const AUTH_DIALOG = 'Auth'; -const SSO_DIALOG = 'Sso'; -const FILE_UPLOAD_DIALOG = 'FileUpload'; -const ECHO_DIALOG = 'Echo'; -const DELETE_DIALOG = 'Delete'; -const UPDATE_DIALOG = 'Update'; - -/** - * A root dialog that can route activities sent to the skill to different sub-dialogs. - */ -class ActivityRouterDialog extends ComponentDialog { - /** - * @param {string} serverUrl - * @param {import('botbuilder').ConversationState} conversationState - * @param {import('../skillConversationIdFactory').SkillConversationIdFactory} conversationIdFactory - * @param {import('botbuilder').SkillHttpClient} skillClient - * @param {Object} continuationParametersStore - */ - constructor (serverUrl, conversationState, conversationIdFactory, skillClient, continuationParametersStore) { - super(MAIN_DIALOG); - - this.addDialog(new CardDialog(CARDS_DIALOG, serverUrl)) - .addDialog(new WaitForProactiveDialog(PROACTIVE_DIALOG, serverUrl, continuationParametersStore)) - .addDialog(new MessageWithAttachmentDialog(ATTACHMENT_DIALOG, serverUrl)) - .addDialog(new AuthDialog(AUTH_DIALOG, process.env)) - .addDialog(new SsoSkillDialog(SSO_DIALOG, process.env)) - .addDialog(new FileUploadDialog(FILE_UPLOAD_DIALOG)) - .addDialog(new DeleteDialog(DELETE_DIALOG)) - .addDialog(new UpdateDialog(UPDATE_DIALOG)) - .addDialog(this.createEchoSkillDialog(ECHO_DIALOG, conversationState, conversationIdFactory, skillClient, process.env)) - .addDialog(new WaterfallDialog(WATERFALL_DIALOG, [ - this.processActivity.bind(this) - ])); - - this.initialDialogId = WATERFALL_DIALOG; - } - - /** - * @param {string} dialogId - * @param {import('botbuilder').ConversationState} conversationState - * @param {import('../skillConversationIdFactory').SkillConversationIdFactory} conversationIdFactory - * @param {import('botbuilder').SkillHttpClient} skillClient - * @param {Object} configuration - */ - createEchoSkillDialog (dialogId, conversationState, conversationIdFactory, skillClient, configuration) { - const skillHostEndpoint = configuration.SkillHostEndpoint; - if (!skillHostEndpoint) { - throw new Error('SkillHostEndpoint is not in configuration'); - } - - const skill = { - id: configuration.EchoSkillInfo_id, - appId: configuration.EchoSkillInfo_appId, - skillEndpoint: configuration.EchoSkillInfo_skillEndpoint - }; - - if (!skill.id || !skill.skillEndpoint) { - throw new Error('EchoSkillInfo_id and EchoSkillInfo_skillEndpoint are not set in configuration'); - } - - return new SkillDialog({ - botId: configuration.MicrosoftAppId, - conversationIdFactory, - skillClient, - skillHostEndpoint, - conversationState, - skill - }, dialogId); - } - - /** - * @param {import('botbuilder-dialogs').WaterfallStepContext} stepContext - */ - async processActivity (stepContext) { - // A skill can send trace activities, if needed. - await stepContext.context.sendActivity({ - type: ActivityTypes.Trace, - timestamp: new Date(), - text: 'ActivityRouterDialog.processActivity()', - label: `Got activityType: ${stepContext.context.activity.type}` - }); - - if (stepContext.context.activity.type === ActivityTypes.Event) { - return this.onEventActivity(stepContext); - } else { - // We didn't get an activity type we can handle. - await stepContext.context.sendActivity( - `Unrecognized ActivityType: "${stepContext.context.activity.type}".`, - undefined, - InputHints.IgnoringInput - ); - return { status: DialogTurnStatus.complete }; - } - } - - /** - * This method performs different tasks based on event name. - * @param {import('botbuilder-dialogs').WaterfallStepContext} stepContext - */ - async onEventActivity (stepContext) { - const activity = stepContext.context.activity; - await stepContext.context.sendActivity({ - type: ActivityTypes.Trace, - timestamp: new Date(), - text: 'ActivityRouterDialog.onEventActivity()', - label: `Name: ${activity.name}, Value: ${JSON.stringify(activity.value)}` - }); - - // Resolve what to execute based on the event name. - switch (activity.name) { - case CARDS_DIALOG: - case PROACTIVE_DIALOG: - case ATTACHMENT_DIALOG: - case AUTH_DIALOG: - case SSO_DIALOG: - case FILE_UPLOAD_DIALOG: - case DELETE_DIALOG: - case UPDATE_DIALOG: - return stepContext.beginDialog(activity.name); - - case ECHO_DIALOG: { - // Start the EchoSkillBot - const messageActivity = MessageFactory.text("I'm the echo skill bot"); - messageActivity.deliveryMode = activity.deliveryMode; - return stepContext.beginDialog(activity.name, { activity: messageActivity }); - } - - default: - // We didn't get an event name we can handle. - await stepContext.context.sendActivity( - `Unrecognized EventName: "${stepContext.context.activity.name}".`, - undefined, - InputHints.IgnoringInput - ); - return { status: DialogTurnStatus.complete }; - } - } -} - -module.exports.ActivityRouterDialog = ActivityRouterDialog; diff --git a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/auth/authDialog.js b/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/auth/authDialog.js deleted file mode 100644 index 573df18620..0000000000 --- a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/auth/authDialog.js +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const { InputHints, MessageFactory } = require('botbuilder'); -const { ComponentDialog, OAuthPrompt, WaterfallDialog, ConfirmPrompt } = require('botbuilder-dialogs'); - -const WATERFALL_DIALOG = 'WaterfallDialog'; -const OAUTH_PROMPT = 'OAuthPrompt'; -const CONFIRM_PROMPT = 'ConfirmPrompt'; - -class AuthDialog extends ComponentDialog { - /** - * @param {string} dialogId - * @param {Object} configuration - */ - constructor (dialogId, configuration) { - super(dialogId); - - this.connectionName = configuration.ConnectionName; - - this.addDialog(new ConfirmPrompt(CONFIRM_PROMPT)) - .addDialog(new OAuthPrompt(OAUTH_PROMPT, { - connectionName: this.connectionName, - text: `Please Sign In to connection: '${this.connectionName}'`, - title: 'Sign In', - timeout: 300000 // User has 5 minutes to login (1000 * 60 * 5) - })); - - this.addDialog(new WaterfallDialog(WATERFALL_DIALOG, [ - this.promptStep.bind(this), - this.loginStep.bind(this), - this.displayToken.bind(this) - ])); - - // The initial child Dialog to run. - this.initialDialogId = WATERFALL_DIALOG; - } - - /** - * @param {import('botbuilder-dialogs').WaterfallStepContext} stepContext - */ - promptStep (stepContext) { - return stepContext.beginDialog(OAUTH_PROMPT); - } - - /** - * @param {import('botbuilder-dialogs').WaterfallStepContext} stepContext - */ - async loginStep (stepContext) { - // Get the token from the previous step. - const tokenResponse = stepContext.result; - - if (tokenResponse) { - stepContext.values.token = tokenResponse.token; - - // Show the token - const loggedInMessage = 'You are now logged in.'; - await stepContext.context.sendActivity(MessageFactory.text(loggedInMessage, loggedInMessage, InputHints.IgnoringInput)); - - return stepContext.prompt(CONFIRM_PROMPT, { - prompt: MessageFactory.text('Would you like to view your token?') - }); - } - - const tryAgainMessage = 'Login was not successful please try again.'; - await stepContext.context.sendActivity(MessageFactory.text(tryAgainMessage, tryAgainMessage, InputHints.IgnoringInput)); - return stepContext.replaceDialog(this.initialDialogId); - } - - /** - * @param {import('botbuilder-dialogs').WaterfallStepContext} stepContext - */ - async displayToken (stepContext) { - const result = stepContext.result; - - if (result) { - const showTokenMessage = 'Here is your token:'; - await stepContext.context.sendActivity(MessageFactory.text(`${showTokenMessage} ${stepContext.values.token}`, showTokenMessage, InputHints.IgnoringInput)); - } - - // Sign out - stepContext.context.adapter.signOutUser(stepContext.context, this.connectionName); - const signOutMessage = 'I have signed you out.'; - await stepContext.context.sendActivity(MessageFactory.text(signOutMessage, signOutMessage, InputHints.IgnoringInput)); - - return stepContext.endDialog(); - } -} - -module.exports.AuthDialog = AuthDialog; diff --git a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/cards/cardDialog.js b/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/cards/cardDialog.js deleted file mode 100644 index 58f61927b0..0000000000 --- a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/cards/cardDialog.js +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const { InputHints, MessageFactory, CardFactory, ActionTypes } = require('botbuilder'); -const { ComponentDialog, WaterfallDialog, ChoicePrompt, ListStyle, ChoiceFactory, DialogTurnStatus } = require('botbuilder-dialogs'); -const fs = require('fs'); -const path = require('path'); -const { CardOptions } = require('./cardOptions'); -const { CardSampleHelper } = require('./cardSampleHelper'); -const { ChannelSupportedCards } = require('./channelSupportedCards'); - -const WATERFALL_DIALOG = 'WaterfallDialog'; -const CARD_PROMPT = 'CardPrompt'; - -class CardDialog extends ComponentDialog { - /** - * @param {string} dialogId - * @param {string} serverUrl - */ - constructor (dialogId, serverUrl) { - super(dialogId); - - this.serverUrl = serverUrl; - this.mindBlownGif = 'https://media3.giphy.com/media/xT0xeJpnrWC4XWblEk/giphy.gif?cid=ecf05e47mye7k75sup6tcmadoom8p1q8u03a7g2p3f76upp9&rid=giphy.gif'; - this.corgiOnCarouselVideo = 'https://www.youtube.com/watch?v=LvqzubPZjHE'; - this.teamsLogoFileName = 'teams-logo.png'; - - this.addDialog(new ChoicePrompt(CARD_PROMPT, this.cardPromptValidator)) - .addDialog(new WaterfallDialog(WATERFALL_DIALOG, [ - this.selectCard.bind(this), - this.displayCard.bind(this) - ])); - - this.initialDialogId = WATERFALL_DIALOG; - } - - /** - * @param {import('botbuilder-dialogs').WaterfallStepContext} stepContext - * @returns {import('botframework-schema').HeroCard} - */ - makeUpdatedHeroCard (stepContext) { - const data = stepContext.context.activity.value; - data.count++; - - const { title, text, cardActions } = { - title: 'Newly updated card.', - text: `Update count - ${data.count}`, - cardActions: [ - { - type: ActionTypes.MessageBack, - title: 'Update Card', - text: 'UpdateCardAction', - value: data - } - ] - }; - - return CardFactory.heroCard(title, text, null, cardActions); - } - - /** - * @param {import('botbuilder-dialogs').WaterfallStepContext} stepContext - */ - async selectCard (stepContext) { - // Create the PromptOptions from the skill configuration which contain the list of configured skills. - const messageText = 'What card do you want?'; - const repromptMessageText = 'This message will be created in the validation code'; - const options = { - prompt: MessageFactory.text(messageText, messageText, InputHints.ExpectingInput), - retryPrompt: MessageFactory.text(repromptMessageText, repromptMessageText, InputHints.ExpectingInput), - choices: ChoiceFactory.toChoices(Object.values(CardOptions)), - style: ListStyle.list - }; - - // Ask the user to enter a card choice. - return stepContext.prompt(CARD_PROMPT, options); - } - - /** - * @param {import('botbuilder-dialogs').WaterfallStepContext} stepContext - */ - async displayCard (stepContext) { - if (stepContext.context.activity.value != null) { - await this.handleSpecialActivity(stepContext); - } else { - // Checks to see if the activity is an adaptive card update or a bot action respose. - const card = stepContext.result.value; - const cardType = Object.keys(CardOptions).find(key => CardOptions[key].toLowerCase() === card.toLowerCase()); - const { channelId } = stepContext.context.activity; - - if (ChannelSupportedCards.isCardSupported(channelId, cardType)) { - switch (cardType) { - case CardOptions.AdaptiveCardBotAction: - await stepContext.context.sendActivity(MessageFactory.attachment(CardSampleHelper.createAdaptiveCardBotAction())); - break; - - case CardOptions.AdaptiveCardTeamsTaskModule: - await stepContext.context.sendActivity(MessageFactory.attachment(CardSampleHelper.createAdaptiveCardTaskModule())); - break; - - case CardOptions.AdaptiveCardSubmitAction: - await stepContext.context.sendActivity(MessageFactory.attachment(CardSampleHelper.createAdaptiveCardSubmit())); - break; - - case CardOptions.Hero: - await stepContext.context.sendActivity(MessageFactory.attachment(CardSampleHelper.createHeroCard())); - break; - - case CardOptions.Thumbnail: - await stepContext.context.sendActivity(MessageFactory.attachment(CardSampleHelper.createThumbnailCard())); - break; - - case CardOptions.Receipt: - await stepContext.context.sendActivity(MessageFactory.attachment(CardSampleHelper.createReceiptCard())); - break; - - case CardOptions.Signin: - await stepContext.context.sendActivity(MessageFactory.attachment(CardSampleHelper.createSigninCard())); - break; - - case CardOptions.Carousel: - // NOTE: if cards are NOT the same height in a carousel, Teams will instead display as AttachmentLayoutTypes.List - await stepContext.context.sendActivity(MessageFactory.carousel([ - CardSampleHelper.createHeroCard(), - CardSampleHelper.createHeroCard(), - CardSampleHelper.createHeroCard() - ])); - break; - - case CardOptions.List: - await stepContext.context.sendActivity(MessageFactory.list([ - CardSampleHelper.createHeroCard(), - CardSampleHelper.createHeroCard(), - CardSampleHelper.createHeroCard() - ])); - break; - - case CardOptions.O365: - await stepContext.context.sendActivity(MessageFactory.attachment(CardSampleHelper.createO365ConnectorCard())); - break; - - case CardOptions.TeamsFileConsent: { - const file = fs.readFileSync(path.resolve(__dirname, 'files', this.teamsLogoFileName)); - await stepContext.context.sendActivity(MessageFactory.attachment(CardSampleHelper.createTeamsFileConsentCard(this.teamsLogoFileName, file.byteLength))); - break; - } - - case CardOptions.Animation: - await stepContext.context.sendActivity(MessageFactory.attachment(CardSampleHelper.createAnimationCard(this.mindBlownGif))); - break; - - case CardOptions.Audio: - await stepContext.context.sendActivity(MessageFactory.attachment(CardSampleHelper.createAudioCard(`${this.serverUrl}/api/music`))); - break; - - case CardOptions.Video: - await stepContext.context.sendActivity(MessageFactory.attachment(CardSampleHelper.createVideoCard(this.corgiOnCarouselVideo))); - break; - - case CardOptions.AdaptiveUpdate: - await stepContext.context.sendActivity(MessageFactory.attachment(CardSampleHelper.createUpdateAdaptiveCard())); - break; - - case CardOptions.End: - return { status: DialogTurnStatus.complete }; - } - } else { - await stepContext.context.sendActivity(MessageFactory.text(`${card} cards are not supported in the ${channelId} channel.`)); - } - } - - return stepContext.replaceDialog(this.initialDialogId); - } - - /** - * @param {import('botbuilder-dialogs').WaterfallStepContext} stepContext - */ - async handleSpecialActivity (stepContext) { - if (!stepContext.context.activity.text) { - await stepContext.context.sendActivity(MessageFactory.Text(`I received an activity with this data in the value field ${stepContext.context.activity.value}`)); - } else { - if (stepContext.context.activity.text.toLowerCase().includes('update')) { - if (!stepContext.context.activity.replyToId) { - await stepContext.context.sendActivity(MessageFactory.Text(`Update activity is not supported in the ${stepContext.context.activity.channelId} channel.`)); - } else { - const heroCard = this.makeUpdatedHeroCard(stepContext); - const activity = MessageFactory.attachment(heroCard); - activity.id = stepContext.context.activity.replyToId; - await stepContext.context.updateActivity(activity); - } - } else { - await stepContext.context.sendActivity(MessageFactory.Text(`I received an activity with this data in the text field ${stepContext.context.activity.text} and this data in the value field ${stepContext.context.activity.value}`)); - } - } - } - - /** - * @param {import('botbuilder-dialogs').PromptValidatorContext} promptContext - */ - async cardPromptValidator (promptContext) { - if (!promptContext.recognized.succeeded) { - // This checks to see if this response is the user clicking the update button on the card - if (promptContext.context.activity.value) { - return true; - } - - if (promptContext.context.activity.attachments) { - return true; - } - - const activityJson = JSON.stringify(promptContext.context.activity, null, 4).replace(/\n/g, '\r\n'); - - // Render the activity so we can assert in tests. - // We may need to simplify the json if it gets too complicated to test. - promptContext.options.retryPrompt.text = `Got ${activityJson}\n\n${promptContext.options.prompt.text}`; - return false; - } - - return true; - } -} - -module.exports.CardDialog = CardDialog; diff --git a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/cards/cardOptions.js b/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/cards/cardOptions.js deleted file mode 100644 index 85a54205a0..0000000000 --- a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/cards/cardOptions.js +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -module.exports.CardOptions = { - /** - * Adaptive card - Bot action - */ - AdaptiveCardBotAction: 'AdaptiveCardBotAction', - /** - * Adaptive card - Task module - */ - AdaptiveCardTeamsTaskModule: 'AdaptiveCardTeamsTaskModule', - /** - * Adaptive card - Submit action - */ - AdaptiveCardSubmitAction: 'AdaptiveCardSubmitAction', - /** - * Hero cards - */ - Hero: 'Hero', - /** - * Thumbnail cards - */ - Thumbnail: 'Thumbnail', - /** - * Receipt cards - */ - Receipt: 'Receipt', - /** - * Signin cards - */ - Signin: 'Signin', - /** - * Carousel cards - */ - Carousel: 'Carousel', - /** - * List cards - */ - List: 'List', - /** - * O365 cards - */ - O365: 'O365', - /** - * File cards - */ - TeamsFileConsent: 'TeamsFileConsent', - /** - * Animation cards - */ - Animation: 'Animation', - /** - * Audio cards - */ - Audio: 'Audio', - /** - * Video cards - */ - Video: 'Video', - /** - * Adaptive update cards - */ - AdaptiveUpdate: 'AdaptiveUpdate', - /** - * Ends the card selection dialog - */ - End: 'End' -}; diff --git a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/cards/cardSampleHelper.js b/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/cards/cardSampleHelper.js deleted file mode 100644 index 36613d78de..0000000000 --- a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/cards/cardSampleHelper.js +++ /dev/null @@ -1,514 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const { CardFactory, ActionTypes } = require('botbuilder'); - -class CardSampleHelper { - static createAdaptiveCardBotAction () { - return CardFactory.adaptiveCard({ - type: 'AdaptiveCard', - version: '1.2', - body: [ - { - text: 'Bot Builder actions', - type: 'TextBlock' - } - ], - actions: [ - { - type: 'Action.Submit', - title: 'imBack', - data: { - msteams: { - type: 'imBack', - value: 'text' - } - } - }, - { - type: 'Action.Submit', - title: 'message back', - data: { - msteams: { - type: ActionTypes.MessageBack, - value: { - key: 'value' - } - } - } - }, - { - type: 'Action.Submit', - title: 'message back local echo', - data: { - msteams: { - type: ActionTypes.MessageBack, - text: 'text received by bots', - displayText: 'display text message back', - value: { - key: 'value' - } - } - } - }, - { - type: 'Action.Submit', - title: 'invoke', - data: { - msteams: { - type: 'invoke', - value: { - key: 'value' - } - } - } - } - ] - }); - } - - static createAdaptiveCardTaskModule () { - return CardFactory.adaptiveCard({ - type: 'AdaptiveCard', - version: '1.2', - body: [ - { - type: 'TextBlock', - text: 'Task Module Adaptive Card' - } - ], - actions: [ - { - type: 'Action.Submit', - title: 'Launch Task Module', - data: { - msteams: { - type: 'invoke', - value: '{\r\n "hiddenKey": "hidden value from task module launcher",\r\n "type": "task/fetch"\r\n}' - } - } - } - ] - }); - } - - static createAdaptiveCardSubmit () { - return CardFactory.adaptiveCard({ - type: 'AdaptiveCard', - version: '1.2', - body: [ - { - type: 'TextBlock', - text: 'Bot Builder actions' - }, - { - type: 'Input.Text', - id: 'x' - } - ], - actions: [ - { - type: 'Action.Submit', - title: 'Action.Submit', - data: { - key: 'value' - } - } - ] - }); - } - - static createHeroCard () { - const { title, subtitle, text, images, buttons } = { - title: 'BotFramework Hero Card', - subtitle: 'Microsoft Bot Framework', - text: 'Build and connect intelligent bots to interact with your users naturally wherever they are, from text/sms to Skype, Slack, Office 365 mail and other popular services.', - images: [ - { - url: 'https://sec.ch9.ms/ch9/7ff5/e07cfef0-aa3b-40bb-9baa-7c9ef8ff7ff5/buildreactionbotframework_960.jpg' - } - ], - buttons: [ - { - type: ActionTypes.OpenUrl, - title: 'Get Started', - value: 'https://docs.microsoft.com/bot-framework' - } - ] - }; - - return CardFactory.heroCard(title, images, buttons, { subtitle, text }); - } - - static createThumbnailCard () { - const { title, subtitle, text, images, buttons } = { - title: 'BotFramework Thumbnail Card', - subtitle: 'Microsoft Bot Framework', - text: 'Build and connect intelligent bots to interact with your users naturally wherever they are, from text/sms to Skype, Slack, Office 365 mail and other popular services.', - images: [ - { - url: 'https://sec.ch9.ms/ch9/7ff5/e07cfef0-aa3b-40bb-9baa-7c9ef8ff7ff5/buildreactionbotframework_960.jpg' - } - ], - buttons: [ - { - type: ActionTypes.OpenUrl, - title: 'Get Started', - value: 'https://docs.microsoft.com/bot-framework' - } - ] - }; - - return CardFactory.thumbnailCard(title, images, buttons, { subtitle, text }); - } - - static createReceiptCard () { - return CardFactory.receiptCard({ - title: 'John Doe', - facts: [ - { - key: 'Order Number', - value: '1234' - }, - { - key: 'Payment Method', - value: 'VISA 5555-****' - } - ], - items: [ - { - title: 'Data Transfer', - image: { - url: 'https://github.com/amido/azure-vector-icons/raw/master/renders/traffic-manager.png' - }, - price: '$ 38.45', - quantity: '368' - }, - { - title: 'App Service', - image: { - url: 'https://github.com/amido/azure-vector-icons/raw/master/renders/cloud-service.png' - }, - price: '$ 45.00', - quantity: '720' - } - ], - total: '$ 90.95', - tax: '$ 7.50', - buttons: [ - { - type: ActionTypes.OpenUrl, - title: 'More information', - image: 'https://account.windowsazure.com/content/6.10.1.38-.8225.160809-1618/aux-pre/images/offer-icon-freetrial.png', - value: 'https://azure.microsoft.com/en-us/pricing/' - } - ] - }); - } - - static createSigninCard () { - return CardFactory.signinCard('Sign-in', 'https://login.microsoftonline.com/', 'BotFramework Sign-in Card'); - } - - static createO365ConnectorCard () { - return CardFactory.o365ConnectorCard({ - title: 'card title', - text: 'card text', - summary: 'O365 card summary', - themeColor: '#E67A9E', - sections: [ - { - title: '**section title**', - text: 'section text', - activityTitle: 'activity title', - activitySubtitle: 'activity subtitle', - activityText: 'activity text', - activityImage: 'http://connectorsdemo.azurewebsites.net/images/MSC12_Oscar_002.jpg', - activityImageType: 'avatar', - markdown: true, - facts: [ - { - name: 'Fact name 1', - value: 'Fact value 1' - }, - { - name: 'Fact name 2', - value: 'Fact value 2' - } - ], - images: [ - { - image: 'http://connectorsdemo.azurewebsites.net/images/MicrosoftSurface_024_Cafe_OH-06315_VS_R1c.jpg', - title: 'image 1' - }, - { - image: 'http://connectorsdemo.azurewebsites.net/images/WIN12_Scene_01.jpg', - title: 'image 2' - }, - { - image: 'http://connectorsdemo.azurewebsites.net/images/WIN12_Anthony_02.jpg', - title: 'image 3' - } - ] - } - ], - potentialAction: - [ - { - '@type': 'ActionCard', - inputs: [ - { - '@type': 'MultichoiceInput', - choices: [ - { - display: 'Choice 1', - value: '1' - }, - { - display: 'Choice 2', - value: '2' - }, - { - display: 'Choice 3', - value: '3' - } - ], - style: 'expanded', - isMultiSelect: true, - id: 'list-1', - isRequired: true, - title: 'Pick multiple options' - }, - { - '@type': 'MultichoiceInput', - choices: [ - { - display: 'Choice 4', - value: '4' - }, - { - display: 'Choice 5', - value: '5' - }, - { - display: 'Choice 6', - value: '6' - } - ], - style: 'compact', - isMultiSelect: true, - id: 'list-2', - isRequired: true, - title: 'Pick multiple options' - }, - { - '@type': 'MultichoiceInput', - choices: [ - { - display: 'Choice a', - value: 'a' - }, - { - display: 'Choice b', - value: 'b' - }, - { - display: 'Choice c', - value: 'c' - } - ], - style: 'expanded', - isMultiSelect: false, - id: 'list-3', - isRequired: false, - title: 'Pick an option' - }, - { - '@type': 'MultichoiceInput', - choices: [ - { - display: 'Choice x', - value: 'x' - }, - { - display: 'Choice y', - value: 'y' - }, - { - display: 'Choice z', - value: 'z' - } - ], - style: 'compact', - isMultiSelect: false, - id: 'list-4', - isRequired: false, - title: 'Pick an option' - } - ], - actions: [ - { - '@type': 'HttpPOST', - body: '{"list1":"{{list-1.value}}", "list2":"{{list-2.value}}", "list3":"{{list-3.value}}", "list4":"{{list-4.value}}"}', - name: 'Send', - '@id': 'card-1-btn-1' - } - ], - name: 'Multiple Choice', - '@id': 'card-1' - }, - { - '@type': 'ActionCard', - inputs: [ - { - '@type': 'TextInput', - isMultiline: true, - id: 'text-1', - isRequired: false, - title: 'multiline, no maxLength' - }, - { - '@type': 'TextInput', - isMultiline: false, - id: 'text-2', - isRequired: false, - title: 'single line, no maxLength' - }, - { - '@type': 'TextInput', - isMultiline: true, - maxLength: 10, - id: 'text-3', - isRequired: true, - title: 'multiline, max len = 10, isRequired' - }, - { - '@type': 'TextInput', - isMultiline: false, - maxLength: 10, - id: 'text-4', - isRequired: true, - title: 'single line, max len = 10, isRequired' - } - ], - actions: [ - { - '@type': 'HttpPOST', - body: '{"text1":"{{text-1.value}}", "text2":"{{text-2.value}}", "text3":"{{text-3.value}}", "text4":"{{text-4.value}}"}', - name: 'Send', - '@id': 'card-2-btn-1' - } - ], - name: 'Text Input', - '@id': 'card-2' - }, - { - '@type': 'ActionCard', - inputs: [ - { - '@type': 'DateInput', - includeTime: true, - id: 'date-1', - isRequired: true, - title: 'date with time' - }, - { - '@type': 'DateInput', - includeTime: false, - id: 'date-2', - isRequired: false, - title: 'date only' - } - ], - actions: [ - { - '@type': 'HttpPOST', - body: '{"date1":"{{date-1.value}}", "date2":"{{date-2.value}}"}', - name: 'Send', - '@id': 'card-3-btn-1' - } - ], - name: 'Date Input', - '@id': 'card-3' - }, - { - '@type': 'ViewAction', - target: [ - 'http://microsoft.com' - ], - name: 'View Action' - }, - { - '@type': 'OpenUri', - targets: [ - { - os: 'default', - uri: 'http://microsoft.com' - }, - { - os: 'iOS', - uri: 'http://microsoft.com' - }, - { - os: 'android', - uri: 'http://microsoft.com' - }, - { - os: 'windows', - uri: 'http://microsoft.com' - } - ], - name: 'Open Uri', - '@id': 'open-uri' - } - ] - - }); - } - - static createTeamsFileConsentCard (filename, filesize) { - const consentContext = { filename }; - - return { - contentType: 'application/vnd.microsoft.teams.card.file.consent', - name: filename, - content: { - description: 'This is the file I want to send you', - sizeInbytes: filesize, - acceptContext: consentContext, - declineContext: consentContext - } - }; - } - - static createAnimationCard (url) { - return CardFactory.animationCard('Animation Card', [{ url }], null, { autostart: true }); - } - - static createAudioCard (url) { - return CardFactory.audioCard('Audio Card', [{ url }], null, { autoloop: true }); - } - - static createVideoCard (url) { - return CardFactory.videoCard('Video Card', [{ url }]); - } - - static createUpdateAdaptiveCard () { - const { title, text, buttons } = { - title: 'Update card', - text: 'Update Card Action', - buttons: [ - { - type: ActionTypes.MessageBack, - title: 'Update card title', - text: 'Update card text', - value: { count: 0 } - } - ] - }; - - return CardFactory.heroCard(title, text, null, buttons); - } -} - -module.exports.CardSampleHelper = CardSampleHelper; diff --git a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/cards/channelSupportedCards.js b/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/cards/channelSupportedCards.js deleted file mode 100644 index 31770b6939..0000000000 --- a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/cards/channelSupportedCards.js +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const { Channels } = require('botbuilder'); -const { CardOptions } = require('./cardOptions'); - -/** - * This tracks what cards are not supported in a given channel. - */ -const unsupportedChannelCards = { - [Channels.Emulator]: [ - CardOptions.AdaptiveCardTeamsTaskModule, - CardOptions.AdaptiveUpdate, - CardOptions.TeamsFileConsent, - CardOptions.O365 - ], - [Channels.Directline]: [ - CardOptions.AdaptiveUpdate - ], - [Channels.Telegram]: [ - CardOptions.AdaptiveCardBotAction, - CardOptions.AdaptiveCardTeamsTaskModule, - CardOptions.AdaptiveCardSubmitAction, - CardOptions.List, - CardOptions.TeamsFileConsent - ] -}; - -class ChannelSupportedCards { - /** - * This let's you know if a card is supported in a given channel. - * @param {string} channel Bot Connector Channel. - * @param {keyof CardOptions} type Card Option to be checked. - * @returns A bool if the card is supported in the channel. - */ - static isCardSupported (channel, type) { - const unsupportedChannel = unsupportedChannelCards[channel]; - if (unsupportedChannel) { - return !unsupportedChannel.includes(type); - } - - return true; - } -} - -module.exports.ChannelSupportedCards = ChannelSupportedCards; diff --git a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/cards/files/buildreactionbotframework.jpg b/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/cards/files/buildreactionbotframework.jpg deleted file mode 100644 index f410fc137e..0000000000 Binary files a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/cards/files/buildreactionbotframework.jpg and /dev/null differ diff --git a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/cards/files/music.mp3 b/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/cards/files/music.mp3 deleted file mode 100644 index b4ff6ee30f..0000000000 Binary files a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/cards/files/music.mp3 and /dev/null differ diff --git a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/cards/files/teams-logo.png b/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/cards/files/teams-logo.png deleted file mode 100644 index 78b0a0c308..0000000000 Binary files a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/cards/files/teams-logo.png and /dev/null differ diff --git a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/delete/deleteDialog.js b/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/delete/deleteDialog.js deleted file mode 100644 index b814ff70f4..0000000000 --- a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/delete/deleteDialog.js +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const { MessageFactory } = require('botbuilder'); -const { ComponentDialog, WaterfallDialog, DialogTurnStatus } = require('botbuilder-dialogs'); -const { Channels } = require('botbuilder-core'); - -const SLEEP_TIMER = 5000; -const WATERFALL_DIALOG = 'WaterfallDialog'; -const DELETE_SUPPORTED = new Set([Channels.Msteams, Channels.Slack, Channels.Telegram]); - -class DeleteDialog extends ComponentDialog { - /** - * @param {string} dialogId - */ - constructor (dialogId) { - super(dialogId); - - this.addDialog(new WaterfallDialog(WATERFALL_DIALOG, [ - this.handleDeleteDialog.bind(this) - ])); - - this.initialDialogId = WATERFALL_DIALOG; - } - - /** - * @param {import('botbuilder-dialogs').WaterfallStepContext} stepContext - */ - async handleDeleteDialog (stepContext) { - const channel = stepContext.context.activity.channelId; - - if (DeleteDialog.isDeleteSupported(channel)) { - const id = await stepContext.context.sendActivity(MessageFactory.text('I will delete this message in 5 seconds')); - await DeleteDialog.sleep(SLEEP_TIMER); - await stepContext.context.deleteActivity(id.id); - } else { - await stepContext.context.sendActivity(MessageFactory.text(`Delete is not supported in the ${channel} channel.`)); - } - - return { status: DialogTurnStatus.complete }; - } - - /** - * @param {number} milliseconds - */ - static sleep (milliseconds) { - return new Promise(resolve => { - setTimeout(resolve, milliseconds); - }); - } - - /** - * @param {string} channel - */ - static isDeleteSupported (channel) { - return DELETE_SUPPORTED.has(channel); - } -} - -module.exports.DeleteDialog = DeleteDialog; diff --git a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/fileUpload/fileUploadDialog.js b/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/fileUpload/fileUploadDialog.js deleted file mode 100644 index 5cc8d38c48..0000000000 --- a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/fileUpload/fileUploadDialog.js +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const { MessageFactory, InputHints } = require('botbuilder'); -const { ComponentDialog, AttachmentPrompt, WaterfallDialog, DialogTurnStatus, ConfirmPrompt } = require('botbuilder-dialogs'); -const fs = require('fs'); -const fetch = require('node-fetch'); -const os = require('os'); -const path = require('path'); -const stream = require('stream'); -const util = require('util'); - -const streamPipeline = util.promisify(stream.pipeline); - -const ATTACHMENT_PROMPT = 'AttachmentPrompt'; -const CONFIRM_PROMPT = 'ConfirmPrompt'; -const WATERFALL_DIALOG = 'WaterfallDialog'; - -class FileUploadDialog extends ComponentDialog { - /** - * @param {string} dialogId - */ - constructor (dialogId) { - super(dialogId); - - this.addDialog(new AttachmentPrompt(ATTACHMENT_PROMPT)) - .addDialog(new ConfirmPrompt(CONFIRM_PROMPT)) - .addDialog(new WaterfallDialog(WATERFALL_DIALOG, [ - this.promptUploadStep.bind(this), - this.handleAttachmentStep.bind(this), - this.finalStep.bind(this) - ])); - - this.initialDialogId = WATERFALL_DIALOG; - } - - /** - * @param {import('botbuilder-dialogs').WaterfallStepContext} stepContext - */ - async promptUploadStep (stepContext) { - return stepContext.prompt(ATTACHMENT_PROMPT, { - prompt: MessageFactory.text('Please upload a file to continue.'), - retryPrompt: MessageFactory.text('You must upload a file.') - }); - } - - /** - * @param {import('botbuilder-dialogs').WaterfallStepContext} stepContext - */ - async handleAttachmentStep (stepContext) { - let fileText = ''; - let fileContent = ''; - - for (const file of stepContext.context.activity.attachments) { - const localFileName = path.resolve(os.tmpdir(), file.name); - const tempFile = fs.createWriteStream(localFileName); - const response = await fetch(file.contentUrl); - await streamPipeline(response.body, tempFile); - - fileContent = fs.readFileSync(localFileName, 'utf8'); - fileText += `Attachment "${file.name}" has been received.\r\n`; - fileText += `File content: ${fileContent}\r\n`; - } - - await stepContext.context.sendActivity(MessageFactory.text(fileText)); - - // Ask to upload another file or end. - const messageText = 'Do you want to upload another file?'; - const repromptMessageText = "That's an invalid choice."; - - return stepContext.prompt(CONFIRM_PROMPT, { - prompt: MessageFactory.text(messageText, messageText, InputHints.ExpectingInput), - retryPrompt: MessageFactory.text(repromptMessageText, repromptMessageText, InputHints.ExpectingInput) - }); - } - - /** - * @param {import('botbuilder-dialogs').WaterfallStepContext} stepContext - */ - async finalStep (stepContext) { - const tryAnother = stepContext.result; - - if (tryAnother) { - return stepContext.replaceDialog(this.initialDialogId); - } - - return { status: DialogTurnStatus.complete }; - } -} - -module.exports.FileUploadDialog = FileUploadDialog; diff --git a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/messageWithAttachment/messageWithAttachmentDialog.js b/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/messageWithAttachment/messageWithAttachmentDialog.js deleted file mode 100644 index 5d242b7d92..0000000000 --- a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/messageWithAttachment/messageWithAttachmentDialog.js +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const { MessageFactory, InputHints } = require('botbuilder'); -const { ComponentDialog, ChoicePrompt, WaterfallDialog, ChoiceFactory, DialogTurnStatus, ConfirmPrompt } = require('botbuilder-dialogs'); -const fs = require('fs'); -const path = require('path'); - -const ATTACHMENT_TYPE_PROMPT = 'AttachmentTypePrompt'; -const CONFIRM_PROMPT = 'ConfirmPrompt'; -const WATERFALL_DIALOG = 'WaterfallDialog'; - -class MessageWithAttachmentDialog extends ComponentDialog { - /** - * @param {string} dialogId - * @param {string} serverUrl - */ - constructor (dialogId, serverUrl) { - super(dialogId); - - this.picture = 'architecture-resize.png'; - this.serverUrl = serverUrl; - - this.addDialog(new ChoicePrompt(ATTACHMENT_TYPE_PROMPT)) - .addDialog(new ConfirmPrompt(CONFIRM_PROMPT)) - .addDialog(new WaterfallDialog(WATERFALL_DIALOG, [ - this.selectAttachmentType.bind(this), - this.sendActivityWithAttachment.bind(this), - this.finalStep.bind(this) - ])); - - this.initialDialogId = WATERFALL_DIALOG; - } - - /** - * @param {import('botbuilder-dialogs').WaterfallStepContext} stepContext - */ - async selectAttachmentType (stepContext) { - const messageText = 'What attachment type do you want?'; - const repromptMessageText = 'That was not a valid choice, please select a valid card type.'; - const options = { - prompt: MessageFactory.text(messageText, messageText, InputHints.ExpectingInput), - retryPrompt: MessageFactory.text(repromptMessageText, repromptMessageText, InputHints.ExpectingInput), - choices: ChoiceFactory.toChoices(['Inline', 'Internet']) - }; - - return stepContext.prompt(ATTACHMENT_TYPE_PROMPT, options); - } - - /** - * @param {import('botbuilder-dialogs').WaterfallStepContext} stepContext - */ - async sendActivityWithAttachment (stepContext) { - const attachmentType = stepContext.result.value.toLowerCase(); - const reply = MessageFactory.text('', '', InputHints.IgnoringInput); - - switch (attachmentType) { - case 'inline': - reply.text = 'This is an inline attachment.'; - reply.attachments = [this.getInlineAttachment()]; - break; - - case 'internet': - reply.text = 'This is an attachment from a HTTP URL.'; - reply.attachments = [this.getInternetAttachment()]; - break; - - default: - throw new Error(`Invalid card type ${attachmentType}`); - } - - await stepContext.context.sendActivity(reply); - - // Ask to submit another or end. - const messageText = 'Do you want another type of attachment?'; - const repromptMessageText = "That's an invalid choice."; - - return stepContext.prompt(CONFIRM_PROMPT, { - prompt: MessageFactory.text(messageText, messageText, InputHints.ExpectingInput), - retryPrompt: MessageFactory.text(repromptMessageText, repromptMessageText, InputHints.ExpectingInput) - }); - } - - /** - * @param {import('botbuilder-dialogs').WaterfallStepContext} stepContext - */ - async finalStep (stepContext) { - const tryAnother = stepContext.result; - - if (tryAnother) { - return stepContext.replaceDialog(this.initialDialogId); - } - - return { status: DialogTurnStatus.complete }; - } - - /** - * Returns an inline attachment. - * @returns {import('botbuilder').Attachment} - */ - getInlineAttachment () { - const filepath = path.resolve(process.cwd(), 'images', this.picture); - const file = fs.readFileSync(filepath, 'base64'); - return { - name: `Files/${this.picture}`, - contentType: 'image/png', - contentUrl: `data:image/png;base64,${file}` - }; - } - - /** - * Returns an attachment to be sent to the user from a HTTPS URL. - * @returns {import('botbuilder').Attachment} - */ - getInternetAttachment () { - return { - name: `Files/${this.picture}`, - contentType: 'image/png', - contentUrl: `${this.serverUrl}/images/architecture-resize.png` - }; - } -} - -module.exports.MessageWithAttachmentDialog = MessageWithAttachmentDialog; diff --git a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/proactive/continuationParameters.js b/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/proactive/continuationParameters.js deleted file mode 100644 index 12b131ef36..0000000000 --- a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/proactive/continuationParameters.js +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -/** - * Stores the information needed to resume a conversation when a proactive message arrives. - */ -class ContinuationParameters { - /** - * @param {string} claimsIdentity - * @param {string} oAuthScope - * @param {import('botbuilder').ConversationReference} conversationReference - */ - constructor (claimsIdentity, oAuthScope, conversationReference) { - this.claimsIdentity = claimsIdentity; - this.oAuthScope = oAuthScope; - this.conversationReference = conversationReference; - } -} - -module.exports.ContinuationParameters = ContinuationParameters; diff --git a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/proactive/waitForProactiveDialog.js b/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/proactive/waitForProactiveDialog.js deleted file mode 100644 index 623bcd0db6..0000000000 --- a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/proactive/waitForProactiveDialog.js +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const { TurnContext, MessageFactory, ActivityTypes, ActivityEventNames, EndOfConversationCodes } = require('botbuilder'); -const { Dialog, DialogTurnStatus } = require('botbuilder-dialogs'); - -class WaitForProactiveDialog extends Dialog { - /** - * @param {string} dialogId - * @param {string} serverUrl - * @param {Object} continuationParametersStore - */ - constructor (dialogId, serverUrl, continuationParametersStore) { - super(dialogId); - this.serverUrl = serverUrl; - this.continuationParametersStore = continuationParametersStore; - } - - /** - * Message to send to users when the bot receives a Conversation Update event - * @param {string} url - * @param {string} id - */ - notifyMessage (url, id) { - return `Navigate to ${url}/api/notify?user=${id} to proactively message the user.`; - } - - /** - * @param {import('botbuilder-dialogs').DialogContext} dc - */ - async beginDialog (dc) { - // Store a reference to the conversation. - this.addOrUpdateContinuationParameters(dc.context); - - // Render message with continuation link. - await dc.context.sendActivity(MessageFactory.text(this.notifyMessage(this.serverUrl, dc.context.activity.from.id))); - return Dialog.EndOfTurn; - } - - /** - * @param {import('botbuilder-dialogs').DialogContext} dc - */ - async continueDialog (dc) { - const { activity } = dc.context; - if (activity.type === ActivityTypes.Event && activity.name === ActivityEventNames.ContinueConversation) { - // We continued the conversation, forget the proactive reference. - this.continuationParametersStore[activity.from.id] = undefined; - - // The continue conversation activity comes from the ProactiveController when the notification is received - await dc.context.sendActivity('We received a proactive message, ending the dialog'); - - // End the dialog so the host gets an EoC - await dc.context.sendActivity({ - type: ActivityTypes.EndOfConversation, - code: EndOfConversationCodes.CompletedSuccessfully - }); - return { status: DialogTurnStatus.complete }; - } - - // Keep waiting for a call to the ProactiveController. - await dc.context.sendActivity(`We are waiting for a proactive message. ${this.notifyMessage(this.serverUrl, activity.from.id)}`); - - return Dialog.EndOfTurn; - } - - /** - * Helper to extract and store parameters we need to continue a conversation from a proactive message. - * @param {import('botbuilder').TurnContext} turnContext - */ - addOrUpdateContinuationParameters (turnContext) { - this.continuationParametersStore[turnContext.activity.from.id] = { - claimsIdentity: turnContext.turnState.get(turnContext.adapter.BotIdentityKey), - conversationReference: TurnContext.getConversationReference(turnContext.activity), - oAuthScope: turnContext.turnState.get(turnContext.adapter.OAuthScopeKey) - }; - } -} - -module.exports.WaitForProactiveDialog = WaitForProactiveDialog; diff --git a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/sso/ssoSkillDialog.js b/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/sso/ssoSkillDialog.js deleted file mode 100644 index 86a357e20a..0000000000 --- a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/sso/ssoSkillDialog.js +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const { MessageFactory, InputHints } = require('botbuilder'); -const { ComponentDialog, ChoicePrompt, ChoiceFactory, DialogTurnStatus, WaterfallDialog } = require('botbuilder-dialogs'); -const { SsoSkillSignInDialog } = require('./SsoSkillSignInDialog'); - -const ACTION_PROMPT = 'ActionStepPrompt'; -const WATERFALL_DIALOG = 'WaterfallDialog'; -const SSO_SKILL_DIALOG = 'SsoSkillDialog'; - -class SsoSkillDialog extends ComponentDialog { - /** - * @param {string} dialogId - * @param {Object} configuration - */ - constructor (dialogId, configuration) { - super(dialogId); - - this.connectionName = configuration.SsoConnectionName; - - this.addDialog(new SsoSkillSignInDialog(SSO_SKILL_DIALOG, this.connectionName)) - .addDialog(new ChoicePrompt(ACTION_PROMPT)) - .addDialog(new WaterfallDialog(WATERFALL_DIALOG, [ - this.promptActionStep.bind(this), - this.handleActionStep.bind(this), - this.promptFinalStep.bind(this) - ])); - - this.initialDialogId = WATERFALL_DIALOG; - } - - /** - * @param {import('botbuilder-dialogs').WaterfallStepContext} stepContext - */ - async promptActionStep (stepContext) { - const messageText = 'What SSO action would you like to perform on the skill?'; - const repromptMessageText = 'That was not a valid choice, please select a valid choice.'; - - return stepContext.prompt(ACTION_PROMPT, { - prompt: MessageFactory.text(messageText, messageText, InputHints.ExpectingInput), - retryPrompt: MessageFactory.text(repromptMessageText, repromptMessageText, InputHints.ExpectingInput), - choices: ChoiceFactory.toChoices(await this.getPromptChoices(stepContext)) - }); - } - - /** - * @param {import('botbuilder-dialogs').WaterfallStepContext} stepContext - */ - async getPromptChoices (stepContext) { - const choices = []; - const token = await stepContext.context.adapter.getUserToken(stepContext.context, this.connectionName); - - if (!token) { - choices.push('Login'); - } else { - choices.push('Logout'); - choices.push('Show token'); - } - - choices.push('End'); - - return choices; - } - - /** - * @param {import('botbuilder-dialogs').WaterfallStepContext} stepContext - */ - async handleActionStep (stepContext) { - const action = stepContext.result.value.toLowerCase(); - - switch (action) { - case 'login': - return stepContext.beginDialog(SSO_SKILL_DIALOG); - - case 'logout': - await stepContext.context.adapter.signOutUser(stepContext.context, this.connectionName); - await stepContext.context.sendActivity('You have been signed out.'); - return stepContext.next(); - - case 'show token': { - const token = await stepContext.context.adapter.getUserToken(stepContext.context, this.connectionName); - - if (!token) { - await stepContext.context.sendActivity('User has no cached token.'); - } else { - await stepContext.context.sendActivity(`Here is your current SSO token: ${token.token}`); - } - - return stepContext.next(); - } - - case 'end': - return { status: DialogTurnStatus.complete }; - - default: - // This should never be hit since the previous prompt validates the choice. - throw new Error(`Unrecognized action: ${action}`); - } - } - - /** - * @param {import('botbuilder-dialogs').WaterfallStepContext} stepContext - */ - async promptFinalStep (stepContext) { - // Restart the dialog (we will exit when the user says end). - return stepContext.replaceDialog(this.initialDialogId); - } -} - -module.exports.SsoSkillDialog = SsoSkillDialog; diff --git a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/sso/ssoSkillSignInDialog.js b/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/sso/ssoSkillSignInDialog.js deleted file mode 100644 index f6312ec17f..0000000000 --- a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/sso/ssoSkillSignInDialog.js +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const { ComponentDialog, OAuthPrompt, WaterfallDialog } = require('botbuilder-dialogs'); - -const OAUTH_PROMPT = 'OAuthPrompt'; -const WATERFALL_DIALOG = 'WaterfallDialog'; - -class SsoSkillSignInDialog extends ComponentDialog { - /** - * @param {string} dialogId - * @param {string} connectionName - */ - constructor (dialogId, connectionName) { - super(dialogId); - - this.addDialog(new OAuthPrompt(OAUTH_PROMPT, { - connectionName: connectionName, - text: 'Sign in to the Skill using AAD', - title: 'Sign In' - })); - - this.addDialog(new WaterfallDialog(WATERFALL_DIALOG, [ - this.signInStep.bind(this), - this.displayToken.bind(this) - ])); - - this.initialDialogId = WATERFALL_DIALOG; - } - - /** - * @param {import('botbuilder-dialogs').WaterfallStepContext} stepContext - */ - async signInStep (stepContext) { - return stepContext.beginDialog(OAUTH_PROMPT); - } - - /** - * @param {import('botbuilder-dialogs').WaterfallStepContext} stepContext - */ - async displayToken (stepContext) { - const { result } = stepContext; - if (!result || !result.token) { - await stepContext.context.sendActivity('No token was provided for the skill.'); - } else { - await stepContext.context.sendActivity(`Here is your token for the skill: ${result.token}`); - } - - return stepContext.endDialog(); - } -} - -module.exports.SsoSkillSignInDialog = SsoSkillSignInDialog; diff --git a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/update/updateDialog.js b/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/update/updateDialog.js deleted file mode 100644 index fc999bf644..0000000000 --- a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/dialogs/update/updateDialog.js +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const { MessageFactory } = require('botbuilder'); -const { ConfirmPrompt, ComponentDialog, WaterfallDialog, DialogTurnStatus } = require('botbuilder-dialogs'); -const { Channels } = require('botbuilder-core'); - -const WATERFALL_DIALOG = 'WaterfallDialog'; -const CONFIRM_PROMPT = 'ConfirmPrompt'; -const UPDATE_SUPPORTED = new Set([Channels.Msteams, Channels.Slack, Channels.Telegram]); - -class UpdateDialog extends ComponentDialog { - /** - * @param {string} dialogId - */ - constructor (dialogId) { - super(dialogId); - - this.updateTracker = {}; - - this.addDialog(new ConfirmPrompt(CONFIRM_PROMPT)); - this.addDialog(new WaterfallDialog(WATERFALL_DIALOG, [ - this.handleUpdateDialog.bind(this), - this.finalStepAsync.bind(this) - ])); - - this.initialDialogId = WATERFALL_DIALOG; - } - - /** - * @param {import('botbuilder-dialogs').WaterfallStepContext} stepContext - */ - async handleUpdateDialog (stepContext) { - const channel = stepContext.context.activity.channelId; - - if (UpdateDialog.isUpdateSupported(channel)) { - const conversationId = stepContext.context.activity.conversation.id; - - if (conversationId in this.updateTracker) { - const tuple = this.updateTracker[conversationId]; - const activity = MessageFactory.text(`This message has been updated ${tuple[1]} time(s).`); - activity.id = tuple[0]; - tuple[1]++; - this.updateTracker[conversationId] = tuple; - await stepContext.context.updateActivity(activity); - } else { - const id = await stepContext.context.sendActivity(MessageFactory.text('Here is the original activity')); - this.updateTracker[conversationId] = [id.id, 1]; - } - } else { - await stepContext.context.sendActivity(MessageFactory.text(`Update is not supported in the ${channel} channel.`)); - return { status: DialogTurnStatus.complete }; - } - - // Ask if we want to update the activity again. - const messageText = 'Do you want to update the activity again?'; - const repromptMessageText = 'Please select a valid answer'; - const options = { - prompt: MessageFactory.text(messageText, messageText), - retryPrompt: MessageFactory.text(repromptMessageText, repromptMessageText) - }; - - // Ask the user to enter a card choice. - return stepContext.prompt(CONFIRM_PROMPT, options); - } - - /** - * @param {import('botbuilder-dialogs').WaterfallStepContext} stepContext - */ - async finalStepAsync (stepContext) { - const tryAnother = stepContext.result; - - if (tryAnother) { - return stepContext.replaceDialog(this.initialDialogId); - } - - this.updateTracker = {}; - return { status: DialogTurnStatus.complete }; - } - - /** - * @param {string} channel - */ - static isUpdateSupported (channel) { - return UPDATE_SUPPORTED.has(channel); - } -} - -module.exports.UpdateDialog = UpdateDialog; diff --git a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/images/architecture-resize.png b/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/images/architecture-resize.png deleted file mode 100644 index e65f0f7332..0000000000 Binary files a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/images/architecture-resize.png and /dev/null differ diff --git a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/index.js b/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/index.js deleted file mode 100644 index 8309e60139..0000000000 --- a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/index.js +++ /dev/null @@ -1,216 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const dotenv = require('dotenv'); -const http = require('http'); -const https = require('https'); -const path = require('path'); -const restify = require('restify'); - -// Import required bot configuration. -const ENV_FILE = path.join(__dirname, '.env'); -dotenv.config({ path: ENV_FILE }); - -// Import required bot services. -// See https://aka.ms/bot-services to learn more about the different parts of a bot. -const { ActivityTypes, BotFrameworkAdapter, InputHints, MemoryStorage, ConversationState, SkillHttpClient, SkillHandler, ChannelServiceRoutes, TurnContext, MessageFactory, SkillConversationIdFactory } = require('botbuilder'); -const { AuthenticationConfiguration, SimpleCredentialProvider } = require('botframework-connector'); - -const { SkillBot } = require('./bots/skillBot'); -const { ActivityRouterDialog } = require('./dialogs/activityRouterDialog'); -const { allowedCallersClaimsValidator } = require('./authentication/allowedCallersClaimsValidator'); -const { SsoSaveStateMiddleware } = require('./middleware/ssoSaveStateMiddleware'); - -// Create HTTP server -const server = restify.createServer({ maxParamLength: 1000 }); -server.use(restify.plugins.queryParser()); - -server.listen(process.env.port || process.env.PORT || 36420, () => { - console.log(`\n${server.name} listening to ${server.url}`); - console.log('\nGet Bot Framework Emulator: https://aka.ms/botframework-emulator'); - console.log('\nTo talk to your bot, open the emulator select "Open Bot"'); -}); - -const maxTotalSockets = (preallocatedSnatPorts, procCount = 1, weight = 0.5, overcommit = 1.1) => - Math.min( - Math.floor((preallocatedSnatPorts / procCount) * weight * overcommit), - preallocatedSnatPorts - ); - -const authConfig = new AuthenticationConfiguration([], allowedCallersClaimsValidator); - -// Create adapter. -// See https://aka.ms/about-bot-adapter to learn more about how bots work. -const adapter = new BotFrameworkAdapter({ - appId: process.env.MicrosoftAppId, - appPassword: process.env.MicrosoftAppPassword, - authConfig: authConfig, - clientOptions: { - agentSettings: { - http: new http.Agent({ - keepAlive: true, - maxTotalSockets: maxTotalSockets(1024, 4, 0.3) - }), - https: new https.Agent({ - keepAlive: true, - maxTotalSockets: maxTotalSockets(1024, 4, 0.7) - }) - } - } -}); - -// Catch-all for errors. -adapter.onTurnError = async (context, error) => { - // This check writes out errors to console log .vs. app insights. - // NOTE: In production environment, you should consider logging this to Azure application insights. - console.error(`\n [onTurnError] unhandled error: ${error}`); - - try { - const { message, stack } = error; - - // Send a message to the user. - let errorMessageText = 'The skill encountered an error or bug.'; - let errorMessage = MessageFactory.text(`${errorMessageText}\r\n${message}\r\n${stack}`, errorMessageText, InputHints.IgnoringInput); - errorMessage.value = { message, stack }; - await context.sendActivity(errorMessage); - - errorMessageText = 'To continue to run this bot, please fix the bot source code.'; - errorMessage = MessageFactory.text(errorMessageText, errorMessageText, InputHints.ExpectingInput); - await context.sendActivity(errorMessage); - - // Send a trace activity, which will be displayed in Bot Framework Emulator - await context.sendTraceActivity( - 'OnTurnError Trace', - `${error}`, - 'https://www.botframework.com/schemas/error', - 'TurnError' - ); - - // Send and EndOfConversation activity to the skill caller with the error to end the conversation - // and let the caller decide what to do. - await context.sendActivity({ - type: ActivityTypes.EndOfConversation, - code: 'SkillError', - text: error - }); - } catch (err) { - console.error(`\n [onTurnError] Exception caught in onTurnError : ${err}`); - } -}; - -const continuationParametersStore = {}; - -// Define the state store for your bot. -// See https://aka.ms/about-bot-state to learn more about using MemoryStorage. -// A bot requires a state storage system to persist the dialog and user state between messages. -const memoryStorage = new MemoryStorage(); - -// Create conversation and user state with in-memory storage provider. -const conversationState = new ConversationState(memoryStorage); - -adapter.use(new SsoSaveStateMiddleware(conversationState)); - -// Create the conversationIdFactory -const conversationIdFactory = new SkillConversationIdFactory(memoryStorage); - -// Create the credential provider; -const credentialProvider = new SimpleCredentialProvider(process.env.MicrosoftAppId, process.env.MicrosoftAppPassword); - -// Create the skill client -const skillClient = new SkillHttpClient(credentialProvider, conversationIdFactory); - -// Create the main dialog. -const dialog = new ActivityRouterDialog(server.url, conversationState, conversationIdFactory, skillClient, continuationParametersStore); - -// Create the bot that will handle incoming messages. -const bot = new SkillBot(conversationState, dialog, server.url); - -// Expose the manifest -server.get('/manifests/*', restify.plugins.serveStatic({ directory: './manifests', appendRequestPath: false })); - -// Expose images -server.get('/images/*', restify.plugins.serveStatic({ directory: './images', appendRequestPath: false })); - -// Listen for incoming requests. -server.post('/api/messages', (req, res) => { - adapter.processActivity(req, res, async (context) => { - // Route to main dialog. - await bot.run(context); - }); -}); - -// Create and initialize the skill classes. - -// Workaround for communicating back to the Host without throwing Unauthorized error due to the creation of a new Connector Client in the Adapter when the continueConvesation happens. - -// Uncomment this when resolved. -// const handler = new SkillHandler(adapter, bot, conversationIdFactory, credentialProvider, authConfig); -// const skillEndpoint = new ChannelServiceRoutes(handler); -// skillEndpoint.register(server, '/api/skills'); - -// Remove this when resolved -const handler = new SkillHandler(adapter, bot, conversationIdFactory, credentialProvider, authConfig); -server.post('/api/skills/v3/conversations/:conversationId/activities/:activityId', async (req, res) => { - try { - const authHeader = req.headers.authorization || req.headers.Authorization || ''; - const activity = await ChannelServiceRoutes.readActivity(req); - const ref = await handler.conversationIdFactory.getSkillConversationReference(req.params.conversationId); - const claimsIdentity = await handler.authenticate(authHeader); - - const response = await new Promise(resolve => { - return adapter.continueConversation(ref.conversationReference, ref.oAuthScope, async (context) => { - context.turnState.set(adapter.BotIdentityKey, claimsIdentity); - context.turnState.set(adapter.SkillConversationReferenceKey, ref); - - const newActivity = TurnContext.applyConversationReference(activity, ref.conversationReference); - - if (newActivity.type === ActivityTypes.EndOfConversation) { - await handler.conversationIdFactory.deleteConversationReference(req.params.conversationId); - SkillHandler.applyEoCToTurnContextActivity(context, newActivity); - resolve(await bot.run(context)); - } - - resolve(await context.sendActivity(newActivity)); - }); - }); - - res.status(200); - res.send(response); - res.end(); - } catch (error) { - ChannelServiceRoutes.handleError(error, res); - } -}); - -// Listen for incoming requests. -server.get('/api/music', restify.plugins.serveStatic({ directory: 'dialogs/cards/files', file: 'music.mp3' })); - -// Listen for incoming notifications and send proactive messages to users. -server.get('/api/notify', async (req, res) => { - let error; - const { user } = req.query; - - const continuationParameters = continuationParametersStore[user]; - - if (!continuationParameters) { - res.setHeader('Content-Type', 'text/html'); - res.writeHead(200); - res.write(`

No messages sent


There are no conversations registered to receive proactive messages for ${user}.`); - res.end(); - return; - } - - try { - adapter.continueConversation(continuationParameters.conversationReference, continuationParameters.oAuthScope, async context => { - await context.sendActivity(`Got proactive message for user: ${user}`); - await bot.run(context); - }); - } catch (err) { - error = err; - } - - res.setHeader('Content-Type', 'text/html'); - res.writeHead(200); - res.write(`

Proactive messages have been sent


Timestamp: ${new Date().toISOString()}
Exception: ${error || ''}`); - res.end(); -}); diff --git a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/manifests/waterfallskillbot-manifest-1.0.json b/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/manifests/waterfallskillbot-manifest-1.0.json deleted file mode 100644 index 0e2c4109f1..0000000000 --- a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/manifests/waterfallskillbot-manifest-1.0.json +++ /dev/null @@ -1,105 +0,0 @@ -{ - "$schema": "https://schemas.botframework.com/schemas/skills/v2.1/skill-manifest.json", - "$id": "WaterfallSkillBotJS", - "name": "WaterfallSkillBotJS", - "version": "1.0", - "description": "This is a skill definition for multiple activity types (implemented using waterfall dialogs).", - "publisherName": "Microsoft", - "privacyUrl": "https://microsoft.com/privacy", - "copyright": "Copyright (c) Microsoft Corporation. All rights reserved.", - "license": "https://github.com/microsoft/BotFramework-FunctionalTests/blob/main/LICENSE", - "tags": [ - "travel", - "weather", - "luis" - ], - "endpoints": [ - { - "name": "default", - "protocol": "BotFrameworkV3", - "description": "Localhost endpoint for the skill (on port 35420)", - "endpointUrl": "http://localhost:35420/api/messages", - "msAppId": "00000000-0000-0000-0000-000000000000" - } - ], - "activities": { - "bookFlight": { - "description": "Books a flight (multi turn).", - "type": "event", - "name": "BookFlight", - "value": { - "$ref": "#/definitions/bookingInfo" - }, - "resultValue": { - "$ref": "#/definitions/bookingInfo" - } - }, - "getWeather": { - "description": "Retrieves and returns the weather for the user's location.", - "type": "event", - "name": "GetWeather", - "value": { - "$ref": "#/definitions/location" - }, - "resultValue": { - "$ref": "#/definitions/weatherReport" - } - }, - "passthroughMessage": { - "type": "message", - "description": "Receives the user's utterance and attempts to resolve it using the skill's LUIS models.", - "value": { - "type": "object" - } - } - }, - "definitions": { - "bookingInfo": { - "type": "object", - "required": [ - "origin" - ], - "properties": { - "origin": { - "type": "string", - "description": "This is the origin city for the flight." - }, - "destination": { - "type": "string", - "description": "This is the destination city for the flight." - }, - "travelDate": { - "type": "string", - "description": "The date for the flight in YYYY-MM-DD format." - } - } - }, - "weatherReport": { - "type": "array", - "description": "Array of forecasts for the next week.", - "items": [ - { - "type": "string" - } - ] - }, - "location": { - "type": "object", - "description": "Location metadata.", - "properties": { - "latitude": { - "type": "number", - "title": "Latitude" - }, - "longitude": { - "type": "number", - "title": "Longitude" - }, - "postalCode": { - "type": "string", - "title": "Postal code" - } - } - } - } -} diff --git a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/middleware/ssoSaveStateMiddleware.js b/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/middleware/ssoSaveStateMiddleware.js deleted file mode 100644 index e0c1806c4b..0000000000 --- a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/middleware/ssoSaveStateMiddleware.js +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -const { CardFactory } = require('botbuilder'); - -class SsoSaveStateMiddleware { - /** - * @param {import('botbuilder').ConversationState} conversationState - */ - constructor (conversationState) { - this.conversationState = conversationState; - } - - /** - * @param {import('botbuilder').TurnContext} turnContext - */ - async onTurn (turnContext, next) { - // Register outgoing handler. - turnContext.onSendActivities(this.outgoingHandler.bind(this)); - - // Continue processing messages. - await next(); - } - - /** - * @param {import('botbuilder').TurnContext} turnContext - * @param {Partial[]} activities - * @param {Function} next - */ - async outgoingHandler (turnContext, activities, next) { - for (const activity of activities) { - if (!!activity.attachments && activity.attachments.some(attachment => attachment.contentType === CardFactory.contentTypes.oauthCard)) { - this.conversationState.saveChanges(turnContext); - } - } - - return next(); - } -} - -module.exports.SsoSaveStateMiddleware = SsoSaveStateMiddleware; diff --git a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/package.json b/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/package.json deleted file mode 100644 index 2ebcff1533..0000000000 --- a/tests/functional/Bots/JavaScript/Skills/CodeFirst/WaterfallSkillBot/package.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "waterfall-skill-bot", - "version": "1.0.0", - "description": "A simple waterfall skill bot for testing skills", - "author": "Microsoft", - "license": "MIT", - "main": "index.js", - "scripts": { - "start": "node ./index.js", - "watch": "nodemon ./index.js", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "repository": { - "type": "git", - "url": "https://github.com" - }, - "dependencies": { - "botbuilder": "~4.14.0", - "botbuilder-dialogs": "~4.14.0", - "dotenv": "^8.2.0", - "node-fetch": "^2.6.1", - "restify": "~8.5.1" - }, - "devDependencies": { - "nodemon": "~2.0.4" - } -} diff --git a/tests/functional/CODE_OF_CONDUCT.md b/tests/functional/CODE_OF_CONDUCT.md deleted file mode 100644 index f9ba8cf65f..0000000000 --- a/tests/functional/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,9 +0,0 @@ -# Microsoft Open Source Code of Conduct - -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). - -Resources: - -- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) -- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) -- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns diff --git a/tests/functional/Docs/README.md b/tests/functional/Docs/README.md deleted file mode 100644 index 89cbf102ae..0000000000 --- a/tests/functional/Docs/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# Index - -## Main documents - -- [Pipelines](./pipelines.md) - -## Guides - -- [Add a Service Connection to an Azure Resource Manager](./addARMServiceConnection.md) -- [Create WebApp Deployment credentials](./createWebAppDeploymentCredentials.md) -- [Get the Service Principal's Object ID](./getServicePrincipalObjectID.md) -- [Setup Pipelines](./setupPipelines.md) diff --git a/tests/functional/Docs/addARMServiceConnection.md b/tests/functional/Docs/addARMServiceConnection.md deleted file mode 100644 index 5218a7937b..0000000000 --- a/tests/functional/Docs/addARMServiceConnection.md +++ /dev/null @@ -1,24 +0,0 @@ -# How to add a Service Connection to an Azure Resource Manager - -The following steps will guide you on how to add a Service Connection to an Azure Resource Manager in your DevOps organization. This is a required step to connect your Azure Subscription to the pipelines and deploy resources to Azure. - -**Note: The name you use for the resource will be used as `AzureSubscription` in the pipeline's variables.** - -Go to [Service connections](https://docs.microsoft.com/en-us/azure/devops/pipelines/library/service-endpoints?view=azure-devops&tabs=yaml) for more information. - -## Requirements - -- An [Azure DevOps Organization](https://docs.microsoft.com/en-us/azure/devops/organizations/accounts/create-organization?view=azure-devops). -- Access to an active [Azure Resource Manager](https://docs.microsoft.com/en-us/azure/azure-resource-manager/management/overview). - -## Steps - -- From your Azure DevOps project, go to `Project settings -> Service Connections -> New Service connection`. - - ![addARMServiceConnection1](./media/addARMServiceConnection1.png) - -- Select `Azure Resource Manager -> Service principal (manual)` and fill out the form. Check the `Grant access permission to all pipelines` option. The name you use for the Subscription Name field will be your `AzureSubscription` variable in the pipelines. - - ![addARMServiceConnection2](./media/addARMServiceConnection2.png) - -- Click on `Verify and save` when done. diff --git a/tests/functional/Docs/createWebAppDeploymentCredentials.md b/tests/functional/Docs/createWebAppDeploymentCredentials.md deleted file mode 100644 index 11075f1c2d..0000000000 --- a/tests/functional/Docs/createWebAppDeploymentCredentials.md +++ /dev/null @@ -1,18 +0,0 @@ -# How to create WebApp Deployment credentials - -The following steps will guide you on how to create WebApp Deployment credentials, required for Linux-based deployments. - -Go to [Configure deployment credentials for Azure App Service](https://docs.microsoft.com/en-us/azure/app-service/deploy-configure-credentials#in-the-cloud-shell) for more information. - -## Requirements - -- An active [Azure Subscription](https://azure.microsoft.com/en-us/free/). -- [az cli](https://docs.microsoft.com/en-us/cli/azure/) installed. - -## Steps - -- From a terminal where [az cli](https://docs.microsoft.com/en-us/cli/azure/) is accessible, run `az login` (in case you're not logged in), then execute the following command: - - `az webapp deployment user set --user-name [name] --password [password]` - - **Note: Special characters at the beginning of the user name will work when creating the credentials, but break later on. We advise to avoid special characters for these credentials altogether.** diff --git a/tests/functional/Docs/getServicePrincipalObjectID.md b/tests/functional/Docs/getServicePrincipalObjectID.md deleted file mode 100644 index d7173e142d..0000000000 --- a/tests/functional/Docs/getServicePrincipalObjectID.md +++ /dev/null @@ -1,14 +0,0 @@ -# Hot to get the Service Principal's Object ID - -The following steps will guide you on how to get the Service Principal's Object ID. - -## Requirements - -- Access to a [Service Principal](https://docs.microsoft.com/en-us/azure/active-directory/develop/app-objects-and-service-principals). - -## Steps - -- Go to `Azure subscription -> Access control -> Role assignments` and select the service principal used to configure the service connection. -- Copy the value of the `Object ID`. - - ![getServicePrincipalObjectID](./media/getServicePrincipalObjectID.png) diff --git a/tests/functional/Docs/media/CreateAppRegistrations.ps1 b/tests/functional/Docs/media/CreateAppRegistrations.ps1 deleted file mode 100644 index 93ce20e028..0000000000 --- a/tests/functional/Docs/media/CreateAppRegistrations.ps1 +++ /dev/null @@ -1,39 +0,0 @@ -Write-Host "You will be prompted to login into your Azure account to create App Registrations" -az login - -$defaultPrefix = "generic-app-registration" -$defaultAmount = 14 -$appRegistrations = @() -do { - $prefix = Read-Host -Prompt "App Registrations prefix ('$defaultPrefix' by default)" - if($prefix -eq ""){ - $prefix = $defaultPrefix - } - $amount = Read-Host -Prompt "Amount to create ($defaultAmount by default)" - if($amount -eq ""){ - $amount = $defaultAmount - } - - For ($index = 1; $index -le $amount; $index++) { - $name = "$prefix-$index" - Write-Host "Creating $name..." - $id = (az ad app create --display-name $name --available-to-other-tenants | ConvertFrom-Json).appId - $password = (az ad app credential reset --id $id | ConvertFrom-Json).password - $appRegistration = @{ - name = $name - id = $id - password = $password - } - Write-Host ($appRegistration | ConvertTo-Json) "`n" - $appRegistrations += $appRegistration - } -} while ((Read-Host -Prompt 'Batch completed. Create another batch? y/n').ToUpper() -eq "Y") - -if((Read-Host -Prompt "Output App Registrations to a file? y/n").ToUpper() -eq "Y"){ - $defaultPath = ".\appRegistrations.json" - $path = Read-Host -Prompt "Where? ('$defaultPath' by default)" - if($path -eq ""){ - $path = $defaultPath - } - $appRegistrations | ConvertTo-Json | Out-File $path -} diff --git a/tests/functional/Docs/media/addARMServiceConnection1.png b/tests/functional/Docs/media/addARMServiceConnection1.png deleted file mode 100644 index 9265312902..0000000000 Binary files a/tests/functional/Docs/media/addARMServiceConnection1.png and /dev/null differ diff --git a/tests/functional/Docs/media/addARMServiceConnection2.png b/tests/functional/Docs/media/addARMServiceConnection2.png deleted file mode 100644 index 99b829d2ec..0000000000 Binary files a/tests/functional/Docs/media/addARMServiceConnection2.png and /dev/null differ diff --git a/tests/functional/Docs/media/getServicePrincipalObjectID.png b/tests/functional/Docs/media/getServicePrincipalObjectID.png deleted file mode 100644 index d4ae01ab8f..0000000000 Binary files a/tests/functional/Docs/media/getServicePrincipalObjectID.png and /dev/null differ diff --git a/tests/functional/Docs/media/setupAppRegistrations1.png b/tests/functional/Docs/media/setupAppRegistrations1.png deleted file mode 100644 index 9ae7045552..0000000000 Binary files a/tests/functional/Docs/media/setupAppRegistrations1.png and /dev/null differ diff --git a/tests/functional/Docs/media/setupAppRegistrations2.png b/tests/functional/Docs/media/setupAppRegistrations2.png deleted file mode 100644 index b6d15cad17..0000000000 Binary files a/tests/functional/Docs/media/setupAppRegistrations2.png and /dev/null differ diff --git a/tests/functional/Docs/media/setupAppRegistrations3.png b/tests/functional/Docs/media/setupAppRegistrations3.png deleted file mode 100644 index be9ed13301..0000000000 Binary files a/tests/functional/Docs/media/setupAppRegistrations3.png and /dev/null differ diff --git a/tests/functional/Docs/media/setupAppRegistrations4.png b/tests/functional/Docs/media/setupAppRegistrations4.png deleted file mode 100644 index db8a2951a9..0000000000 Binary files a/tests/functional/Docs/media/setupAppRegistrations4.png and /dev/null differ diff --git a/tests/functional/Docs/media/setupAppRegistrations5.png b/tests/functional/Docs/media/setupAppRegistrations5.png deleted file mode 100644 index f5bc4dfb71..0000000000 Binary files a/tests/functional/Docs/media/setupAppRegistrations5.png and /dev/null differ diff --git a/tests/functional/Docs/media/setupPipelines1.png b/tests/functional/Docs/media/setupPipelines1.png deleted file mode 100644 index 8d991855e2..0000000000 Binary files a/tests/functional/Docs/media/setupPipelines1.png and /dev/null differ diff --git a/tests/functional/Docs/media/setupPipelines2.png b/tests/functional/Docs/media/setupPipelines2.png deleted file mode 100644 index f8465e9783..0000000000 Binary files a/tests/functional/Docs/media/setupPipelines2.png and /dev/null differ diff --git a/tests/functional/Docs/media/setupPipelines3.png b/tests/functional/Docs/media/setupPipelines3.png deleted file mode 100644 index 3c40597486..0000000000 Binary files a/tests/functional/Docs/media/setupPipelines3.png and /dev/null differ diff --git a/tests/functional/Docs/media/setupPipelines4.png b/tests/functional/Docs/media/setupPipelines4.png deleted file mode 100644 index 1c17ed1b2c..0000000000 Binary files a/tests/functional/Docs/media/setupPipelines4.png and /dev/null differ diff --git a/tests/functional/Docs/media/setupPipelines5.png b/tests/functional/Docs/media/setupPipelines5.png deleted file mode 100644 index cea9352e99..0000000000 Binary files a/tests/functional/Docs/media/setupPipelines5.png and /dev/null differ diff --git a/tests/functional/Docs/media/setupPipelines6.png b/tests/functional/Docs/media/setupPipelines6.png deleted file mode 100644 index 07f561587d..0000000000 Binary files a/tests/functional/Docs/media/setupPipelines6.png and /dev/null differ diff --git a/tests/functional/Docs/pipelines.md b/tests/functional/Docs/pipelines.md deleted file mode 100644 index c5f2dbd3bd..0000000000 --- a/tests/functional/Docs/pipelines.md +++ /dev/null @@ -1,129 +0,0 @@ -# Pipelines - -## 01 - Create Shared Resources Pipeline - -- **Description**: Creates all the long-term resources required. -- **Schedule**: Quarterly or on demand. -- **YAML**: [build\yaml\sharedResources\createSharedResources.yml](../build/yaml/sharedResources/createSharedResources.yml) - -| Variable Name | Source | Description | -| - | - | - | -| **AzureSubscription** | Azure DevOps | Name of the Azure Resource Manager Service Connection configured in the DevOps organization. Click [here](./addARMServiceConnection.md) to see how to set it up. | -| **KeyVaultObjectId** | Azure | Suscription's Object Id to create the keyvault to store App Registrations in Azure. Click [here](./getServicePrincipalObjectID.md) to see how to get it. | -| **AppServicePlanPricingTier** | User | (optional) Pricing Tier for App Service Plans. **Default value is F1.** | -| **ResourceGroupName** | User | (optional) Name for the resource group that will contain the shared resources. | -| **ResourceSuffix** | User | (optional) Suffix to add to the resources' name to avoid collisions. | - -## 02 - Deploy Bot Resources Pipeline - -- **Description:** Creates the test bot resources to be used in the functional tests, separated in a Resource Group for each language (DotNet, JS, and Python) -- **Schedule**: Nightly or on-demand. -- **YAML**: [build\yaml\deployBotResources\deployBotResources.yml](../build/yaml/deployBotResources/deployBotResources.yml) - -| Variable Name | Source | Description | -| - | - | - | -| **AzureSubscription** | Azure DevOps | Name of the Azure Resource Manager Service Connection configured in the DevOps organization. Click [here](./addARMServiceConnection.md) to see how to set it up. | -| **AppServicePlanGroup** | Create Shared Resources | (optional) Name of the Resource Group where the Windows App Service Plan is located. | -| **AppServicePlanGroupLinux** | Create Shared Resources | (optional) Name of the Resource Group where the Linux App Service Plan is located. | -| **AppServicePlanDotNetName** | Create Shared Resources | (optional) Name of the DotNet App Service Plan. | -| **AppServicePlanJSName** | Create Shared Resources | (optional) Name of the JavaScript App Service Plan. | -| **AppServicePlanPythonName** | Create Shared Resources | (optional) Name of the Python App Service Plan. | -| **BotPricingTier** | User | (optional) Pricing tier for the Web App resources. ***Default value is F0.** | -| **ResourceGroup** | User | (optional) Name of the Resource Group where the bots will be deployed. | -| **ResourceSuffix** | Create Shared Resources | (optional) Suffix to add to the resources' name to avoid collisions. | -| **[BotName](#botnames) + AppId** | [App Registration Portal](https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade) | (optional) App ID to use. If not configured, will be retrieved from the key vault. | -| **[BotName](#botnames) + AppSecret** | [App Registration Portal](https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade) | (optional) App Secret to use. If not configured, will be retrieved from the key vault. | - -The following parameters will be displayed in the run pipeline blade. - -| Parameter Name | Source | Description | -| - | - | - | -| **[Language](#dependency-variables-language) Hosts Registry** | User | (optional) Source from which the Bot Builder dependencies will be downloaded for selected host bots. [**More info**](#dependency-variables-language) | -| **[Language](#dependency-variables-language) Skills Registry** | User | (optional) Source from which the Bot Builder dependencies will be downloaded for selected skill bots. [**More info**](#dependency-variables-language) | -| **[Language](#dependency-variables-language) Skills V3 Registry** | User | (optional) Source from which the Bot Builder dependencies will be downloaded for selected V3 skill bots. [**More info**](#dependency-variables-language) | -| **[Language](#dependency-variables-language) Hosts Version** | User | (optional) Bot Builder dependency version to use for selected host bots. **Possible values are: Latest (default), Stable, or specific version numbers.** | -| **[Language](#dependency-variables-language) Skills Version** | User | (optional) Bot Builder dependency version to use for selected skill bots. **Possible values are: Latest (default), Stable, or specific version numbers.** | -| **[Language](#dependency-variables-language) Skills V3 Version** | User | (optional) Bot Builder dependency version to use for selected V3 skill bots. **Possible values are: Latest (default), Stable, or specific version numbers.** | - -## 03 - Run Test Scenarios Pipeline - -- **Description:** Configures and executes the test scenarios. -- **Schedule**: Nightly (after Deploy Bot Resources) or on demand. -- **YAML**: [build\yaml\testScenarios\runTestScenarios.yml](../build/yaml/testScenarios/runTestScenarios.yml) - -| Variable Name | Source | Description | -| - | - | - | -| **AzureSubscription** | Azure DevOps | Name of the Azure Resource Manager Service Connection configured in the DevOps organization. Click [here](./addARMServiceConnection.md) to see how to set it up. | -| **ResourceGroup** | User | (optional) Name of the Resource Group where the bots are deployed. | -| **ResourceSuffix** | Create Shared Resources | (optional) Suffix to add to the resources' name to avoid collitions. | -| **[BotName](#botnames) + AppId** | [App Registration Portal](https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade) | (optional) App ID to use. If not configured, it will be retrieved from the key vault. | -| **ResourceSuffix** | Create Bot Resources | (optional) Deploy Bot Resources pipeline GUID. | - -## 04 - Cleanup Resources Pipeline - -- **Description:** Removes all resources, including all the shared resources, bots, and app registrations. -- **Schedule**: Quarterly or on demand. -- **YAML**: [build\yaml\cleanupResources\cleanupResources.yml](../build/yaml/cleanupResources/cleanupResources.yml) - -| Variable Name | Source | Description | -| - | - | - | -| **AzureSubscription** | Azure DevOps | Name of the Azure Resource Manager Service Connection configured in the DevOps organization. Click [here](./addARMServiceConnection.md) to see how to set it up. | -| **DeployResourceGroup** | Deploy Bot Resources | (optional) Name of the Resource Group containing the bots. | -| **ResourceSuffix** | Create Shared Resources | (optional) Suffix to add to the resources' name to avoid collitions. | -| **SharedResourceGroup** | Create Shared Resources | (optional) Name of the Resource Group containing the shared resources. | - -### Dependency Variables - -These are the available languages for the dependencies registry and version variables: - -You can choose between one of the following options to select the package's feed. - -- DotNet - - Artifacts (default) - - MyGet (default for V3 skill) - - NuGet -- JS - - MyGet (default) - - Npm -- Python (Not available for SkillsV3) - - Artifacts (default) - - Pypi - - Test.Pypi - -The version parameters support LATEST (default), STABLE, or a specific version. - -Note: Npm and NuGet feeds only support stable versions, fill the corresponding variable with a specific version or set it to `stable`. - -### BotNames - -As of now, these are the bots available. This list will be expanded in the future. - -- DotNet - - Consumers - - BffnSimpleHostBotDotNet - - BffnSimpleHostBotDotNet21 - - BffnSimpleComposerHostBotDotNet - - BffnWaterfallHostBotDotNet - - Skills - - BffnEchoSkillBotDotNet - - BffnEchoSkillBotDotNet21 - - BffnEchoSkillBotDotNetV3 - - BffnEchoComposerSkillBotDotNet - - BffnWaterfallSkillBotDotNet - -- JS - - Consumers - - BffnSimpleHostBotJS - - BffnWaterfallHostBotJS - - Skills - - BffnEchoSkillBotJS - - BffnEchoSkillBotJSV3 - - BffnWaterfallSkillBotJS - -- Python - - Consumers - - BffnSimpleHostBotPython - - BffnWaterfallHostBotPython - - Skills - - BffnEchoSkillBotPython - - BffnWaterfallSkillBotPython diff --git a/tests/functional/Docs/setupAppRegistrations.md b/tests/functional/Docs/setupAppRegistrations.md deleted file mode 100644 index cd4fcee8d8..0000000000 --- a/tests/functional/Docs/setupAppRegistrations.md +++ /dev/null @@ -1,88 +0,0 @@ -# How to setup up App Registrations - -The following steps will guide you on how to manually set up App Registrations. -By default, App Registrations are created and used automatically across the [pipelines](./pipelines.md), but the possibility of setting them up manually is available. - -## Requirements - -- An active [Azure Subscription](https://azure.microsoft.com/en-us/free/). - -## (optional) Skip the automatic creation/deletion of App Registrations at runtime - -The Key Vault and App Registration creation/deletion stages can be skipped at runtime on the [Create Shared Resources](../build/yaml/sharedResources/createSharedResources.yml) and [Cleanup Resources](../build/yaml/cleanupResources/cleanupResources.yml). Running these stages without having assigned the required Service Principal permissions can lead to errors (although these errors won't interrupt the rest of the processes, so this step is optional). -To skip stages, simple click on Stages to run, deselect them, and click Use selected stages before running the pipeline (see picture below). - -![setupAppRegistrations1](media/setupAppRegistrations1.png) - -## Create App Registrations - -App Registration credentials must be created for each bot to be deployed and tested (there are fourteen currently, but the number is expected to increase in the future). -To do this, you could head to [App Registrations](https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade) on Azure and create them one by one, but we created a PowerShell script to create registration batches automatically. The only requirement to run it is to have access to an active Azure account. - -Click [here](./media/CreateAppRegistrations.ps1) to go to the script. - -Executing it will prompt you to log into your account with your default browser, then to define a prefix for the registration names, and finally for the amount of registrations to create. Both have default values (triggered by pressing Enter) to accommodate for the creation of fourteen credentials with generic names. -The creation process will start, displaying each registration's name, ID, and password created while running. - -![setupAppRegistrations2](media/setupAppRegistrations2.png) - -Once all the credentials are created, the script will ask if you'd like to save them to a file for easier access. It will be saved in the same directory as the script in a file named AppRegistrations.json by default. - -![setupAppRegistrations3](media/setupAppRegistrations3.png) - -## Assign credentials in the pipelines - -You'll have to create variables for each bot in the [Deploy Bot Resources](../build/yaml/deployBotResources/deployBotResources.yml) and [Run Test Scenarios](../build/yaml/testScenarios/runTestScenarios.yml) pipelines. -Alternatively, you could set the variables into a Key Vault or library, but we'll focus on the more straight-forward method of setting them up directly into the pipelines as it requires the least amount of permissions possible. -Head to your [Deploy Bot Resources](../build/yaml/deployBotResources/deployBotResources.yml) pipeline overview (click [here](./setupPipelines.md) for instructions on how to set pipelines up), and click on `Edit` (left of the Run pipeline button on the top right corner). -You will be redirected to the *Edit view*, here click on `Variables` (left of the Run button on the top right corner), and the *Variables panel* will pop up. -If you didn't have any variables set up already, it will look like the picture on the left; if you had, it will look like the one on the right. Click on `New variable` or the plus button respectively to start adding variables. - -![setupAppRegistrations4](media/setupAppRegistrations4.png) - -The following list contains all current variable names to be created, each credential pair can be populated with any of the ones created in the [Create App Registrations](#create-app-registrations) step. - -- BffnEchoSkillBotComposerDotNetAppId -- BffnEchoSkillBotComposerDotNetAppSecret -- BffnEchoSkillBotDotNet21AppId -- BffnEchoSkillBotDotNet21AppSecret -- BffnEchoSkillBotDotNetAppId -- BffnEchoSkillBotDotNetAppSecret -- BffnEchoSkillBotDotNetV3AppId -- BffnEchoSkillBotDotNetV3AppSecret -- BffnEchoSkillBotJSAppId -- BffnEchoSkillBotJSAppSecret -- BffnEchoSkillBotJSV3AppId -- BffnEchoSkillBotJSV3AppSecret -- BffnEchoSkillBotPythonAppId -- BffnEchoSkillBotPythonAppSecret -- BffnSimpleHostBotComposerDotNetAppId -- BffnSimpleHostBotComposerDotNetAppSecret -- BffnSimpleHostBotDotNet21AppId -- BffnSimpleHostBotDotNet21AppSecret -- BffnSimpleHostBotDotNetAppId -- BffnSimpleHostBotDotNetAppSecret -- BffnSimpleHostBotJSAppId -- BffnSimpleHostBotJSAppSecret -- BffnSimpleHostBotPythonAppId -- BffnSimpleHostBotPythonAppSecret -- BffnWaterfallHostBotDotNetAppId -- BffnWaterfallHostBotDotNetAppSecret -- BffnWaterfallSkillBotDotNetAppId -- BffnWaterfallSkillBotDotNetAppSecret - -Don't forget to click on SAVE once you've finished setting up every variable. - -![setupAppRegistrations5](media/setupAppRegistrations5.png) - -Lastly, you'll have to create new variables for the [Run Test Scenarios](../build/yaml/testScenarios/runTestScenarios.yml), same steps apply. -This pipeline requires only the App Registration IDs of the skill bots, so the list is shorter. Just be sure to use the same IDs as with the [Deploy Bot Resources](../build/yaml/deployBotResources/deployBotResources.yml) pipeline. - -- BffnEchoSkillBotComposerDotnetAppId -- BffnEchoSkillBotDotNet21AppId -- BffnEchoSkillBotDotNetAppId -- BffnEchoSkillBotDotNetV3AppId -- BffnEchoSkillBotJSAppId -- BffnEchoSkillBotJSV3AppId -- BffnEchoSkillBotPythonAppId -- BffnWaterfallSkillBotDotNetAppId diff --git a/tests/functional/Docs/setupPipelines.md b/tests/functional/Docs/setupPipelines.md deleted file mode 100644 index 2db02d7a0b..0000000000 --- a/tests/functional/Docs/setupPipelines.md +++ /dev/null @@ -1,34 +0,0 @@ -# How to setup Pipelines - -The following steps will guide you through the creation of a pipeline from a YAML file. - -## Requirements - -- An [Azure DevOps Organization](https://docs.microsoft.com/en-us/azure/devops/organizations/accounts/create-organization?view=azure-devops). -- Access to the repository where the pipeline definition YAML file is stored. - -## Steps - -- Go to the pipeline section of your Azure DevOps organization and create a new pipeline using the classic editor. - - ![setupPipelines1](./media/setupPipelines1.png) - -- Configure the repository and branch. In the configuration as code section, click on Apply for the YAML file option. - - ![setupPipelines2](./media/setupPipelines2.png) - -- In the YAML tab, write the pipeline's name and select the YAML file you want to use. - - ![setupPipelines3](./media/setupPipelines3.png) - -- Go to the variables tab to add all the variables that will be used in the pipeline. - - ![setupPipelines4](./media/setupPipelines4.png) - -- In the triggers tab you can configure the triggers and schedules for each pipeline, all pipelines are configured to not trigger by default. - - ![setupPipelines5](./media/setupPipelines5.png) - -- When you are done configuring the pipeline, save the changes. - - ![setupPipelines6](./media/setupPipelines6.png) diff --git a/tests/functional/LICENSE b/tests/functional/LICENSE deleted file mode 100644 index 9e841e7a26..0000000000 --- a/tests/functional/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ - MIT License - - Copyright (c) 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/tests/functional/README.md b/tests/functional/README.md deleted file mode 100644 index 2f12fba0b2..0000000000 --- a/tests/functional/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# Bot Framework Functional Tests - -This repository contains functional tests for the different [Bot Framework SDK](https://github.com/microsoft/botframework-sdk) tools. - -- [Skill functional tests](Tests/SkillFunctionalTests) aims to automate the testing of Host and Skill interactions through several scenarios in all available programming languages. -- [Transcript Test Runner](Libraries/TranscriptTestRunner) aims to simplify complex conversation scenarios against bots, starting from a transcript file with a user-bot recorded conversation. -- [Transcript Converter](Libraries/TranscriptConverter) takes a conversation transcript and converts it into JSON to be consumed by other projects. - -Head to [Docs](./Docs/) directory for more information. - -## Build Status - - | Branch | Description | Deploy Bot Resources Status | Run Test Scenarios Status | - |--------|-------------|-----------------------------|---------------------------| - | Main | 4.14.* Nightly Run | [![Build Status](https://dev.azure.com/FuseLabs/SDK_v4/_apis/build/status/FunctionalTests/02.A.%20Deploy%20skill%20bots%20(daily)?branchName=main)](https://dev.azure.com/FuseLabs/SDK_v4/_build/latest?definitionId=1229&branchName=main) | [![Build Status](https://dev.azure.com/FuseLabs/SDK_v4/_apis/build/status/FunctionalTests/02.B.%20Run%20skill%20test%20scenarios%20(daily)?branchName=main)](https://dev.azure.com/FuseLabs/SDK_v4/_build/latest?definitionId=1237&branchName=main)| - -## Contributing - -This project welcomes contributions and suggestions. Most contributions require you to agree to a -Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit . - -When you submit a pull request, a CLA bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA. - -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 [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. diff --git a/tests/functional/SECURITY.md b/tests/functional/SECURITY.md deleted file mode 100644 index e0dfff56a9..0000000000 --- a/tests/functional/SECURITY.md +++ /dev/null @@ -1,41 +0,0 @@ - - -## 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 [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)) of a security vulnerability, 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://msrc.microsoft.com/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 the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). - -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://www.microsoft.com/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://microsoft.com/msrc/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://www.microsoft.com/en-us/msrc/cvd). - - diff --git a/tests/functional/build/yaml/dotnetBotsBuild-CI.yml b/tests/functional/build/yaml/dotnetBotsBuild-CI.yml deleted file mode 100644 index a5d9e29280..0000000000 --- a/tests/functional/build/yaml/dotnetBotsBuild-CI.yml +++ /dev/null @@ -1,53 +0,0 @@ -parameters: - - name: buildConfiguration - displayName: Build Configuration - type: string - - - name: buildPlatform - displayName: Build Platfrom - type: string - - - name: solutionDir - displayName: Solution Directory - type: string - -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: NuGetToolInstaller@1 - displayName: "Use NuGet" - -- task: NuGetCommand@2 - displayName: "NuGet restore" - inputs: - restoreSolution: "Bots/DotNet/FunctionalTestsBots.sln" - restoreDirectory: "${{ parameters.solutionDir }}packages" - -- task: MSBuild@1 - displayName: "Build" - inputs: - solution: "Bots/DotNet/FunctionalTestsBots.sln" - vsVersion: 16.0 - platform: "${{ parameters.buildPlatform }}" - configuration: "${{ parameters.buildConfiguration }}" - -- task: PublishBuildArtifacts@1 - displayName: "Publish Artifact: build folder" - inputs: - PathtoPublish: build - ArtifactName: build - -- powershell: | - cd .. - ls -R - displayName: "Dir workspace" - continueOnError: true - condition: succeededOrFailed() diff --git a/tests/functional/build/yaml/functionalTests-CI.yml b/tests/functional/build/yaml/functionalTests-CI.yml deleted file mode 100644 index a7bd678a83..0000000000 --- a/tests/functional/build/yaml/functionalTests-CI.yml +++ /dev/null @@ -1,31 +0,0 @@ -# This YAML runs all the CI pipelines Dot Net, python, JS in parallel -variables: - BuildConfiguration: "Debug" - BuildPlatform: "Any CPU" - -jobs: - - job: "BuildJSBots" - steps: - - template: jsBotsBuildCI.yml - - - job: "BuildPythonBots" - steps: - - template: pythonBotsBuild-CI.yml - - - job: "BuildDotnetBots" - steps: - - template: dotnetBotsBuild-CI.yml - parameters: - buildConfiguration: "$(BUILDCONFIGURATION)" - buildPlatform: "$(BUILDPLATFORM)" - solutionDir: "$(BUILD.SOURCESDIRECTORY)/Bots/DotNet/" - - - job: "BuildSkillsFunctionalTests" - steps: - - template: functionalTestsBuild-CI.yml - parameters: - buildConfiguration: "$(BUILDCONFIGURATION)" - buildPlatform: "$(BUILDPLATFORM)" - -pool: - vmImage: "windows-2019" diff --git a/tests/functional/build/yaml/jsBotsBuildCI.yml b/tests/functional/build/yaml/jsBotsBuildCI.yml deleted file mode 100644 index 264b80a4c5..0000000000 --- a/tests/functional/build/yaml/jsBotsBuildCI.yml +++ /dev/null @@ -1,6 +0,0 @@ -steps: -- script: yarn - displayName: "yarn install" - -- script: yarn lint - displayName: "yarn lint" diff --git a/tests/functional/build/yaml/pythonBotsBuild-CI.yml b/tests/functional/build/yaml/pythonBotsBuild-CI.yml deleted file mode 100644 index 816153ba31..0000000000 --- a/tests/functional/build/yaml/pythonBotsBuild-CI.yml +++ /dev/null @@ -1,14 +0,0 @@ - steps: - - task: UsePythonVersion@0 - displayName: "Use Python version" - inputs: - versionSpec: "3.8.x" - - - task: Bash@3 - displayName: "Install dependencies" - inputs: - targetType: "inline" - script: | - python -m pip install pip==21.0.1; - find ./Bots -type f -name "requirements.txt" -exec pip install pip==21.0.1 -r "{}" \; - diff --git a/tests/functional/package.json b/tests/functional/package.json deleted file mode 100644 index e42329bcdd..0000000000 --- a/tests/functional/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "botframework-functional-tests", - "private": true, - "workspaces": { - "packages": [ - "Bots/JavaScript/*/*/*" - ] - }, - "devDependencies": { - "semistandard": "^16.0.0" - }, - "scripts": { - "lint": "semistandard" - } -} diff --git a/tests/functional/specs/TransciptTestRunner.md b/tests/functional/specs/TransciptTestRunner.md deleted file mode 100644 index 595652c2b9..0000000000 --- a/tests/functional/specs/TransciptTestRunner.md +++ /dev/null @@ -1,59 +0,0 @@ -# Transcript based test runner (DRAFT) - -## Summary - -Transcript based functional tests aim to allow us to test complex conversation flows against a bot and related skills end to end without having to worry about how the bot is implemented. - -## Contents - -- [Requirements](#requirements) -- [Design notes](#design-notes) -- [Implementation notes](#implementation-notes) -- [Other considerations and TODOs](#other-considerations-and-todos) - -## Requirements - -1. I can run functional tests from visual studio -2. I can run functional tests from the command line -3. I can run functional tests from a CI/CD pipeline in Azure DevOps -4. I can take a transcript created using the Emulator and run it against a deployed bot -5. I can emulate a channel from my tests (Emulator, Teams, WebChat) -6. I get clear messages describing where my test failed so I can debug it -7. I can debug a test step by step from the IDE -8. I should be able to use expressions in my expected bot responses to be able to deal with variable data -9. I should be able to run my tests against a bot running in localhost -10. We should write unit tests to make sure the runner classes and be easy to maintain - -## Design notes - -Here is a high level class diagram to get started - -![Class Diagram](media/TestRunnerClassDiagram2.png) - -- `TestRunner`: responsible for executing the `TestScript` using the desired `TestClientBase` -- `TranscriptConverter`: responsible for reading a transcript, converting it into a sequence of steps and returning the steps to be executed. Input: an emulator transcript file. Output: a "Test Script" file. -- `TestClientFactory`: creates an instance of a TestClient implementation. -- `TestClientBase`(abstract): base class for implementing channel specific test clients -- `_XYZ_Client`: specialized client that knows how to interact with a specific channel (it should be capable of generating channel OAuthTokens, handle responses coming from that specific channel, etc.) -- `ClientType` (enum): types are: DirectLine, Emulator, Teams - -## Implementation notes - -1. The first version of the runner will be in C# targeting DotNet Core 3.1 -2. We will rely on XUnit to write the tests but let's try to keep it out of the base classes (if possible) -3. The test runner should be able to load its settings from configuration using IConfiguration -4. The code should follow the SyleCop and FxCop ruleset used by the dotnet SDK -5. (Not a P0), we may be able to refactor some of the code in the test runner and make it part of the SDK testing package in the future - -## Other considerations and TODOs - -- Can we use Adaptive expressions to create asserts? -- Do we create a tool to convert a transcript into a test script that removes some of the noise in the transcript and makes test easier to read and write? -- Do we implement _XYZ_Options classes to configure the runner, the test script and the test client? -- Possible feature - chaining test scripts, consider we have a welcome part that we do over and over again and want to combine with the book a flight or get weather. The welcome portion and assertions can be written once and then we can append the other scenarios. - -## Test Script file - -It is generated by the TranscriptConverter from an Emulator transcript. The test script file contains the list of Activities found in its corresponding Emulator transcript with the following properties removed: -* All IDs -* All timestamps \ No newline at end of file diff --git a/tests/functional/specs/media/TestRunnerClassDiagram.png b/tests/functional/specs/media/TestRunnerClassDiagram.png deleted file mode 100644 index 1f1f4a5d72..0000000000 Binary files a/tests/functional/specs/media/TestRunnerClassDiagram.png and /dev/null differ diff --git a/tests/functional/specs/media/TestRunnerClassDiagram2.png b/tests/functional/specs/media/TestRunnerClassDiagram2.png deleted file mode 100644 index 627aaf21bc..0000000000 Binary files a/tests/functional/specs/media/TestRunnerClassDiagram2.png and /dev/null differ diff --git a/tests/functional/specs/media/src/TestRunnerDiagrams.vsdx b/tests/functional/specs/media/src/TestRunnerDiagrams.vsdx deleted file mode 100644 index 9755494c79..0000000000 Binary files a/tests/functional/specs/media/src/TestRunnerDiagrams.vsdx and /dev/null differ diff --git a/tests/functional/yarn.lock b/tests/functional/yarn.lock deleted file mode 100644 index 50dd5fb796..0000000000 --- a/tests/functional/yarn.lock +++ /dev/null @@ -1,2032 +0,0 @@ -# This file is generated by running "yarn install" inside your project. -# Manual changes might be lost - proceed with caution! - -__metadata: - version: 4 - cacheKey: 7 - -"@babel/code-frame@npm:^7.0.0": - version: 7.12.13 - resolution: "@babel/code-frame@npm:7.12.13" - dependencies: - "@babel/highlight": ^7.12.13 - checksum: 471532bb7cb4a300bd1a3201e75e7c0c83ebfb4e0e6610fdb53270521505d7efe0961258de61e7b1970ef3092a97ed675248ee1a44597912a1f61f903d85ef41 - languageName: node - linkType: hard - -"@babel/helper-validator-identifier@npm:^7.12.11": - version: 7.12.11 - resolution: "@babel/helper-validator-identifier@npm:7.12.11" - checksum: 18de432203264b501db2690b53370a4289dc56084f5a2c66de624b159ee28b8abaeb402b2b7584296d9261645d91ddb6bfd21125d3ffd9bf02e9262e77baf3d2 - languageName: node - linkType: hard - -"@babel/highlight@npm:^7.12.13": - version: 7.13.10 - resolution: "@babel/highlight@npm:7.13.10" - dependencies: - "@babel/helper-validator-identifier": ^7.12.11 - chalk: ^2.0.0 - js-tokens: ^4.0.0 - checksum: 8f23d3b728422713bfab45bee1e7584f2a3d2e20c9c4d6153312b898c82e776bdc5b1b2afaf9433fddb21d70417f5b477c0bb1a48613324fd761117e19a5702b - languageName: node - linkType: hard - -"@eslint/eslintrc@npm:^0.2.1": - version: 0.2.2 - resolution: "@eslint/eslintrc@npm:0.2.2" - dependencies: - ajv: ^6.12.4 - debug: ^4.1.1 - espree: ^7.3.0 - globals: ^12.1.0 - ignore: ^4.0.6 - import-fresh: ^3.2.1 - js-yaml: ^3.13.1 - lodash: ^4.17.19 - minimatch: ^3.0.4 - strip-json-comments: ^3.1.1 - checksum: 049c98898934100aa5ad4912242ecbec136454787c00776a236e5321c73176dbc813917445a3d50d230a5a84531a99ec0e2885dd7a6838be68b46b6720fa1459 - languageName: node - linkType: hard - -"@types/json5@npm:^0.0.29": - version: 0.0.29 - resolution: "@types/json5@npm:0.0.29" - checksum: 66e9ac0143ec521522c7bb670301e9836ee886207eeed1aab6d4854a1b19b404ab3a54cd8d449f9b1f13acc357f540be96f8ac2d1e86e301eab52ae0f9a4066f - languageName: node - linkType: hard - -"acorn-jsx@npm:^5.3.1": - version: 5.3.1 - resolution: "acorn-jsx@npm:5.3.1" - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - checksum: 5925bc5d79a2821a8f7250b6de2b02bb86c0470dcb78cf68a603855291c5e50602b9eaf294aba2efbf3ee7063c80a9074b520b2330cc1aef80b849bfc7a041c0 - languageName: node - linkType: hard - -"acorn@npm:^7.4.0": - version: 7.4.1 - resolution: "acorn@npm:7.4.1" - bin: - acorn: bin/acorn - checksum: 2bde98c28c1be9a08e41e581179b776b43396c9486ce52b2b9848d73c53df38c516b7edba4bacdc84cabc9d7a3299f3b46ef240ae261c38dbf8ddd89f635bd32 - languageName: node - linkType: hard - -"ajv@npm:^6.10.0, ajv@npm:^6.10.2, ajv@npm:^6.12.4": - version: 6.12.6 - resolution: "ajv@npm:6.12.6" - dependencies: - fast-deep-equal: ^3.1.1 - fast-json-stable-stringify: ^2.0.0 - json-schema-traverse: ^0.4.1 - uri-js: ^4.2.2 - checksum: 19a8f3b0a06001eb68e6268f4f9f04424b32baadd5df6ba8292cd473e22e5f4019ed9ab17c3e3510394178ed8bef9b42ad0bdb5c675d65f042421a774780ce1a - languageName: node - linkType: hard - -"ansi-colors@npm:^4.1.1": - version: 4.1.1 - resolution: "ansi-colors@npm:4.1.1" - checksum: 50d8dfbce25602caea1b170ecf4c71c4c9c58d2d1e3186fb5712848c0610d05fe60b8bb6a9eaebd9b54f1db3baf6f603e04214cce597cc7799bc9f47fd9a797a - languageName: node - linkType: hard - -"ansi-regex@npm:^4.1.0": - version: 4.1.0 - resolution: "ansi-regex@npm:4.1.0" - checksum: 53b6fe447cf92ee59739379de637af6f86b3b8a9537fbfe36a66f946f1d9d34afc3efe664ac31bcc7c3af042d43eabcfcfd3f790316d474bbc7b19a4b1d132dd - languageName: node - linkType: hard - -"ansi-regex@npm:^5.0.0": - version: 5.0.0 - resolution: "ansi-regex@npm:5.0.0" - checksum: cbd9b5c9dbbb4a949c2a6e93f1c6cc19f0683d8a4724d08d2158627be6d373f0f3ba1f4ada01dce7ee141f2ba2628fbbd29932c7d49926e3b630c7f329f3178b - languageName: node - linkType: hard - -"ansi-styles@npm:^3.2.0, ansi-styles@npm:^3.2.1": - version: 3.2.1 - resolution: "ansi-styles@npm:3.2.1" - dependencies: - color-convert: ^1.9.0 - checksum: 456e1c23d9277512a47718da75e7fbb0a5ee215ef893c2f76d6b3efe8fceabc861121b80b0362146f5f995d21a1633f05a19bbf6283ae66ac11dc3b9c0bed779 - languageName: node - linkType: hard - -"ansi-styles@npm:^4.1.0": - version: 4.3.0 - resolution: "ansi-styles@npm:4.3.0" - dependencies: - color-convert: ^2.0.1 - checksum: ea02c0179f3dd089a161f5fdd7ccd89dd84f31d82b68869f1134bf5c5b9e1313dadd2ff9edb02b44f46243f285ef5b785f6cb61c84a293694221417c42934407 - languageName: node - linkType: hard - -"argparse@npm:^1.0.7": - version: 1.0.10 - resolution: "argparse@npm:1.0.10" - dependencies: - sprintf-js: ~1.0.2 - checksum: 435adaef5f6671c3ef1478a22be6fd54bdb99fdbbce8f5561b9cbbb05068ccce87b7df3b9f3322ff52a6ebb9cab2b427cbedac47a07611690a9beaa5184093e2 - languageName: node - linkType: hard - -"array-includes@npm:^3.1.1, array-includes@npm:^3.1.2": - version: 3.1.3 - resolution: "array-includes@npm:3.1.3" - dependencies: - call-bind: ^1.0.2 - define-properties: ^1.1.3 - es-abstract: ^1.18.0-next.2 - get-intrinsic: ^1.1.1 - is-string: ^1.0.5 - checksum: 0610b361162071ef8749827f3a6e171da03ac14a518d4d45a513b6c22a7c5017c55dcbea4d34d699ef006f1f148aa52e6d437e0101c93996da736d0055add173 - languageName: node - linkType: hard - -"array.prototype.flat@npm:^1.2.3": - version: 1.2.4 - resolution: "array.prototype.flat@npm:1.2.4" - dependencies: - call-bind: ^1.0.0 - define-properties: ^1.1.3 - es-abstract: ^1.18.0-next.1 - checksum: b4936e68f8bb4ed8d6bf12eff4e19e93f263ee6ff66b0e394be275c0b168e2a4889740f105799ec1d19631e93020fba528534ca34dd0538e24d2a90043ebc6b0 - languageName: node - linkType: hard - -"array.prototype.flatmap@npm:^1.2.3": - version: 1.2.4 - resolution: "array.prototype.flatmap@npm:1.2.4" - dependencies: - call-bind: ^1.0.0 - define-properties: ^1.1.3 - es-abstract: ^1.18.0-next.1 - function-bind: ^1.1.1 - checksum: 2718f73b91e5f377fb230765c1ab1a67063d11f7d14b1edfa6283ba4129f710b2d2416003dc8c9fc25595d921a76430ed8a379f66e58d237e5f1f300b6465ece - languageName: node - linkType: hard - -"astral-regex@npm:^1.0.0": - version: 1.0.0 - resolution: "astral-regex@npm:1.0.0" - checksum: 08e37f599604eb3894af4ec5f9845caec7a45d10c1b57b04c4fc21cc669091803f8386efc52957ec3c7ae8c3af60b933018926aab156e5696a7aab393d6e0874 - languageName: node - linkType: hard - -"balanced-match@npm:^1.0.0": - version: 1.0.0 - resolution: "balanced-match@npm:1.0.0" - checksum: f515a605fe1b59f476f7477c5e1d53ad55b4f42982fca1d57b6701906f4ad1f1ac90fd6587d92cc1af2edb43eecf979214dd847ee410a6de9db4ebf0dd128d62 - languageName: node - linkType: hard - -"botframework-functional-tests@workspace:.": - version: 0.0.0-use.local - resolution: "botframework-functional-tests@workspace:." - dependencies: - semistandard: ^16.0.0 - languageName: unknown - linkType: soft - -"brace-expansion@npm:^1.1.7": - version: 1.1.11 - resolution: "brace-expansion@npm:1.1.11" - dependencies: - balanced-match: ^1.0.0 - concat-map: 0.0.1 - checksum: 4c878e25e4858baf801945dfd63eb68feab2e502cf1122f25f3915c0e3bf397af3a93ff6bef0798db41c0d81ef28c08e55daac38058710f749a3b96eee6b8f40 - languageName: node - linkType: hard - -"call-bind@npm:^1.0.0, call-bind@npm:^1.0.2": - version: 1.0.2 - resolution: "call-bind@npm:1.0.2" - dependencies: - function-bind: ^1.1.1 - get-intrinsic: ^1.0.2 - checksum: 18cc6107a1f028247f2b505dae73ad1c63b737addfcd43ff75159f072c5c827300c1fb66f26ee0ec70fc2fdd005ce68d65c05a2a34b74bab08c3b1921954ada9 - languageName: node - linkType: hard - -"callsites@npm:^3.0.0": - version: 3.1.0 - resolution: "callsites@npm:3.1.0" - checksum: f726bf10d752901174cae348e69c2e58206404d5eebcea485b3fedbcf7fcffdb397e10919fdf6ee2c8adb4be52a64eea2365d52583611939bfecd109260451c9 - languageName: node - linkType: hard - -"chalk@npm:^2.0.0": - version: 2.4.2 - resolution: "chalk@npm:2.4.2" - dependencies: - ansi-styles: ^3.2.1 - escape-string-regexp: ^1.0.5 - supports-color: ^5.3.0 - checksum: 22c7b7b5bc761c882bb6516454a1a671923f1c53ff972860065aa0b28a195f230163c1d46ee88bcc7a03e5539177d896457d8bc727de7f244c6e87032743038e - languageName: node - linkType: hard - -"chalk@npm:^4.0.0": - version: 4.1.0 - resolution: "chalk@npm:4.1.0" - dependencies: - ansi-styles: ^4.1.0 - supports-color: ^7.1.0 - checksum: f860285b419f9e925c2db0f45ffa88aa8794c14b80cc5d01ff30930bcfc384996606362706f0829cf557f6d36152a5fb2d227ad63c4bc90e2ec9e9dbed4a3c07 - languageName: node - linkType: hard - -"color-convert@npm:^1.9.0": - version: 1.9.3 - resolution: "color-convert@npm:1.9.3" - dependencies: - color-name: 1.1.3 - checksum: 5f244daa3d1fe1f216d48878c550465067d15268688308554e613b7640a068f96588096d51f0b98b68f15d6ff6bb8ad24e172582ac8c0ad43fa4d3da60fd1b79 - languageName: node - linkType: hard - -"color-convert@npm:^2.0.1": - version: 2.0.1 - resolution: "color-convert@npm:2.0.1" - dependencies: - color-name: ~1.1.4 - checksum: 3d5d8a011a43012ca11b6d739049ecf2055d95582fd16ec44bf1e685eb0baa5cc652002be8a1dc92b429c8d87418287d0528266a7595cb1ad8a7f4f1d3049df2 - languageName: node - linkType: hard - -"color-name@npm:1.1.3": - version: 1.1.3 - resolution: "color-name@npm:1.1.3" - checksum: d8b91bb90aefc05b6ff568cf8889566dcc6269824df6f3c9b8ca842b18d7f4d089c07dc166808d33f22092d4a79167aa56a96a5ff0d21efab548bf44614db43b - languageName: node - linkType: hard - -"color-name@npm:~1.1.4": - version: 1.1.4 - resolution: "color-name@npm:1.1.4" - checksum: 3e1c9a4dee12eada307436f61614dd11fe300469db2b83f80c8b7a7cd8a1015f0f18dd13403f018927b249003777ff60baba4a03c65f12e6bddc0dfd9642021f - languageName: node - linkType: hard - -"concat-map@npm:0.0.1": - version: 0.0.1 - resolution: "concat-map@npm:0.0.1" - checksum: 554e28d9ee5aa6e061795473ee092cb3d3a2cbdb76c35416e0bb6e03f136d7d07676da387b2ed0ec4106cedbb6534080d9abc48ecc4a92b76406cf2d0c3c0c4b - languageName: node - linkType: hard - -"contains-path@npm:^0.1.0": - version: 0.1.0 - resolution: "contains-path@npm:0.1.0" - checksum: 59920a59a0c7d1244235d76b8cfd2b2e7a8dcc463fa578ef9d4d5a5a73eeb14d75dada6b21188e0b35f2474ae9efd10c3698372e674db9c6a904b281998b97d6 - languageName: node - linkType: hard - -"cross-spawn@npm:^7.0.2": - version: 7.0.3 - resolution: "cross-spawn@npm:7.0.3" - dependencies: - path-key: ^3.1.0 - shebang-command: ^2.0.0 - which: ^2.0.1 - checksum: 51f10036f5f1de781be98f4738d58b50c6d44f4f471069b8ab075b21605893ba1548654880f7310a29a732d6fc7cd481da6026169b9f0831cab0148a62fb397a - languageName: node - linkType: hard - -"debug@npm:^2.6.9": - version: 2.6.9 - resolution: "debug@npm:2.6.9" - dependencies: - ms: 2.0.0 - checksum: 559f44f98cf25e2ee489022aec173afbff746564cb108c4493becb95bc3c017a67bdaa25a0ff64801fd32c35051d00af0e56cc7f762ae2c3bc089496e5a1c31b - languageName: node - linkType: hard - -"debug@npm:^4.0.1, debug@npm:^4.1.1": - version: 4.3.1 - resolution: "debug@npm:4.3.1" - dependencies: - ms: 2.1.2 - peerDependenciesMeta: - supports-color: - optional: true - checksum: 0d41ba5177510e8b388dfd7df143ab0f9312e4abdaba312595461511dac88e9ef8101939d33b4e6d37e10341af6a5301082e4d7d6f3deb4d57bc05fc7d296fad - languageName: node - linkType: hard - -"deep-is@npm:^0.1.3": - version: 0.1.3 - resolution: "deep-is@npm:0.1.3" - checksum: 3de58f86af4dec86c8be531a5abaf2e6d8ea98fa2f1d81a3a778d0d8df920ee282043a6ef05bfb4eb699c8551df9ac1b808d4dc71d54cc40ab1efa5ce8792943 - languageName: node - linkType: hard - -"define-properties@npm:^1.1.3": - version: 1.1.3 - resolution: "define-properties@npm:1.1.3" - dependencies: - object-keys: ^1.0.12 - checksum: b69c48c1b1dacb61f0b1cea367707c3bb214e3c47818aff18e6f20a7f88cbfa33d4cbdfd9ff79e56faba95ddca3d78ff10fbf2f02ecfad6f3e13b256e76b1212 - languageName: node - linkType: hard - -"doctrine@npm:1.5.0": - version: 1.5.0 - resolution: "doctrine@npm:1.5.0" - dependencies: - esutils: ^2.0.2 - isarray: ^1.0.0 - checksum: aaffea02f963b8b07a78b1e27d7cef29be65d31be2c6681cb2872c2fb3781e14615bd05d4dff6036f75dcf3f191216058409fbfec805d3a7277a8647cd5bdee1 - languageName: node - linkType: hard - -"doctrine@npm:^2.1.0": - version: 2.1.0 - resolution: "doctrine@npm:2.1.0" - dependencies: - esutils: ^2.0.2 - checksum: 4aa55e46757cc11bff8efa67cdb679dd89e87c954ea9d88fad5a9198cfe0a73748085503d29bebcb143487d720a759a6bbe81d6848c94da46a55c7a366b9834e - languageName: node - linkType: hard - -"doctrine@npm:^3.0.0": - version: 3.0.0 - resolution: "doctrine@npm:3.0.0" - dependencies: - esutils: ^2.0.2 - checksum: 2eae469bd2889ceee9892083a67340b3622568fe5290edce620e5d5ddab23d644b2a780e9a7c68ad9c8a62716a70c5e484402ac93a398fa78b54b7505592aa7f - languageName: node - linkType: hard - -"emoji-regex@npm:^7.0.1": - version: 7.0.3 - resolution: "emoji-regex@npm:7.0.3" - checksum: e3a504cf5242061d9b3c78a88ce787d6beee37a5d21287c6ccdddf1fe665d5ef3eddfdda663d0baf683df8e7d354210eeb1458a7d9afdf0d7a28d48cbb9975e1 - languageName: node - linkType: hard - -"enquirer@npm:^2.3.5": - version: 2.3.6 - resolution: "enquirer@npm:2.3.6" - dependencies: - ansi-colors: ^4.1.1 - checksum: e249bb97bf7d5a91d51081547ea5aa1d849604e5de74feff2c48f7174fc6c9dfcfeea42ef5536e9a3be58964a248c322d6897269ae7bba3e1b6d24f152d9d685 - languageName: node - linkType: hard - -"error-ex@npm:^1.2.0, error-ex@npm:^1.3.1": - version: 1.3.2 - resolution: "error-ex@npm:1.3.2" - dependencies: - is-arrayish: ^0.2.1 - checksum: 6c6c9187429ae867d145bc64c682c7c137b1f8373a406dc3b605c0d92f15b85bfcea02b461dc55ae11b10d013377e1eaf3d469d2861b2f94703c743620a9c08c - languageName: node - linkType: hard - -"es-abstract@npm:^1.18.0-next.1, es-abstract@npm:^1.18.0-next.2": - version: 1.18.0 - resolution: "es-abstract@npm:1.18.0" - dependencies: - call-bind: ^1.0.2 - es-to-primitive: ^1.2.1 - function-bind: ^1.1.1 - get-intrinsic: ^1.1.1 - has: ^1.0.3 - has-symbols: ^1.0.2 - is-callable: ^1.2.3 - is-negative-zero: ^2.0.1 - is-regex: ^1.1.2 - is-string: ^1.0.5 - object-inspect: ^1.9.0 - object-keys: ^1.1.1 - object.assign: ^4.1.2 - string.prototype.trimend: ^1.0.4 - string.prototype.trimstart: ^1.0.4 - unbox-primitive: ^1.0.0 - checksum: 019fa7c51e10532cd07ca3aa9b76e4c6ad6f421e15064205d144da08da54f8fc057edc262f6f95775e0b249ecbb753b497050dd75ab69a3c1c89cb9b734e42ca - languageName: node - linkType: hard - -"es-to-primitive@npm:^1.2.1": - version: 1.2.1 - resolution: "es-to-primitive@npm:1.2.1" - dependencies: - is-callable: ^1.1.4 - is-date-object: ^1.0.1 - is-symbol: ^1.0.2 - checksum: d20b7be268b84662469972ec7265a57d4d6a65b9bf2b73f040d75e14f9f6dbe266a1a88579162e11349f9cb70eaa17640efb515c90dab19745a904b680b14be3 - languageName: node - linkType: hard - -"escape-string-regexp@npm:^1.0.5": - version: 1.0.5 - resolution: "escape-string-regexp@npm:1.0.5" - checksum: f9484b8b4c8827d816e0fd905c25ed4b561376a9c220e1430403ea84619bf680c76a883a48cff8b8e091daf55d6a497e37479f9787b9f15f3c421b6054289744 - languageName: node - linkType: hard - -"eslint-config-semistandard@npm:15.0.1": - version: 15.0.1 - resolution: "eslint-config-semistandard@npm:15.0.1" - peerDependencies: - eslint: ">=6.0.1" - eslint-config-standard: ">=14.1.0" - eslint-plugin-import: ">=2.18.0" - eslint-plugin-node: ">=9.1.0" - eslint-plugin-promise: ">=4.2.1" - eslint-plugin-standard: ">=4.0.0" - checksum: 2550dfd3f9f4151f52cbbc072dc835d0fbc7582e83715f47f743d31bceabcaffd616dbb8facbcbe804181ea3c387511a4ad608efe109508867e05404a5fcd3c7 - languageName: node - linkType: hard - -"eslint-config-standard-jsx@npm:10.0.0": - version: 10.0.0 - resolution: "eslint-config-standard-jsx@npm:10.0.0" - peerDependencies: - eslint: ^7.12.1 - eslint-plugin-react: ^7.21.5 - checksum: 1808aa6f198b1fcebfc9590363e6c9cc6b00fb1c40a2f9eb66fe50ff4ab5fbc6b5f6ca070f7ea8e3afc15331a05c8e38b5f3fdb1c70949839eacd3da9a2237b8 - languageName: node - linkType: hard - -"eslint-config-standard@npm:16.0.0": - version: 16.0.0 - resolution: "eslint-config-standard@npm:16.0.0" - peerDependencies: - eslint: ^7.12.1 - eslint-plugin-import: ^2.22.1 - eslint-plugin-node: ^11.1.0 - eslint-plugin-promise: ^4.2.1 - checksum: 385e702d59929f4eeae471d47a51da138c25bb0a263adb935623ac0b9f25f54869e5eb2ea0a6f9658c4c52caeb6cfa282ac9335ff6eb39b8ff8e998997af73e8 - languageName: node - linkType: hard - -"eslint-import-resolver-node@npm:^0.3.4": - version: 0.3.4 - resolution: "eslint-import-resolver-node@npm:0.3.4" - dependencies: - debug: ^2.6.9 - resolve: ^1.13.1 - checksum: 825e34e662c988ece8229e6956a95f12d2fa19265b429e3e3db14e58bfe72e270c999cda0cfc690793ed6e6a3e49ffa8df0e0a8842d668a1f0f7de5ae1aa36f9 - languageName: node - linkType: hard - -"eslint-module-utils@npm:^2.6.0": - version: 2.6.0 - resolution: "eslint-module-utils@npm:2.6.0" - dependencies: - debug: ^2.6.9 - pkg-dir: ^2.0.0 - checksum: f584af176480a702eedcdb3f610797f8b8d1293c3835ed71fadb579ec28400b91ded5283729418f63d48dc27c6358bd66f2bd839614d565a1b78d3c3440ee8f7 - languageName: node - linkType: hard - -"eslint-plugin-es@npm:^3.0.0": - version: 3.0.1 - resolution: "eslint-plugin-es@npm:3.0.1" - dependencies: - eslint-utils: ^2.0.0 - regexpp: ^3.0.0 - peerDependencies: - eslint: ">=4.19.1" - checksum: 11b3229c3c7126d24dea1df90ec6b7966f8a269fc95fa24ae1b868fb676474cdb7e5144076aefca99cb1d3e9c89e6d0ae72d4874242d0987f210e0ee7e7f6896 - languageName: node - linkType: hard - -"eslint-plugin-import@npm:~2.22.1": - version: 2.22.1 - resolution: "eslint-plugin-import@npm:2.22.1" - dependencies: - array-includes: ^3.1.1 - array.prototype.flat: ^1.2.3 - contains-path: ^0.1.0 - debug: ^2.6.9 - doctrine: 1.5.0 - eslint-import-resolver-node: ^0.3.4 - eslint-module-utils: ^2.6.0 - has: ^1.0.3 - minimatch: ^3.0.4 - object.values: ^1.1.1 - read-pkg-up: ^2.0.0 - resolve: ^1.17.0 - tsconfig-paths: ^3.9.0 - peerDependencies: - eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 - checksum: 35ae09ceae6f0fe239f6b72e134d58d74762ad1ed0f57aa989affb856354e46bc082bb6df9399b624989107efb9ab9af2c91c08f03c0c70c5cb46a37676591ec - languageName: node - linkType: hard - -"eslint-plugin-node@npm:~11.1.0": - version: 11.1.0 - resolution: "eslint-plugin-node@npm:11.1.0" - dependencies: - eslint-plugin-es: ^3.0.0 - eslint-utils: ^2.0.0 - ignore: ^5.1.1 - minimatch: ^3.0.4 - resolve: ^1.10.1 - semver: ^6.1.0 - peerDependencies: - eslint: ">=5.16.0" - checksum: 634e03613a4aa0ffd5ccf0807a375840a915797781a4e5d44fb1c4d331cd2a5e6b3da41924a0d6a2123c1a51626db032a038210153fbba6a0012e0b7c7f26499 - languageName: node - linkType: hard - -"eslint-plugin-promise@npm:~4.2.1": - version: 4.2.1 - resolution: "eslint-plugin-promise@npm:4.2.1" - checksum: 8c233a0b5f5646e08709e999aa75973481ee46c45255ec4b4d1577915f68d79ae52f6f84e361af5b761971294bdb38ec86021515fa4cff178f57c989226dd671 - languageName: node - linkType: hard - -"eslint-plugin-react@npm:~7.21.5": - version: 7.21.5 - resolution: "eslint-plugin-react@npm:7.21.5" - dependencies: - array-includes: ^3.1.1 - array.prototype.flatmap: ^1.2.3 - doctrine: ^2.1.0 - has: ^1.0.3 - jsx-ast-utils: ^2.4.1 || ^3.0.0 - object.entries: ^1.1.2 - object.fromentries: ^2.0.2 - object.values: ^1.1.1 - prop-types: ^15.7.2 - resolve: ^1.18.1 - string.prototype.matchall: ^4.0.2 - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 - checksum: a7123f6feb287ad7e15d36b1c9fda8f78a0f0d39b9ee2d6479a5f0ad0effb8f60f639ef7449cecabb711095060e487f0950b69478b22768547b909c0de4f3afd - languageName: node - linkType: hard - -"eslint-plugin-standard@npm:~4.0.2": - version: 4.0.2 - resolution: "eslint-plugin-standard@npm:4.0.2" - peerDependencies: - eslint: ">=5.0.0" - checksum: 1d1e4e5e25e39fd2e645e204d3c407d098b5d179730ed1bb9f156ecc8fa3828b11246b8eff03d4af95f33abdc91efa103255b3dacc311479edb585fea7d3b65b - languageName: node - linkType: hard - -"eslint-scope@npm:^5.1.1": - version: 5.1.1 - resolution: "eslint-scope@npm:5.1.1" - dependencies: - esrecurse: ^4.3.0 - estraverse: ^4.1.1 - checksum: 79465cf5082f4216176f6d49c7d088de89ee890f912eb87b831f23ee9a5e17ed0f3f2ab6108fb8fefa0474ba5ebeaa9bdefbe49ba704bd879b73f2445e23ee10 - languageName: node - linkType: hard - -"eslint-utils@npm:^2.0.0, eslint-utils@npm:^2.1.0": - version: 2.1.0 - resolution: "eslint-utils@npm:2.1.0" - dependencies: - eslint-visitor-keys: ^1.1.0 - checksum: a43892372a4205374982ac9d4c8edc5fe180cba76535ab9184c48f18a3d931b7ffdd6862cb2f8ca4305c47eface0e614e39884a75fbf169fcc55a6131af2ec48 - languageName: node - linkType: hard - -"eslint-visitor-keys@npm:^1.1.0, eslint-visitor-keys@npm:^1.3.0": - version: 1.3.0 - resolution: "eslint-visitor-keys@npm:1.3.0" - checksum: 58ab7a0107621d8a0fe19142a5e1306fd527c0f36b65d5c79033639e80278d8060264804f42c56f68e5541c4cc83d9175f9143083774cec8222f6cd5a695306e - languageName: node - linkType: hard - -"eslint-visitor-keys@npm:^2.0.0": - version: 2.0.0 - resolution: "eslint-visitor-keys@npm:2.0.0" - checksum: 429dabdcab3c1cf5e65d44843afc513398d4ee32a37f93edc93bb5ba59a12b78fa67d87ff23c752c170b5e4f9085050f45b3c036cdfb23d40a724f2614048140 - languageName: node - linkType: hard - -"eslint@npm:~7.12.1": - version: 7.12.1 - resolution: "eslint@npm:7.12.1" - dependencies: - "@babel/code-frame": ^7.0.0 - "@eslint/eslintrc": ^0.2.1 - ajv: ^6.10.0 - chalk: ^4.0.0 - cross-spawn: ^7.0.2 - debug: ^4.0.1 - doctrine: ^3.0.0 - enquirer: ^2.3.5 - eslint-scope: ^5.1.1 - eslint-utils: ^2.1.0 - eslint-visitor-keys: ^2.0.0 - espree: ^7.3.0 - esquery: ^1.2.0 - esutils: ^2.0.2 - file-entry-cache: ^5.0.1 - functional-red-black-tree: ^1.0.1 - glob-parent: ^5.0.0 - globals: ^12.1.0 - ignore: ^4.0.6 - import-fresh: ^3.0.0 - imurmurhash: ^0.1.4 - is-glob: ^4.0.0 - js-yaml: ^3.13.1 - json-stable-stringify-without-jsonify: ^1.0.1 - levn: ^0.4.1 - lodash: ^4.17.19 - minimatch: ^3.0.4 - natural-compare: ^1.4.0 - optionator: ^0.9.1 - progress: ^2.0.0 - regexpp: ^3.1.0 - semver: ^7.2.1 - strip-ansi: ^6.0.0 - strip-json-comments: ^3.1.0 - table: ^5.2.3 - text-table: ^0.2.0 - v8-compile-cache: ^2.0.3 - bin: - eslint: bin/eslint.js - checksum: b3d11ea516d2932db5854ea942a546b711e6ee8735306627042bb9e2539e96844cc62cdb5826039f5d0509202936045c2cd7b84b457004c891a326f8eccae333 - languageName: node - linkType: hard - -"espree@npm:^7.3.0": - version: 7.3.1 - resolution: "espree@npm:7.3.1" - dependencies: - acorn: ^7.4.0 - acorn-jsx: ^5.3.1 - eslint-visitor-keys: ^1.3.0 - checksum: ff8e0f73939e1e76529b630cba65b6128e4d18ed7bf0b16af62022cadc73ddb950c7e5aa629cca74e8abebdf76f6dcd1cf873dbc819f10599827c6019e2f8e91 - languageName: node - linkType: hard - -"esprima@npm:^4.0.0": - version: 4.0.1 - resolution: "esprima@npm:4.0.1" - bin: - esparse: ./bin/esparse.js - esvalidate: ./bin/esvalidate.js - checksum: 5df45a3d9c95c36800d028ba76d8d4e04e199932b58c2939f462f859fd583e7d39b4a12d3f97986cf272a28a5fe5948ee6e49e36ef63f67b5b48d82a635c5081 - languageName: node - linkType: hard - -"esquery@npm:^1.2.0": - version: 1.4.0 - resolution: "esquery@npm:1.4.0" - dependencies: - estraverse: ^5.1.0 - checksum: 3293ecc1507a8cec8d2da8a4707275c2ccf5413e7a3c771fe41c16cee603cacd193bb7383a6e219d1f7d2449156ef575ffd66c839073d4a8058f72856a15f622 - languageName: node - linkType: hard - -"esrecurse@npm:^4.3.0": - version: 4.3.0 - resolution: "esrecurse@npm:4.3.0" - dependencies: - estraverse: ^5.2.0 - checksum: 2c96302dd5c4e6d07154d0ce6baee9e829ebf77e21c50c5ca4f24d6d0006fe4a4582364624a01f5667a3633b3e39bbce1a8191924f8419fb71584bb45bf7bb81 - languageName: node - linkType: hard - -"estraverse@npm:^4.1.1": - version: 4.3.0 - resolution: "estraverse@npm:4.3.0" - checksum: 1e4c627da9e9af07bf7b2817320f606841808fb2ec0cbd81097b30d5f90d8613288b3e523153babe04615d59b54ef876d98f0ca27488b6c0934dacd725a8d338 - languageName: node - linkType: hard - -"estraverse@npm:^5.1.0, estraverse@npm:^5.2.0": - version: 5.2.0 - resolution: "estraverse@npm:5.2.0" - checksum: 7dc1b027aebf937bab10c3254d9d73ed21672d7382518c9ddb9dc45560cb2f4e6548cc8ff1a07b7f431e94bd0fb0bf5da75b602e2473f966fea141c4c31b31d6 - languageName: node - linkType: hard - -"esutils@npm:^2.0.2": - version: 2.0.3 - resolution: "esutils@npm:2.0.3" - checksum: 590b04533177f8f6f0f352b3ac7da6c1c1e3d8375d8973972fba9c94558ca168685fd38319c3c6f4c37ba256df7494a7f15d8e761df1655af8a8f0027d988f8f - languageName: node - linkType: hard - -"fast-deep-equal@npm:^3.1.1": - version: 3.1.3 - resolution: "fast-deep-equal@npm:3.1.3" - checksum: 451526766b219503131d11e823eaadd1533080b0be4860e316670b039dcaf31cd1007c2fe036a9b922abba7c040dfad5e942ed79d21f2ff849e50049f36e0fb7 - languageName: node - linkType: hard - -"fast-json-stable-stringify@npm:^2.0.0": - version: 2.1.0 - resolution: "fast-json-stable-stringify@npm:2.1.0" - checksum: 7df3fabfe445d65953b2d9d9d3958bd895438b215a40fb87dae8b2165c5169a897785eb5d51e6cf0eb03523af756e3d82ea01083f6ac6341fe16db532fee3016 - languageName: node - linkType: hard - -"fast-levenshtein@npm:^2.0.6": - version: 2.0.6 - resolution: "fast-levenshtein@npm:2.0.6" - checksum: a2d03af3088b0397633e007fb3010ecfa4f91cae2116d2385653c59396a1b31467641afa672a79e6f82218518670dc144128378124e711e35dbf90bc82846f22 - languageName: node - linkType: hard - -"file-entry-cache@npm:^5.0.1": - version: 5.0.1 - resolution: "file-entry-cache@npm:5.0.1" - dependencies: - flat-cache: ^2.0.1 - checksum: 7140588becf15f05ee956cfb359b5f23e0c73acbbd38ad14c7a76a0097342e6bfc0a8151cd2e481ea3cbb735190ba9a0df4b69055ebb5b0389c62339b1a2f86b - languageName: node - linkType: hard - -"find-up@npm:^2.0.0, find-up@npm:^2.1.0": - version: 2.1.0 - resolution: "find-up@npm:2.1.0" - dependencies: - locate-path: ^2.0.0 - checksum: 9dedb89f936b572f7c9fda3f66ebe146b0000fe9ef16fad94a77c25ce9585962e910bb32c1e08bab9b423985ff20221d2af4b7e4130b27c0f5f60c1aad3f6a7f - languageName: node - linkType: hard - -"find-up@npm:^3.0.0": - version: 3.0.0 - resolution: "find-up@npm:3.0.0" - dependencies: - locate-path: ^3.0.0 - checksum: c5422fc7231820421cff6f6e3a5d00a11a79fd16625f2af779c6aedfbaad66764fd149c1b84017aa44e85f86395eb25c31188ad273fc468a981b529eaa59a424 - languageName: node - linkType: hard - -"flat-cache@npm:^2.0.1": - version: 2.0.1 - resolution: "flat-cache@npm:2.0.1" - dependencies: - flatted: ^2.0.0 - rimraf: 2.6.3 - write: 1.0.3 - checksum: a36ba407553064be4a571cdee4021a50290f6179a0827df1d076a2e33cd84e543d0274cb15dbeb551c2ae6d53e611e3c02564a93f0d527563d0f560be7a14b0d - languageName: node - linkType: hard - -"flatted@npm:^2.0.0": - version: 2.0.2 - resolution: "flatted@npm:2.0.2" - checksum: a3e5fb71ad3c4f0661cd3899864812bcf3f64bdf6aa5f33f967c9c2a8a5f0c7219707e864c0602115fef40e093415f76a43e77afd0a86990904e2217ddb44eb4 - languageName: node - linkType: hard - -"fs.realpath@npm:^1.0.0": - version: 1.0.0 - resolution: "fs.realpath@npm:1.0.0" - checksum: 698a91b1695e3926185c9e5b0dd57cf687dceb4eb73799af91e6b2ab741735e2962c366c5af6403ffddae2619914193bd339efa706fdc984d0ffc74b7a3603f4 - languageName: node - linkType: hard - -"function-bind@npm:^1.1.1": - version: 1.1.1 - resolution: "function-bind@npm:1.1.1" - checksum: ffad86e7d2010ba179aaa6a3987d2cc0ed48fa92d27f1ed84bfa06d14f77deeed5bfbae7f00bdebc0c54218392cab2b18ecc080e2c72f592431927b87a27d42b - languageName: node - linkType: hard - -"functional-red-black-tree@npm:^1.0.1": - version: 1.0.1 - resolution: "functional-red-black-tree@npm:1.0.1" - checksum: 477ecaf62d4f8d788876099b35ed4b97586b331e729d2d28d0df96b598863d21c18b8a45a6cbecb6c2bf7f5e5ef1e82a053570583ef9a0ff8336683ab42b8d14 - languageName: node - linkType: hard - -"get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.0, get-intrinsic@npm:^1.1.1": - version: 1.1.1 - resolution: "get-intrinsic@npm:1.1.1" - dependencies: - function-bind: ^1.1.1 - has: ^1.0.3 - has-symbols: ^1.0.1 - checksum: acf1506f25a32a194cfc5c19d33835756080d970eb6e29a8a3852380106df981acef7bb9ac2002689437235221f24bcbdc1e3532b9bcacd7ff3621091fafe607 - languageName: node - linkType: hard - -"get-stdin@npm:^8.0.0": - version: 8.0.0 - resolution: "get-stdin@npm:8.0.0" - checksum: 009a4c42484cc30fe0f353d0a8c593dda0cdef46f4fb8c1668860d8e2fd6ca53faa2a08a85fdf3a4a56dfbe38f940772fd8b66e0ecba333a8f055c56cda72537 - languageName: node - linkType: hard - -"glob-parent@npm:^5.0.0": - version: 5.1.2 - resolution: "glob-parent@npm:5.1.2" - dependencies: - is-glob: ^4.0.1 - checksum: 82fcaa4ce102a0ae01370ed8fd5299ca32184af0418e1c1b613ed851240160558c0cc9712868eb9ca1924f687b07cd9c70c25f303f39f9f376d9a32f94f28e76 - languageName: node - linkType: hard - -"glob@npm:^7.1.3": - version: 7.1.6 - resolution: "glob@npm:7.1.6" - dependencies: - fs.realpath: ^1.0.0 - inflight: ^1.0.4 - inherits: 2 - minimatch: ^3.0.4 - once: ^1.3.0 - path-is-absolute: ^1.0.0 - checksum: 789977b52432865bd63846da5c75a6efc2c56abdc0cb5ffcdb8e91eeb67a58fa5594c1195d18b2b4aff99675b0739ed6bd61024b26562e0cca18c8f993efdc82 - languageName: node - linkType: hard - -"globals@npm:^12.1.0": - version: 12.4.0 - resolution: "globals@npm:12.4.0" - dependencies: - type-fest: ^0.8.1 - checksum: 0b9764bdeab0bc9762dea8954a0d4c5db029420bd8bf693df9098ce7e045ccaf9b2d259185396fd048b051d42fdc8dc7ab02af62e3dbeb2324a78a05aac8d33c - languageName: node - linkType: hard - -"graceful-fs@npm:^4.1.15, graceful-fs@npm:^4.1.2": - version: 4.2.6 - resolution: "graceful-fs@npm:4.2.6" - checksum: 84d39c7756892553da990a9db7e45f844b3309b37b5a00174cbb4748476f2250c54f24594d4d252f64f085c65c2fdac7c809419bf6d55f0e6e42eb07ac0f5bf2 - languageName: node - linkType: hard - -"has-bigints@npm:^1.0.0": - version: 1.0.1 - resolution: "has-bigints@npm:1.0.1" - checksum: 1074b644f5f2c319fc31af00fe2f81b6e21e204bb46da70ff7b970fe65c56f504e697fe6b41823ba679bd4111840482a83327d3432b8d670a684da4087ed074b - languageName: node - linkType: hard - -"has-flag@npm:^3.0.0": - version: 3.0.0 - resolution: "has-flag@npm:3.0.0" - checksum: 63aade480d27aeedb3b5b63a2e069d47d0006bf182338d662e7941cdc024e68a28418e0efa8dc5df30db9c4ee2407f39e6ea3f16cfbc6b83848b450826a28aa0 - languageName: node - linkType: hard - -"has-flag@npm:^4.0.0": - version: 4.0.0 - resolution: "has-flag@npm:4.0.0" - checksum: 2e5391139d3d287231ccb58659702392f6e3abeac3296fb4721afaff46493f3d9b99a9329ae015dfe973aa206ed5c75f43e86aec0267dce79aa5c2b6e811b3ad - languageName: node - linkType: hard - -"has-symbols@npm:^1.0.0, has-symbols@npm:^1.0.1, has-symbols@npm:^1.0.2": - version: 1.0.2 - resolution: "has-symbols@npm:1.0.2" - checksum: 1b73928752fa9ca993fa48f7b3832c95ea408c0ec635b2d6cbaf011b94a7e6a704a9254ae6d8ecc913d4dd92f2ff760dc43aad7c7e790ddb3f627005614d8e28 - languageName: node - linkType: hard - -"has@npm:^1.0.3": - version: 1.0.3 - resolution: "has@npm:1.0.3" - dependencies: - function-bind: ^1.1.1 - checksum: c686e15300d41364486c099a9259d9c418022c294244843dcd712c4c286ff839d4f23a25413baa28c4d2c1e828afc2aaab70f685400b391533980223c71fa1ca - languageName: node - linkType: hard - -"hosted-git-info@npm:^2.1.4": - version: 2.8.8 - resolution: "hosted-git-info@npm:2.8.8" - checksum: 3ecc389dc6ecbd5463fada7e04461e96f3c817fe2f989ca41e9dd3b503745a0bfa26fba405861b2831ca64edc1abc5d2fbc97ee977303f89650dac4fbfdc2d7a - languageName: node - linkType: hard - -"ignore@npm:^4.0.6": - version: 4.0.6 - resolution: "ignore@npm:4.0.6" - checksum: 8f7b7f7c261d110604aed4340771933b0a42ffd2075e87bf8b4229ceb679659c5384c99e25c059f53a2b0e16cebaa4c49f7e837d1f374d1abf91fea46ccddd1a - languageName: node - linkType: hard - -"ignore@npm:^5.1.1": - version: 5.1.8 - resolution: "ignore@npm:5.1.8" - checksum: b08e3d5b5d94eca13475f29a5d47d221060e9cdd7e38d7647088e29d90130669a970fecbc4cdb41b8fa295c6673740c729d3dc05dadc381f593efb42282cbf9f - languageName: node - linkType: hard - -"import-fresh@npm:^3.0.0, import-fresh@npm:^3.2.1": - version: 3.3.0 - resolution: "import-fresh@npm:3.3.0" - dependencies: - parent-module: ^1.0.0 - resolve-from: ^4.0.0 - checksum: 3ff624f00140850a2878eb7630d635daaad556cfa5a0e23191e9b65ab4fec8cc23f929f03bc9b3c4251b497a434f459bf3e7a8aa547a400ad140f431a1b0e4d6 - languageName: node - linkType: hard - -"imurmurhash@npm:^0.1.4": - version: 0.1.4 - resolution: "imurmurhash@npm:0.1.4" - checksum: 34d414d789286f6ef4d2b954c76c7df40dd7cabffef9b9959c8bd148677e98151f4fa5344aae2e3ad2b62308555ccbba3022e535a3e24288c9babb1308e35532 - languageName: node - linkType: hard - -"inflight@npm:^1.0.4": - version: 1.0.6 - resolution: "inflight@npm:1.0.6" - dependencies: - once: ^1.3.0 - wrappy: 1 - checksum: 17c53fc42cbe7f7f471d2bc41b97a0cde4b79a74d5ff59997d3f75210566fa278e17596da526d43de2bd07e222706240ce50e60097e54f2cde2e64cbbb372638 - languageName: node - linkType: hard - -"inherits@npm:2": - version: 2.0.4 - resolution: "inherits@npm:2.0.4" - checksum: 98426da247ddfc3dcd7d7daedd90c3ca32d5b08deca08949726f12d49232aef94772a07b36cf4ff833e105ae2ef931777f6de4a6dd8245a216b9299ad4a50bea - languageName: node - linkType: hard - -"internal-slot@npm:^1.0.3": - version: 1.0.3 - resolution: "internal-slot@npm:1.0.3" - dependencies: - get-intrinsic: ^1.1.0 - has: ^1.0.3 - side-channel: ^1.0.4 - checksum: 2465f832aa80c3740f2cfc5c75e74c727b4a45b8d80e295bb66dbb59435de536b9951b7f4d1a8075d5bb90054bd30ff22a37356a247fba3608987c7765569345 - languageName: node - linkType: hard - -"is-arrayish@npm:^0.2.1": - version: 0.2.1 - resolution: "is-arrayish@npm:0.2.1" - checksum: fc2bbe14dbcb27b490e63b7fbf0e3b0aae843e5e1fa96d79450bb9617797615a575c78c454ffc8e027c3ad50d63d83e85a7387784979dcd46686d2eb5f412db0 - languageName: node - linkType: hard - -"is-bigint@npm:^1.0.1": - version: 1.0.1 - resolution: "is-bigint@npm:1.0.1" - checksum: dd132ab80f389d6968315d491706c5dbb3f6c4bf35b64085d74895e7f3516123ab1bcf6a9a83a63cfede688f44550a08713ed37f3ae9153afe8d0cf569a8b956 - languageName: node - linkType: hard - -"is-boolean-object@npm:^1.1.0": - version: 1.1.0 - resolution: "is-boolean-object@npm:1.1.0" - dependencies: - call-bind: ^1.0.0 - checksum: 1d6047a022aa49cdf8580ac8b3d6d25da0d33a65ae00142bec2ba95c6c889de84693a0ef5acc9eabb59ba9e66fb473f47fa589ec22dd8e7ef8d88b6774e3adc6 - languageName: node - linkType: hard - -"is-callable@npm:^1.1.4, is-callable@npm:^1.2.3": - version: 1.2.3 - resolution: "is-callable@npm:1.2.3" - checksum: 8180a1c4e227e204e199ff355c4f24a80f74536898e16716583aa6a09167f2cceecc188cea750a2f3ae3b163577691595ae8d22bf7bb94b4bbb9fbdfea1bc5c3 - languageName: node - linkType: hard - -"is-core-module@npm:^2.2.0": - version: 2.2.0 - resolution: "is-core-module@npm:2.2.0" - dependencies: - has: ^1.0.3 - checksum: 2344744de98a3bc22e2bb30895f307d7889f09e963f9bcb1cc321788f508c8b463f75e0a9ca009eeeb8eb9465181b5c15f1ec9299a6bb6921cfbb2423892e0ba - languageName: node - linkType: hard - -"is-date-object@npm:^1.0.1": - version: 1.0.2 - resolution: "is-date-object@npm:1.0.2" - checksum: 0e322699464a99da638c8a583b74dfb791732b6bc9c102bc0b7ac6303d83c86b9935f19b8d2ed4de52092241190c8826b099cb31972dea49a99b755293c0b1cf - languageName: node - linkType: hard - -"is-extglob@npm:^2.1.1": - version: 2.1.1 - resolution: "is-extglob@npm:2.1.1" - checksum: ca623e2c56c893714a237aff645ec7caa8fea4d78868682af8d6803d7f0780323f8d566311e0dc6f942c886e81cbfa517597e48fcada7f3bf78a4d099eeecdd3 - languageName: node - linkType: hard - -"is-fullwidth-code-point@npm:^2.0.0": - version: 2.0.0 - resolution: "is-fullwidth-code-point@npm:2.0.0" - checksum: e1e5284f848ab6885665967cd768292a75022304d4401e78937a68f423047c29bfe87a43a9cdb67a3210fff7bcd5da51469122a0eff59b03261c379e58dbe921 - languageName: node - linkType: hard - -"is-glob@npm:^4.0.0, is-glob@npm:^4.0.1": - version: 4.0.1 - resolution: "is-glob@npm:4.0.1" - dependencies: - is-extglob: ^2.1.1 - checksum: 98cd4f715f0fb81da34aa6c8be4a5ef02d8cfac3ebc885153012abc2a0410df5a572f9d0393134fcba9192c7a845da96142c5f74a3c02787efe178ed798615e6 - languageName: node - linkType: hard - -"is-negative-zero@npm:^2.0.1": - version: 2.0.1 - resolution: "is-negative-zero@npm:2.0.1" - checksum: e2160af9a6fad7027bbd513e1efe9a99c780bb6af688e61e6b71084b5893f976241ca081e1ed8c18222d391ea3c1c0771cd23ab322be107150b66faf03d6ecbd - languageName: node - linkType: hard - -"is-number-object@npm:^1.0.4": - version: 1.0.4 - resolution: "is-number-object@npm:1.0.4" - checksum: 5bae52129f0e097485da25cbe89307dd46cf5ce7640edb6fcf40350d59c9f909039713d35fbeb0f1de1df817da6ec6e88aceca41b01e5ac989f6fdfc15c438a7 - languageName: node - linkType: hard - -"is-regex@npm:^1.1.2": - version: 1.1.2 - resolution: "is-regex@npm:1.1.2" - dependencies: - call-bind: ^1.0.2 - has-symbols: ^1.0.1 - checksum: 5e2f80f495f5297d1295730820a4be31f3848ca92357cfef1b2a61c09fe0fcd3f68c34f3042a5b81885e249cd50eac8efac472ad6da7ecb497bb2d7bad402a9a - languageName: node - linkType: hard - -"is-string@npm:^1.0.5": - version: 1.0.5 - resolution: "is-string@npm:1.0.5" - checksum: c64c791eb75935db9055291bc598edc22f03d3879b8a050b2955ba8087642d006338a1dedf7ac414c95f985c77c2d6fce655498d33c0df248fa92228a9945720 - languageName: node - linkType: hard - -"is-symbol@npm:^1.0.2, is-symbol@npm:^1.0.3": - version: 1.0.3 - resolution: "is-symbol@npm:1.0.3" - dependencies: - has-symbols: ^1.0.1 - checksum: 753aa0cf95069387521b110c6646df4e0b5cce76cf604521c26b4f5d30a997a95036ed5930c0cca9e850ac6fccb04de551cc95aab71df471ee88e04ed1a96f21 - languageName: node - linkType: hard - -"isarray@npm:^1.0.0": - version: 1.0.0 - resolution: "isarray@npm:1.0.0" - checksum: b0ff31a290e783f7b3fb73f2951ee7fc2946dc197b05f73577dc77f87dc3be2e0f66007bedf069123d4e5c4b691e7c89a241f6ca06f0c0f4765cdac5aa4b4047 - languageName: node - linkType: hard - -"isexe@npm:^2.0.0": - version: 2.0.0 - resolution: "isexe@npm:2.0.0" - checksum: 7b437980bb77881a146fba85cfbdf01edc2b148673e9c2722a1e49661fea73adf524430a80fdbfb8ce9f60d43224e682c657c45030482bd39e0c488fc29b4afe - languageName: node - linkType: hard - -"js-tokens@npm:^3.0.0 || ^4.0.0, js-tokens@npm:^4.0.0": - version: 4.0.0 - resolution: "js-tokens@npm:4.0.0" - checksum: 1fc4e4667ac2d972aba65148b9cbf9c17566b2394d3504238d8492bbd3e68f496c657eab06b26b40b17db5cac0a34d153a12130e2d2d2bb6dc2cdc8a4764eb1b - languageName: node - linkType: hard - -"js-yaml@npm:^3.13.1": - version: 3.14.1 - resolution: "js-yaml@npm:3.14.1" - dependencies: - argparse: ^1.0.7 - esprima: ^4.0.0 - bin: - js-yaml: bin/js-yaml.js - checksum: 46b61f889796a20d16b0b64580a01b6a02b2e45c1a2744906346da54d07e14cde764e887ab6d1512d8b2541c63711bd4b75859c28eb99605baf59fa173fc38c2 - languageName: node - linkType: hard - -"json-parse-better-errors@npm:^1.0.1": - version: 1.0.2 - resolution: "json-parse-better-errors@npm:1.0.2" - checksum: b4c4f0e43b43892af887db742b26f9aa6302b09cd5f6e655ead49fca9f47f3cdd300dcf98cf5218778262be51d7b29859221206fc98b87a1a61c5af7618dae89 - languageName: node - linkType: hard - -"json-schema-traverse@npm:^0.4.1": - version: 0.4.1 - resolution: "json-schema-traverse@npm:0.4.1" - checksum: 6f71bddba38aa043cf9c05ff9cf37158a6657909f1dd37032ba164b76923da47a17bb4592ee4f7f9c029dfaf26965b821ac214c1f991bb3bd038c9cfea2da50b - languageName: node - linkType: hard - -"json-stable-stringify-without-jsonify@npm:^1.0.1": - version: 1.0.1 - resolution: "json-stable-stringify-without-jsonify@npm:1.0.1" - checksum: a01b6c65413b2d0dd6797004ace6166bb6f8a0a2a77c742966021c74233cebe48de3c33223f003a9e8e4a241bb882fe92141e538e7e1dad58afd32649444e269 - languageName: node - linkType: hard - -"json5@npm:^1.0.1": - version: 1.0.1 - resolution: "json5@npm:1.0.1" - dependencies: - minimist: ^1.2.0 - bin: - json5: lib/cli.js - checksum: df41624f9f40bfacc546f779eef6d161a3312fbb6ec1dbd69f8c4388e9807af653b753371ab19b6d2bab22af2ca7dde62fe03c791596acf76915e1fc4ee6fd88 - languageName: node - linkType: hard - -"jsx-ast-utils@npm:^2.4.1 || ^3.0.0": - version: 3.2.0 - resolution: "jsx-ast-utils@npm:3.2.0" - dependencies: - array-includes: ^3.1.2 - object.assign: ^4.1.2 - checksum: 2a8033e63234d04e6ed77ac91222e2dff527f64cf70c10d2f26fda6f35dc9b78d5e3a43fc3d28df7fe0dab45294b94c9c90e58ab242ecf14e58cd39691ee0ed4 - languageName: node - linkType: hard - -"levn@npm:^0.4.1": - version: 0.4.1 - resolution: "levn@npm:0.4.1" - dependencies: - prelude-ls: ^1.2.1 - type-check: ~0.4.0 - checksum: 2f6ddfb0b956f2cb6b1608253a62b0c30e7392dd3c7b4cf284dfe2889b44d8385eaa81597646e253752c312a960ccb5e4d596968e476d5f6614f4ca60e5218e9 - languageName: node - linkType: hard - -"load-json-file@npm:^2.0.0": - version: 2.0.0 - resolution: "load-json-file@npm:2.0.0" - dependencies: - graceful-fs: ^4.1.2 - parse-json: ^2.2.0 - pify: ^2.0.0 - strip-bom: ^3.0.0 - checksum: c6ea93d36099dd6e778c6c018c9e184ad65d278a9538c2280f959b040b1a9a756d8856bdaf8a38c8f1454eca19bf4798ea59f79ccd8bb1c33aa8b7ecbe157f0c - languageName: node - linkType: hard - -"load-json-file@npm:^5.2.0": - version: 5.3.0 - resolution: "load-json-file@npm:5.3.0" - dependencies: - graceful-fs: ^4.1.15 - parse-json: ^4.0.0 - pify: ^4.0.1 - strip-bom: ^3.0.0 - type-fest: ^0.3.0 - checksum: c45b21cf66cb3a5948ef1ab12db94f9bf8d298c713014c8d9b6667062413916b57eb3c8ca365e1e84d422014c8c4d749ceb3e7335d2400e3e062e4009314eae7 - languageName: node - linkType: hard - -"locate-path@npm:^2.0.0": - version: 2.0.0 - resolution: "locate-path@npm:2.0.0" - dependencies: - p-locate: ^2.0.0 - path-exists: ^3.0.0 - checksum: ee5a888d686f8d555ebfa6c4f6f3b7c5cdfa5f382dee17e0b3fde7456fc68301ddb6a79790a412659d1e067f2f58fd74c683b203fc20368deaed45fb985b4fda - languageName: node - linkType: hard - -"locate-path@npm:^3.0.0": - version: 3.0.0 - resolution: "locate-path@npm:3.0.0" - dependencies: - p-locate: ^3.0.0 - path-exists: ^3.0.0 - checksum: 0b6bf0c1bb09021499f6198ed6a4ae367e8224e2493a74cc7bc5f4e6eca9ed880a5f7fdfb4d57b7e21d3e289c3abfe152cd510cacb1d03049f9d81d9a7d302ca - languageName: node - linkType: hard - -"lodash@npm:^4.17.14, lodash@npm:^4.17.19": - version: 4.17.21 - resolution: "lodash@npm:4.17.21" - checksum: 4983720b9abca930a4a46f18db163d7dad8dd00dbed6db0cc7b499b33b717cce69f80928b27bbb1ff2cbd3b19d251ee90669a8b5ea466072ca81c2ebe91e7468 - languageName: node - linkType: hard - -"loose-envify@npm:^1.4.0": - version: 1.4.0 - resolution: "loose-envify@npm:1.4.0" - dependencies: - js-tokens: ^3.0.0 || ^4.0.0 - bin: - loose-envify: cli.js - checksum: 5c3b47bbe5f597a3889fb001a3a98aaea2a3fafa48089c19034de1e0121bf57dbee609d184478514d74d5c5a7e9cfa3d846343455e5123b060040d46c39e91dc - languageName: node - linkType: hard - -"lru-cache@npm:^6.0.0": - version: 6.0.0 - resolution: "lru-cache@npm:6.0.0" - dependencies: - yallist: ^4.0.0 - checksum: b8b78353d2391c0f135cdc245c4744ad41c2efb1a6d98f31bc57a2cf48ebf02de96e4876657c3026673576bf1f1f61fc3fdd77ab00ad1ead737537bf17d8019d - languageName: node - linkType: hard - -"minimatch@npm:^3.0.4": - version: 3.0.4 - resolution: "minimatch@npm:3.0.4" - dependencies: - brace-expansion: ^1.1.7 - checksum: 47eab9263962cacd5733e274ecad2d8e54b0f8e124ba35ae69189e296058f634a4967b87a98954f86fa5c830ff177caf827ce0136d28717ed3232951fb4fae62 - languageName: node - linkType: hard - -"minimist@npm:^1.2.0, minimist@npm:^1.2.5": - version: 1.2.5 - resolution: "minimist@npm:1.2.5" - checksum: b77b8590147a4e217ff34266236bc39de23b52e6e33054076991ff674c7397a1380a7bde11111916f16f003a94aaa7e4f3d92595a32189644ff607fabc65a5b6 - languageName: node - linkType: hard - -"mkdirp@npm:^0.5.1": - version: 0.5.5 - resolution: "mkdirp@npm:0.5.5" - dependencies: - minimist: ^1.2.5 - bin: - mkdirp: bin/cmd.js - checksum: 9dd9792e891927b14ca02226dbe1daeb717b9517a001620d5e2658bbc72c5e4f06887b6cbcbb60595fa5a56e701073cf250f1ed69c1988a6b89faf9fd6a4d049 - languageName: node - linkType: hard - -"ms@npm:2.0.0": - version: 2.0.0 - resolution: "ms@npm:2.0.0" - checksum: 1a230340cc7f322fbe916783d8c8d60455407c6b7fb7f901d6ee34eb272402302c5c7f070a97b8531245cbb4ca6a0a623f6a128d7e5a5440cefa2c669c0b35bb - languageName: node - linkType: hard - -"ms@npm:2.1.2": - version: 2.1.2 - resolution: "ms@npm:2.1.2" - checksum: 9b65fb709bc30c0c07289dcbdb61ca032acbb9ea5698b55fa62e2cebb04c5953f1876a1f3f7f4bc2e91d4bf4d86003f3e207c3bc6ee2f716f99827e62389cd0e - languageName: node - linkType: hard - -"natural-compare@npm:^1.4.0": - version: 1.4.0 - resolution: "natural-compare@npm:1.4.0" - checksum: 2daf93d9bb516eddb06e2e80657a605af2e494d47c65d090ba43691aaffbc41f520840f1c9d3b7b641977af950217a4ab6ffb85bafcd5dfa8ba6fe4e68c43b53 - languageName: node - linkType: hard - -"normalize-package-data@npm:^2.3.2": - version: 2.5.0 - resolution: "normalize-package-data@npm:2.5.0" - dependencies: - hosted-git-info: ^2.1.4 - resolve: ^1.10.0 - semver: 2 || 3 || 4 || 5 - validate-npm-package-license: ^3.0.1 - checksum: 97d4d6b061cab51425ddb05c38d126d7a1a2a6f2c9949bef2b5ad7ef19c005df12099ea442e4cb09190929b7770008f94f87b10342a66f739acf92a7ebb9d9f2 - languageName: node - linkType: hard - -"object-assign@npm:^4.1.1": - version: 4.1.1 - resolution: "object-assign@npm:4.1.1" - checksum: 66cf021898fc1b13ea573ea8635fbd5a76533f50cecbc2fcd5eee1e8029af41bcebe7023788b6d0e06cbe4401ecea075d972f78ec74467cdc571a0f1a4d1a081 - languageName: node - linkType: hard - -"object-inspect@npm:^1.9.0": - version: 1.9.0 - resolution: "object-inspect@npm:1.9.0" - checksum: 63b412167d716e332b3233090a9e8cc7951479a6971629fb8a3d00135a2329136c697fbd2f56e48bb132928f01bd0f8c5fe2d7386222f217228ca697b8c3932a - languageName: node - linkType: hard - -"object-keys@npm:^1.0.12, object-keys@npm:^1.1.1": - version: 1.1.1 - resolution: "object-keys@npm:1.1.1" - checksum: 30d72d768b7f3f42144cee517b80e70c40cf39bb76f100557ffac42779613c591780135c54d8133894a78d2c0ae817e24a5891484722c6019a5cd5b58c745c66 - languageName: node - linkType: hard - -"object.assign@npm:^4.1.2": - version: 4.1.2 - resolution: "object.assign@npm:4.1.2" - dependencies: - call-bind: ^1.0.0 - define-properties: ^1.1.3 - has-symbols: ^1.0.1 - object-keys: ^1.1.1 - checksum: a5855cc6db3f64606c41ceb97cb9847e667d8240889d771d65638244be1d35c2e2ccb5762f437bb76abf4e98ab4634a9d302380398121cee288a44dce5028f54 - languageName: node - linkType: hard - -"object.entries@npm:^1.1.2": - version: 1.1.3 - resolution: "object.entries@npm:1.1.3" - dependencies: - call-bind: ^1.0.0 - define-properties: ^1.1.3 - es-abstract: ^1.18.0-next.1 - has: ^1.0.3 - checksum: 89eec6d43bf8440dfd800ad76486d76a900ff7e3e20b560abb4cba8494bdc8524af8cf49a897739da776fe434f8091569b2422333075378f9fd5a50d599601eb - languageName: node - linkType: hard - -"object.fromentries@npm:^2.0.2": - version: 2.0.4 - resolution: "object.fromentries@npm:2.0.4" - dependencies: - call-bind: ^1.0.2 - define-properties: ^1.1.3 - es-abstract: ^1.18.0-next.2 - has: ^1.0.3 - checksum: 9e02d109f6f63dda78715e43fcbd80941491e56ee771a5d21da93e271859f43b0db15e26e0b945989a6a6ee0ba480ca57b047cd331a71e4c4251d44517e0649c - languageName: node - linkType: hard - -"object.values@npm:^1.1.1": - version: 1.1.3 - resolution: "object.values@npm:1.1.3" - dependencies: - call-bind: ^1.0.2 - define-properties: ^1.1.3 - es-abstract: ^1.18.0-next.2 - has: ^1.0.3 - checksum: 31111fe8b8dfe7c3326ae8729eae542dc32d5705339b9b63d89d4a2f766641bfe8989744bd4771c65a7ca0dff281800e99640262c2e82daa97079143a86b3e0b - languageName: node - linkType: hard - -"once@npm:^1.3.0": - version: 1.4.0 - resolution: "once@npm:1.4.0" - dependencies: - wrappy: 1 - checksum: 57afc246536cf6494437f982b26475f22bee860f8b77ce8eb1543f42a8bffe04b2c66ddfea9a16cb25ccb80943f8ee4fc639367ef97b7a6a4f2672eb573963f5 - languageName: node - linkType: hard - -"optionator@npm:^0.9.1": - version: 0.9.1 - resolution: "optionator@npm:0.9.1" - dependencies: - deep-is: ^0.1.3 - fast-levenshtein: ^2.0.6 - levn: ^0.4.1 - prelude-ls: ^1.2.1 - type-check: ^0.4.0 - word-wrap: ^1.2.3 - checksum: bdf5683f986d00e173e6034837b7b6a9e68c7e1a37d7684b240adf1758db9076cfb04c9f64be29327881bb06c5017afb8b65012c5f02d07b180e9f6f42595ffd - languageName: node - linkType: hard - -"p-limit@npm:^1.1.0": - version: 1.3.0 - resolution: "p-limit@npm:1.3.0" - dependencies: - p-try: ^1.0.0 - checksum: 579cbd3d6c606058aa624c464e2cb3c4b56d04ed4cbafdb705633cbe62ba36d77ba2c4289023335ba382f4fbf32c15709465eea18a0e1547c5ebc4b887f2a7da - languageName: node - linkType: hard - -"p-limit@npm:^2.0.0": - version: 2.3.0 - resolution: "p-limit@npm:2.3.0" - dependencies: - p-try: ^2.0.0 - checksum: 5f20492a25c5f93fca2930dbbf41fa1bee46ef70eaa6b49ad1f7b963f309e599bc40507e0a3a531eee4bcd10fec4dd4a63291d0e3b2d84ac97d7403d43d271a9 - languageName: node - linkType: hard - -"p-locate@npm:^2.0.0": - version: 2.0.0 - resolution: "p-locate@npm:2.0.0" - dependencies: - p-limit: ^1.1.0 - checksum: b6dabbd855fba9bfa74b77882f96d0eac6c25d9966e61ab0ed7bf3d19f2e3b766f290ded1aada1ac4ce2627217b00342cf7a1d36482bada59ba6789be412dad7 - languageName: node - linkType: hard - -"p-locate@npm:^3.0.0": - version: 3.0.0 - resolution: "p-locate@npm:3.0.0" - dependencies: - p-limit: ^2.0.0 - checksum: 3ee9e3ed0b1b543f8148ef0981d33013d82a21c338b117a2d15650456f8dc888c19eb8a98484e7e159276c3ad9219c3e2a00b63228cab46bf29aeaaae096b1d6 - languageName: node - linkType: hard - -"p-try@npm:^1.0.0": - version: 1.0.0 - resolution: "p-try@npm:1.0.0" - checksum: 85739d77b3e9f6a52a8545f1adc53621fb5df4d6ef9b59a3f54f3f3159b45c4100d4e63128a1e790e9ff8ff8b86213ace314ff6d2d327c3edcceea18891baa42 - languageName: node - linkType: hard - -"p-try@npm:^2.0.0": - version: 2.2.0 - resolution: "p-try@npm:2.2.0" - checksum: 20983f3765466c1ab617ed153cb53b70ac5df828d854a3334d185e20b37f436e9096f12bc1b7fc96d8908dc927a3685172d3d89e755774f57b7103460c54dcc5 - languageName: node - linkType: hard - -"parent-module@npm:^1.0.0": - version: 1.0.1 - resolution: "parent-module@npm:1.0.1" - dependencies: - callsites: ^3.0.0 - checksum: 58714b9699f8e84340aaf0781b7cbd82f1c357f6ce9c035c151d0e8c1e9b869c51b95b680882f0d21b4751e817a6c936d4bb2952a1a1d9d9fb27e5a84baec2aa - languageName: node - linkType: hard - -"parse-json@npm:^2.2.0": - version: 2.2.0 - resolution: "parse-json@npm:2.2.0" - dependencies: - error-ex: ^1.2.0 - checksum: 920582196a8edebb3d3c4623b2f057987218272b35ae4d2d310c00bc1bd7e89b87c79358d7e009d54f047ca2eea82eab8d7e1b14e1f7cbbb345ef29fcda29731 - languageName: node - linkType: hard - -"parse-json@npm:^4.0.0": - version: 4.0.0 - resolution: "parse-json@npm:4.0.0" - dependencies: - error-ex: ^1.3.1 - json-parse-better-errors: ^1.0.1 - checksum: fa9d23708f562c447f2077c6007938334a16e772c5a9b25a6eb1853d792bc34560b483bb6079143040bc89e5476288dd2edd5a60024722986e3e434d326218c9 - languageName: node - linkType: hard - -"path-exists@npm:^3.0.0": - version: 3.0.0 - resolution: "path-exists@npm:3.0.0" - checksum: 09683e92bafb5657838217cce04e4f2f0530c274bc357c995c3231461030566e9f322b9a8bcc1ea810996e250d9a293ca36dd78dbdd6bfbee42e85a94772d6d5 - languageName: node - linkType: hard - -"path-is-absolute@npm:^1.0.0": - version: 1.0.1 - resolution: "path-is-absolute@npm:1.0.1" - checksum: 907e1e3e6ac0aef6e65adffd75b3892191d76a5b94c5cf26b43667c4240531d11872ca6979c209b2e5e1609f7f579d02f64ba9936b48bb59d36cc529f0d965ed - languageName: node - linkType: hard - -"path-key@npm:^3.1.0": - version: 3.1.1 - resolution: "path-key@npm:3.1.1" - checksum: e44aa3ca9faed0440994883050143b1214fffb907bf3a7bbdba15dc84f60821617c0d84e4cc74e1d84e9274003da50427f54d739b0b47636bcbaff4ec71b9b86 - languageName: node - linkType: hard - -"path-parse@npm:^1.0.6": - version: 1.0.6 - resolution: "path-parse@npm:1.0.6" - checksum: 2eee4b93fb3ae13600e3fca18390d9933bbbcf725a624f6b8df020d87515a74872ff6c58072190d6dc75a5584a683dc6ae5c385ad4e4f4efb6e66af040d56c67 - languageName: node - linkType: hard - -"path-type@npm:^2.0.0": - version: 2.0.0 - resolution: "path-type@npm:2.0.0" - dependencies: - pify: ^2.0.0 - checksum: d028f828dffe48a0062dc4370d5118a0c45f5fb075b013a1dfb13eadd1426eba0c8c2a13fa78f19fc4fd8771ef2012e9d062f8f970c8e56df36d4fbbe5073b26 - languageName: node - linkType: hard - -"pify@npm:^2.0.0": - version: 2.3.0 - resolution: "pify@npm:2.3.0" - checksum: d5758aa570bbd5969c62b5f745065006827ef4859b32af302e3df2bb5978e6c1e50c2360d7ffefa102e451084f4530115c84570c185ba5153ee9871c977fe278 - languageName: node - linkType: hard - -"pify@npm:^4.0.1": - version: 4.0.1 - resolution: "pify@npm:4.0.1" - checksum: 786486a8c94a7e1980ea56c59dcc05ebf0793740b71df9b9f273e48032e6301c5ecc5cc237c5a9ff45b13db27678b4d71aa37a2777bc11473c1310718b648e98 - languageName: node - linkType: hard - -"pkg-conf@npm:^3.1.0": - version: 3.1.0 - resolution: "pkg-conf@npm:3.1.0" - dependencies: - find-up: ^3.0.0 - load-json-file: ^5.2.0 - checksum: e0567c5b0f7d7abe3d9f87650436ee8f7175b6ed55027e72fc186fed124b05b2563233a5ad4d2f0468730439687c8e75128fa220b4ba0d05e186149b8788337e - languageName: node - linkType: hard - -"pkg-dir@npm:^2.0.0": - version: 2.0.0 - resolution: "pkg-dir@npm:2.0.0" - dependencies: - find-up: ^2.1.0 - checksum: f8ae3a151714c61283aeb24385b10355a238732fab822a560145c670c21350da2024f01918231222bcdfce53ec5d69056681be2c2cffe3f3a06e462b9ef2ac29 - languageName: node - linkType: hard - -"prelude-ls@npm:^1.2.1": - version: 1.2.1 - resolution: "prelude-ls@npm:1.2.1" - checksum: bc1649f521e8928cde0e1b349b224de2e6f00b71361a4a44f2e4a615342b6e1ae30366c32d26412dabe74d999a40f79c0ae044ae6b17cf19af935e74d12ea4fa - languageName: node - linkType: hard - -"progress@npm:^2.0.0": - version: 2.0.3 - resolution: "progress@npm:2.0.3" - checksum: c46ef5a1de4d527dfd32fe56a7df0c1c8b420a4c02617196813bf7f10ac7c2a929afc265d44fdd68f5c439a7e7cb3d70d569716c82d6b4148ec72089860a1312 - languageName: node - linkType: hard - -"prop-types@npm:^15.7.2": - version: 15.7.2 - resolution: "prop-types@npm:15.7.2" - dependencies: - loose-envify: ^1.4.0 - object-assign: ^4.1.1 - react-is: ^16.8.1 - checksum: a440dd406c5cf53bf39f3e898d2c65178511d34ca3c8c789b30c177992408b9e4273969726b274719aa69ccce5ab34b2fd8caa60b90f23cd2e910cdcf682de52 - languageName: node - linkType: hard - -"punycode@npm:^2.1.0": - version: 2.1.1 - resolution: "punycode@npm:2.1.1" - checksum: 0202dc191cb35bfd88870ac99a1e824b03486d4cee20b543ef337a6dee8d8b11017da32a3e4c40b69b19976e982c030b62bd72bba42884acb691bc5ef91354c8 - languageName: node - linkType: hard - -"react-is@npm:^16.8.1": - version: 16.13.1 - resolution: "react-is@npm:16.13.1" - checksum: 11bcf1267a314a522615f626f3ce3727a3a24cdbf61c4d452add3550a7875326669631326cfb1ba3e92b6f72244c32ffecf93ad21c0cad8455d3e169d0e3f060 - languageName: node - linkType: hard - -"read-pkg-up@npm:^2.0.0": - version: 2.0.0 - resolution: "read-pkg-up@npm:2.0.0" - dependencies: - find-up: ^2.0.0 - read-pkg: ^2.0.0 - checksum: f35e4cb4577b994fc9497886672c748de766ab034e24f029111b6bbbfe757b2e27b6d2b82a28a38f45d9d89ea8a9b1d3c04854e5f991d5deed48f4c9ff7baeb9 - languageName: node - linkType: hard - -"read-pkg@npm:^2.0.0": - version: 2.0.0 - resolution: "read-pkg@npm:2.0.0" - dependencies: - load-json-file: ^2.0.0 - normalize-package-data: ^2.3.2 - path-type: ^2.0.0 - checksum: ddf911317fba54abb447b1d76dd1785c37e1360f7b1e39d83201f6f3807572391ab7392f11727a9c4d90600ebc6616d22e72514d2291688c89ebd440148840b4 - languageName: node - linkType: hard - -"regexp.prototype.flags@npm:^1.3.1": - version: 1.3.1 - resolution: "regexp.prototype.flags@npm:1.3.1" - dependencies: - call-bind: ^1.0.2 - define-properties: ^1.1.3 - checksum: 967e462a83cdfd6f226aa9337bda6f739e3fba72a49f3d3f4ed16b60d5a811ba576ef22f01e37b9230022ba715c6207c082ca117160b304b6503e4a6557628f5 - languageName: node - linkType: hard - -"regexpp@npm:^3.0.0, regexpp@npm:^3.1.0": - version: 3.1.0 - resolution: "regexpp@npm:3.1.0" - checksum: 69d0ce6b449cf35d3732d6341a1e70850360ffc619f8eef10629871c462e614853fffb80d3f00fc17cd0bb5b8f34b0cde5be4b434e72c0eb3fbba2360c8b5ac4 - languageName: node - linkType: hard - -"resolve-from@npm:^4.0.0": - version: 4.0.0 - resolution: "resolve-from@npm:4.0.0" - checksum: 87a4357c0c1c2d165012ec04a3b2aa58931c0c0be257890806760b627bad36c9bceb6f9b2a3726f8570c67f2c9ff3ecc9507fe65cc3ad8d45cdab015245c649f - languageName: node - linkType: hard - -"resolve@^1.10.0, resolve@^1.10.1, resolve@^1.13.1, resolve@^1.17.0, resolve@^1.18.1": - version: 1.20.0 - resolution: "resolve@npm:1.20.0" - dependencies: - is-core-module: ^2.2.0 - path-parse: ^1.0.6 - checksum: 0f5206d454b30e74d9b2d575b5f8aedf443c4d8b90b84cdf79474ade29bb459075220da3127b682896872a16022ed65cc4db09e0f23849654144d3d75c65cd1b - languageName: node - linkType: hard - -"resolve@patch:resolve@^1.10.0#builtin, resolve@patch:resolve@^1.10.1#builtin, resolve@patch:resolve@^1.13.1#builtin, resolve@patch:resolve@^1.17.0#builtin, resolve@patch:resolve@^1.18.1#builtin": - version: 1.20.0 - resolution: "resolve@patch:resolve@npm%3A1.20.0#builtin::version=1.20.0&hash=3388aa" - dependencies: - is-core-module: ^2.2.0 - path-parse: ^1.0.6 - checksum: c4a515b76026806b5b26513fc7bdb80458c532bc91c02ef45ac928d1025585f93bec0b904be39c02131118a37ff7e3f9258f1526850b025d2ec0948bb5fd03d0 - languageName: node - linkType: hard - -"rimraf@npm:2.6.3": - version: 2.6.3 - resolution: "rimraf@npm:2.6.3" - dependencies: - glob: ^7.1.3 - bin: - rimraf: ./bin.js - checksum: c9ce1854f19327606934558f4729b0f7aa7b9f1a3e7ca292d56261cce1074e20b0a0b16689166da6d8ab24ed9c30f7c71fba0df38e1d37f0233b6f48307c5c7a - languageName: node - linkType: hard - -"semistandard@npm:^16.0.0": - version: 16.0.0 - resolution: "semistandard@npm:16.0.0" - dependencies: - eslint: ~7.12.1 - eslint-config-semistandard: 15.0.1 - eslint-config-standard: 16.0.0 - eslint-config-standard-jsx: 10.0.0 - eslint-plugin-import: ~2.22.1 - eslint-plugin-node: ~11.1.0 - eslint-plugin-promise: ~4.2.1 - eslint-plugin-react: ~7.21.5 - eslint-plugin-standard: ~4.0.2 - standard-engine: ^14.0.0 - bin: - semistandard: bin/cmd.js - checksum: a9b525a9747327561a1cf81cb4d0b9a9589753cf92c61e7086e420d985c1d6c2cf52ae2807c22f04477f9aeb8d7c0fd4f9e47bfd80ec329f12b5bec4feebc884 - languageName: node - linkType: hard - -"semver@npm:2 || 3 || 4 || 5": - version: 5.7.1 - resolution: "semver@npm:5.7.1" - bin: - semver: ./bin/semver - checksum: 06ff0ed753ebf741b7602be8faad620d6e160a2cb3f61019d00d919c8bca141638aa23c34da779b8595afdc9faa3678bfbb5f60366b6a4f65f98cf86605bbcdb - languageName: node - linkType: hard - -"semver@npm:^6.1.0": - version: 6.3.0 - resolution: "semver@npm:6.3.0" - bin: - semver: ./bin/semver.js - checksum: f0d155c06a67cc7e500c92d929339f1c6efd4ce9fe398aee6acc00a2333489cca0f5b4e76ee7292beba237fcca4b5a3d4a6153471f105f56299801bdab37289f - languageName: node - linkType: hard - -"semver@npm:^7.2.1": - version: 7.3.4 - resolution: "semver@npm:7.3.4" - dependencies: - lru-cache: ^6.0.0 - bin: - semver: bin/semver.js - checksum: f2c7f9aeb976d1484b2f39aa7afc8332a1d21fd32ca4a6fbf650e1423455ebf3e7029f6e2e7ba0cd71935b85942521f1ec25b6cc2c031b953c8ca4ff2d7a823d - languageName: node - linkType: hard - -"shebang-command@npm:^2.0.0": - version: 2.0.0 - resolution: "shebang-command@npm:2.0.0" - dependencies: - shebang-regex: ^3.0.0 - checksum: 85aa394d8cedeedf2e03524d6defef67a2b07d3a17d7ee50d4281d62d3fca898f26ebe7aa7bf674d51b80f197aa1d346bc1a10e8efb04377b534f4322c621012 - languageName: node - linkType: hard - -"shebang-regex@npm:^3.0.0": - version: 3.0.0 - resolution: "shebang-regex@npm:3.0.0" - checksum: ea18044ffaf18129ced5a246660a9171a7dff98999aaa9de8abb237d8a7711d8a1f76e16881399994ee429156717ce1c6a50c665bb18a4d55a7f80b9125b1f7d - languageName: node - linkType: hard - -"side-channel@npm:^1.0.4": - version: 1.0.4 - resolution: "side-channel@npm:1.0.4" - dependencies: - call-bind: ^1.0.0 - get-intrinsic: ^1.0.2 - object-inspect: ^1.9.0 - checksum: 84258ce3edb1ad35810ca17eccd52fd504b5d4da59447a6829cfd1ae8e3cff97b7df2a14f9a45b7aaa3b507ded95626cf20a500735d3b797e9ffb1eba3cfa9e7 - languageName: node - linkType: hard - -"slice-ansi@npm:^2.1.0": - version: 2.1.0 - resolution: "slice-ansi@npm:2.1.0" - dependencies: - ansi-styles: ^3.2.0 - astral-regex: ^1.0.0 - is-fullwidth-code-point: ^2.0.0 - checksum: 7578393cac91c28f8cb5fa5df36b826ad62c9e66313d2547770db8401570fa8f4aa20cd84ef9244fa054d8e9cc6bfc02578784bb89b238d384b99f2728a35a6d - languageName: node - linkType: hard - -"spdx-correct@npm:^3.0.0": - version: 3.1.1 - resolution: "spdx-correct@npm:3.1.1" - dependencies: - spdx-expression-parse: ^3.0.0 - spdx-license-ids: ^3.0.0 - checksum: f3413eb225ef9f13aa2ec05230ff7669bffad055a7f62ec85164dd27f00a9f1e19880554a8fa5350fc434764ff895836c207f98813511a0180b0e929581bfe01 - languageName: node - linkType: hard - -"spdx-exceptions@npm:^2.1.0": - version: 2.3.0 - resolution: "spdx-exceptions@npm:2.3.0" - checksum: 3cbd2498897dc384158666a9dd7435e3b42ece5da42fd967b218b790e248381d001ec77a676d13d1f4e8da317d97b7bc0ebf4fff37bfbb95923d49b024030c96 - languageName: node - linkType: hard - -"spdx-expression-parse@npm:^3.0.0": - version: 3.0.1 - resolution: "spdx-expression-parse@npm:3.0.1" - dependencies: - spdx-exceptions: ^2.1.0 - spdx-license-ids: ^3.0.0 - checksum: f0211cada3fa7cd9db2243143fb0e66e28a46d72d8268f38ad2196aac49408d87892cda6e5600d43d6b05ed2707cb2f4148deb27b092aafabc50a67038f4cbf5 - languageName: node - linkType: hard - -"spdx-license-ids@npm:^3.0.0": - version: 3.0.7 - resolution: "spdx-license-ids@npm:3.0.7" - checksum: 21e38ec5dd970643f78d37700b6c6ebd42d68c0e4618db914a56cabd2fe4cc1608404ce6abc7535d5165c6555560e821553d06edf6af6ae439617883cf932c0e - languageName: node - linkType: hard - -"sprintf-js@npm:~1.0.2": - version: 1.0.3 - resolution: "sprintf-js@npm:1.0.3" - checksum: 51df1bce9e577287f56822d79ac5bd94f6c634fccf193895f2a1d2db2e975b6aa7bc97afae9cf11d49b7c37fe4afc188ff5c4878be91f2c86eabd11c5df8b62c - languageName: node - linkType: hard - -"standard-engine@npm:^14.0.0": - version: 14.0.1 - resolution: "standard-engine@npm:14.0.1" - dependencies: - get-stdin: ^8.0.0 - minimist: ^1.2.5 - pkg-conf: ^3.1.0 - xdg-basedir: ^4.0.0 - checksum: 671824cd48a890936e2815757284c138da3aa49b9743f88726add53866b67fc97200e6a7f8ce72dfeb8111208feaefcdd05b6d3c5ddc1b38f4221589f9f8eb67 - languageName: node - linkType: hard - -"string-width@npm:^3.0.0": - version: 3.1.0 - resolution: "string-width@npm:3.1.0" - dependencies: - emoji-regex: ^7.0.1 - is-fullwidth-code-point: ^2.0.0 - strip-ansi: ^5.1.0 - checksum: 54c5d1842dc122d8e0251ad50e00e91c06368f1aca44f41a67cd5ce013c4ba8f5a26f1b7f72a3e1644f38c62092a82c86b646aff514073894faf84b9564a38a0 - languageName: node - linkType: hard - -"string.prototype.matchall@npm:^4.0.2": - version: 4.0.4 - resolution: "string.prototype.matchall@npm:4.0.4" - dependencies: - call-bind: ^1.0.2 - define-properties: ^1.1.3 - es-abstract: ^1.18.0-next.2 - has-symbols: ^1.0.1 - internal-slot: ^1.0.3 - regexp.prototype.flags: ^1.3.1 - side-channel: ^1.0.4 - checksum: e19b26a14adc6f6957248ae0ce3a054ddfb5b783b7528a9dd7ee2635d95a977486805c7b2182dca17fd60e1a40e1be1e02f0a9fee32e19c29d2f1850564d96d1 - languageName: node - linkType: hard - -"string.prototype.trimend@npm:^1.0.4": - version: 1.0.4 - resolution: "string.prototype.trimend@npm:1.0.4" - dependencies: - call-bind: ^1.0.2 - define-properties: ^1.1.3 - checksum: ea8793bee1104362587e6a0fab2cb48e76548423d8ac95847284f9f6ef6a11338cf47114e8ec1c2a9519cce55cfa8d19fc8e26413937c3e804a768ec43ebe38e - languageName: node - linkType: hard - -"string.prototype.trimstart@npm:^1.0.4": - version: 1.0.4 - resolution: "string.prototype.trimstart@npm:1.0.4" - dependencies: - call-bind: ^1.0.2 - define-properties: ^1.1.3 - checksum: dd2c994af9b9194c7ce9d94e30b8f8bbe30ec95ada94534a71d63df2964a200c8d2264378252a5047a5f1cf805e8216911d78d16b22d5db7b0abcdbbb2d24b4a - languageName: node - linkType: hard - -"strip-ansi@npm:^5.1.0": - version: 5.2.0 - resolution: "strip-ansi@npm:5.2.0" - dependencies: - ansi-regex: ^4.1.0 - checksum: 44a0d0d354f5f7b15f83323879a9112ea746daae7bef0b68238a27626ee757d9a04ce6590433841e14b325e8e7c5d62b8442885e50497e21b7cbca6da40d54ea - languageName: node - linkType: hard - -"strip-ansi@npm:^6.0.0": - version: 6.0.0 - resolution: "strip-ansi@npm:6.0.0" - dependencies: - ansi-regex: ^5.0.0 - checksum: 10568c91cadbef182a807c38dfa718dce15a35b12fcc97b96b6b2029d0508ef66ca93fabddeb49482d9b027495d1e18591858e80f27ad26861c4967c60fd207f - languageName: node - linkType: hard - -"strip-bom@npm:^3.0.0": - version: 3.0.0 - resolution: "strip-bom@npm:3.0.0" - checksum: 361dd1dd08ae626940061570d20bcf73909d0459734b8880eb3d14176aa28f41cf85d13af036c323ce739e04ef3930a71b516950c5985b318bae3757ecb2974c - languageName: node - linkType: hard - -"strip-json-comments@npm:^3.1.0, strip-json-comments@npm:^3.1.1": - version: 3.1.1 - resolution: "strip-json-comments@npm:3.1.1" - checksum: f16719ce25abc58a55ef82b1c27f541dcfa5d544f17158f62d10be21ff9bd22fde45a53c592b29d80ad3c97ccb67b7451c4833913fdaeadb508a40f5e0a9c206 - languageName: node - linkType: hard - -"supports-color@npm:^5.3.0": - version: 5.5.0 - resolution: "supports-color@npm:5.5.0" - dependencies: - has-flag: ^3.0.0 - checksum: edacee6425498440744c418be94b0660181aad2a1828bcf2be85c42bd385da2fd8b2b358d9b62b0c5b03ff5cd3e992458d7b8f879d9fb42f2201fe05a4848a29 - languageName: node - linkType: hard - -"supports-color@npm:^7.1.0": - version: 7.2.0 - resolution: "supports-color@npm:7.2.0" - dependencies: - has-flag: ^4.0.0 - checksum: 8e57067c39216f3c2ffce7cc14ca934d54746192571203aa9c9922d97d2d55cc1bdaa9e41a11f91e620670b5a74ebdec6b548a885d8cc2dea7cab59e21416029 - languageName: node - linkType: hard - -"table@npm:^5.2.3": - version: 5.4.6 - resolution: "table@npm:5.4.6" - dependencies: - ajv: ^6.10.2 - lodash: ^4.17.14 - slice-ansi: ^2.1.0 - string-width: ^3.0.0 - checksum: 38877a196c0a57b955e4965fa3ff1cede38649b6e1f6286aa5435579dfd01663fdf8d19c87510e67a79474d75ae0144a0819f2054d654c45d7f525270aafe56b - languageName: node - linkType: hard - -"text-table@npm:^0.2.0": - version: 0.2.0 - resolution: "text-table@npm:0.2.0" - checksum: 373904ce70524ba11ec7e1905c44fb92671132d5e0b0aba2fb48057161f8bf9cbf7f6178f0adf31810150cf44fb52c7b912dc722bff3fddf9688378596dbeb56 - languageName: node - linkType: hard - -"tsconfig-paths@npm:^3.9.0": - version: 3.9.0 - resolution: "tsconfig-paths@npm:3.9.0" - dependencies: - "@types/json5": ^0.0.29 - json5: ^1.0.1 - minimist: ^1.2.0 - strip-bom: ^3.0.0 - checksum: 5383ba626b3ac70e08094b9dfd1e30ce82878407b6c8db8cd84279cc7c7340d5f53f67dbeb8174a233c082a068322a6b00ec8514b96d9a80a453e0476dc116d2 - languageName: node - linkType: hard - -"type-check@npm:^0.4.0, type-check@npm:~0.4.0": - version: 0.4.0 - resolution: "type-check@npm:0.4.0" - dependencies: - prelude-ls: ^1.2.1 - checksum: 6c2e1ce339567e122504f0c729cfa35d877fb2da293b99110f0819eca81e6ed8d3ba9bb36c0bc0ee4904d5340dbe678f8642a395c1c67b1d0f69f081efb47f4a - languageName: node - linkType: hard - -"type-fest@npm:^0.3.0": - version: 0.3.1 - resolution: "type-fest@npm:0.3.1" - checksum: 508923061144ff7ebc69d4f49bc812c7b8a81c633d10e89191092efb5746531ee6c4dd912db1447e954a766186ed48eee0dcfa53047c55a7646076a76640ff43 - languageName: node - linkType: hard - -"type-fest@npm:^0.8.1": - version: 0.8.1 - resolution: "type-fest@npm:0.8.1" - checksum: f8c4b4249f52e8bea7a4fc55b3653c96c2d547240e4c772e001d02b7cc38b8c3eb493ab9fbe985a76a203cd1aa7044776b728a71ba12bf36e7131f989597885b - languageName: node - linkType: hard - -"unbox-primitive@npm:^1.0.0": - version: 1.0.0 - resolution: "unbox-primitive@npm:1.0.0" - dependencies: - function-bind: ^1.1.1 - has-bigints: ^1.0.0 - has-symbols: ^1.0.0 - which-boxed-primitive: ^1.0.1 - checksum: 25e82f99bb40981f30615644305c757ecefff43d2ef2ac1b80e24f304f3002cd637eecb672bdd07f5fb858a265d96a4b2e983c714cba65498715acf7af23e86b - languageName: node - linkType: hard - -"uri-js@npm:^4.2.2": - version: 4.4.1 - resolution: "uri-js@npm:4.4.1" - dependencies: - punycode: ^2.1.0 - checksum: 7d8ae8e2d7b82480d7d337f3e53c9a89ffdc7ebb1c31f212da3df6349f2fd1e6a4361f5fb27369ecab33fa37aa85edc53aec6eb7c9a7c3207a9e0944e8c48802 - languageName: node - linkType: hard - -"v8-compile-cache@npm:^2.0.3": - version: 2.3.0 - resolution: "v8-compile-cache@npm:2.3.0" - checksum: b56f83d9ff14187562badc4955dadeef53ff3abde478ce60759539dd8d5472a91fce9db6083fc2450e54cef6f2110c1a28d8c12162dbf575a6cfcb846986904b - languageName: node - linkType: hard - -"validate-npm-package-license@npm:^3.0.1": - version: 3.0.4 - resolution: "validate-npm-package-license@npm:3.0.4" - dependencies: - spdx-correct: ^3.0.0 - spdx-expression-parse: ^3.0.0 - checksum: 940899bd4eacfa012ceecb10a5814ba0e8103da5243aa74d0d62f1f8a405efcd23e034fb7193e2d05b392870c53aabcb1f66439b062075cdcb28bc5d562a8ff6 - languageName: node - linkType: hard - -"which-boxed-primitive@npm:^1.0.1": - version: 1.0.2 - resolution: "which-boxed-primitive@npm:1.0.2" - dependencies: - is-bigint: ^1.0.1 - is-boolean-object: ^1.1.0 - is-number-object: ^1.0.4 - is-string: ^1.0.5 - is-symbol: ^1.0.3 - checksum: 771ef43357afbba9febf2da4867b2971ada0a5126227f9b7926751525e3721f7f5f3722f8c60af67881714d9a82a98ed686f1768490cfb2cd40518df5f2e056e - languageName: node - linkType: hard - -"which@npm:^2.0.1": - version: 2.0.2 - resolution: "which@npm:2.0.2" - dependencies: - isexe: ^2.0.0 - bin: - node-which: ./bin/node-which - checksum: ea9b1db1266b08f7880717cf70dd9012dd523e5a317f10fbe4d5e8c1a761c5fd237f88642f2ba33b23f973ff4002c9b26648d63084ab208d8ecef36497315f6e - languageName: node - linkType: hard - -"word-wrap@npm:^1.2.3": - version: 1.2.3 - resolution: "word-wrap@npm:1.2.3" - checksum: 6526abd75d4409c76d1989cf2fbf6080b903db29824be3d17d0a0b8f6221486c76a021174eda2616cf311199787983c34bae3c5e7b51d2ad7476f2066cddb75a - languageName: node - linkType: hard - -"wrappy@npm:1": - version: 1.0.2 - resolution: "wrappy@npm:1.0.2" - checksum: 519fcda0fcdf0c16327be2de9d98646742307bc830277e8868529fcf7566f2b330a6453c233e0cdcb767d5838dd61a90984a02ecc983bcddebea5ad0833bbf98 - languageName: node - linkType: hard - -"write@npm:1.0.3": - version: 1.0.3 - resolution: "write@npm:1.0.3" - dependencies: - mkdirp: ^0.5.1 - checksum: e8f8fddefea3eaaf4c8bacf072161f82d5e05c5fb8f003e1bae52673b94b88a4954d97688c7403a20301d2f6e9f61363b1affe74286b499b39bc0c42f17a56cb - languageName: node - linkType: hard - -"xdg-basedir@npm:^4.0.0": - version: 4.0.0 - resolution: "xdg-basedir@npm:4.0.0" - checksum: 928953cb7dda8e2475932f748847a3aae751f44864a0067b03e5ca66820a36e1e9ffb647f9b06fb68ebcb0b9d06d5aee630717a1d2501be14cec99f82efa2fe6 - languageName: node - linkType: hard - -"yallist@npm:^4.0.0": - version: 4.0.0 - resolution: "yallist@npm:4.0.0" - checksum: a2960ef879af6ee67a76cae29bac9d8bffeb6e9e366c217dbd21464e7fce071933705544724f47e90ba5209cf9c83c17d5582dd04415d86747a826b2a231efb8 - languageName: node - linkType: hard