-
Notifications
You must be signed in to change notification settings - Fork 4.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Porting Scaleout sample from C# to JavaScript #3968
Open
gandiddi
wants to merge
2
commits into
main
Choose a base branch
from
v-gandiddi/scaleoutsamplejs
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
/* eslint-disable */ | ||
module.exports = { | ||
"extends": "standard", | ||
"rules": { | ||
"semi": [2, "always"], | ||
"indent": [2, 4], | ||
"no-return-await": 0, | ||
"space-before-function-paren": [2, { | ||
"named": "never", | ||
"anonymous": "never", | ||
"asyncArrow": "always" | ||
}], | ||
"template-curly-spacing": [2, "always"] | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
# Scale Out | ||
|
||
Bot Framework v4 bot Scale Out sample | ||
|
||
This bot has been created using [Bot Framework](https://dev.botframework.com), is shows how to use a custom storage solution that supports a deployment scaled out across multiple machines. | ||
|
||
The custom storage solution is implemented against memory for testing purposes and against Azure Blob Storage. The sample shows how storage solutions with different policies can be implemented and integrated with the framework. The solution makes use of the standard HTTP ETag/If-Match mechanisms commonly found on cloud storage technologies. | ||
|
||
## Prerequisites | ||
|
||
- [Node.js](https://nodejs.org) version 16.16.0 or higher | ||
|
||
```bash | ||
# determine node version | ||
node --version | ||
``` | ||
- Update `.env` with required configuration settings | ||
- MicrosoftAppId | ||
- MicrosoftAppPassword | ||
- ConnectionName | ||
|
||
## To try this sample | ||
|
||
- Clone the repository | ||
|
||
```bash | ||
git clone https://github.com/microsoft/botbuilder-samples.git | ||
``` | ||
|
||
- In a terminal, navigate to `samples/javascript_nodejs/42.scale-out` | ||
|
||
```bash | ||
cd samples/javascript_nodejs/42.scale-out | ||
``` | ||
|
||
- Install modules | ||
|
||
```bash | ||
npm install | ||
``` | ||
|
||
- Start the bot | ||
|
||
```bash | ||
npm start | ||
``` | ||
|
||
## Testing the bot using Bot Framework Emulator | ||
|
||
[Bot Framework Emulator](https://github.com/microsoft/botframework-emulator) is a desktop application that allows bot developers to test and debug their bots on localhost or running remotely through a tunnel. | ||
|
||
- Install the latest Bot Framework Emulator from [here](https://github.com/Microsoft/BotFramework-Emulator/releases) | ||
|
||
### Connect to the bot using Bot Framework Emulator | ||
|
||
- Launch Bot Framework Emulator | ||
- File -> Open Bot | ||
- Enter a Bot URL of `http://localhost:3978/api/messages` | ||
|
||
## Further reading | ||
|
||
- [Bot Framework Documentation](https://docs.botframework.com) | ||
- [Bot Basics](https://docs.microsoft.com/azure/bot-service/bot-builder-basics?view=azure-bot-service-4.0) | ||
- [Implementing custom storage for you bot](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-custom-storage?view=azure-bot-service-4.0) | ||
- [Bot Storage](https://docs.microsoft.com/en-us/azure/bot-service/dotnet/bot-builder-dotnet-state?view=azure-bot-service-3.0&viewFallbackFrom=azure-bot-service-4.0) | ||
- [HTTP ETag](https://en.wikipedia.org/wiki/HTTP_ETag) | ||
- [Activity processing](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-concept-activity-processing?view=azure-bot-service-4.0) | ||
- [Azure Bot Service Introduction](https://docs.microsoft.com/azure/bot-service/bot-service-overview-introduction?view=azure-bot-service-4.0) | ||
- [Azure Bot Service Documentation](https://docs.microsoft.com/azure/bot-service/?view=azure-bot-service-4.0) | ||
- [.NET Core CLI tools](https://docs.microsoft.com/en-us/dotnet/core/tools/?tabs=netcore2x) | ||
- [Azure CLI](https://docs.microsoft.com/cli/azure/?view=azure-cli-latest) | ||
- [Azure Portal](https://portal.azure.com) | ||
- [Channels and Bot Connector Service](https://docs.microsoft.com/en-us/azure/bot-service/bot-concepts?view=azure-bot-service-4.0) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
const { BlobServiceClient, StorageSharedKeyCredential } = require('@azure/storage-blob'); | ||
|
||
class BlobStore { | ||
constructor(accountName, accountKey, containerName) { | ||
if (!accountName) { | ||
throw new Error('accountName is required'); | ||
} | ||
|
||
if (!accountKey) { | ||
throw new Error('accountKey is required'); | ||
} | ||
|
||
if (!containerName) { | ||
throw new Error('containerName is required'); | ||
} | ||
|
||
const sharedKeyCredential = new StorageSharedKeyCredential(accountName, accountKey); | ||
const blobServiceClient = new BlobServiceClient(`https://${ accountName }.blob.core.windows.net`, sharedKeyCredential); | ||
this.containerClient = blobServiceClient.getContainerClient(containerName); | ||
} | ||
|
||
async loadAsync(key) { | ||
if (!key) { | ||
throw new Error('key is required'); | ||
} | ||
|
||
const blobClient = this.containerClient.getBlockBlobClient(key); | ||
try { | ||
const downloadBlockBlobResponse = await blobClient.download(); | ||
const content = await streamToString(downloadBlockBlobResponse.readableStreamBody); | ||
const obj = JSON.parse(content); | ||
const etag = downloadBlockBlobResponse.properties.etag; | ||
return { content: obj, etag: etag }; | ||
} catch (error) { | ||
if (error.statusCode === 404) { | ||
return { content: {}, etag: null }; | ||
} | ||
throw error; | ||
} | ||
} | ||
|
||
async saveAsync(key, obj, etag) { | ||
if (!key) { | ||
throw new Error('key is required'); | ||
} | ||
|
||
if (!obj) { | ||
throw new Error('obj is required'); | ||
} | ||
|
||
const blobClient = this.containerClient.getBlockBlobClient(key); | ||
blobClient.properties.contentType = 'application/json'; | ||
const content = JSON.stringify(obj); | ||
if (etag) { | ||
try { | ||
await blobClient.upload(content, content.length, { conditions: { ifMatch: etag } }); | ||
} catch (error) { | ||
if (error.statusCode === 412) { | ||
return false; | ||
} | ||
throw error; | ||
} | ||
} else { | ||
await blobClient.upload(content, content.length); | ||
} | ||
|
||
return true; | ||
} | ||
} | ||
|
||
async function streamToString(readableStream) { | ||
return new Promise((resolve, reject) => { | ||
const chunks = []; | ||
readableStream.on('data', (data) => { | ||
chunks.push(data.toString()); | ||
}); | ||
readableStream.on('end', () => { | ||
resolve(chunks.join('')); | ||
}); | ||
readableStream.on('error', reject); | ||
}); | ||
} | ||
|
||
module.exports.BlobStore = BlobStore; |
54 changes: 54 additions & 0 deletions
54
samples/javascript_nodejs/42.scale-out/bots/scaleoutBot.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
const { ActivityHandler } = require('botbuilder'); | ||
const { MemoryStore } = require('../memoryStore'); | ||
const { DialogHost } = require('../dialogHost'); | ||
|
||
class ScaleoutBot extends ActivityHandler { | ||
/** | ||
* | ||
* @param {Dialog} dialog | ||
*/ | ||
constructor(dialog) { | ||
super(); | ||
if (!dialog) throw new Error('[ScaleoutBot]: Missing parameter. dialog is required'); | ||
|
||
this.dialog = dialog; | ||
|
||
this.onMessage(async (context, next) => { | ||
// Create the storage key for this conversation. | ||
const key = `${ context.activity.channelId }/conversations/${ context.activity.conversation?.id }`; | ||
|
||
var store = new MemoryStore(); | ||
var dialogHost = new DialogHost(); | ||
|
||
// The execution sits in a loop because there might be a retry if the save operation fails. | ||
while (true) { | ||
// Load any existing state associated with this key | ||
const { oldState, etag } = await store.loadAsync(key); | ||
|
||
// Run the dialog system with the old state and inbound activity, the result is a new state and outbound activities. | ||
const { activities, newState } = await dialogHost.runAsync(this.dialog, context.activity, oldState); | ||
|
||
// Save the updated state associated with this key. | ||
const success = await store.saveAsync(key, newState, etag); | ||
|
||
// Following a successful save, send any outbound Activities, otherwise retry everything. | ||
if (success) { | ||
if (activities.length > 0) { | ||
// This is an actual send on the TurnContext we were given and so will actual do a send this time. | ||
await context.sendActivities(activities); | ||
} | ||
|
||
break; | ||
} | ||
} | ||
|
||
// By calling next() you ensure that the next BotHandler is run. | ||
await next(); | ||
}); | ||
} | ||
} | ||
|
||
module.exports.ScaleoutBot = ScaleoutBot; |
33 changes: 33 additions & 0 deletions
33
...oymentTemplates/deployUseExistResourceGroup/parameters-for-template-AzureBot-with-rg.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
{ | ||
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", | ||
"contentVersion": "1.0.0.0", | ||
"parameters": { | ||
"azureBotId": { | ||
"value": "" | ||
}, | ||
"azureBotSku": { | ||
"value": "S1" | ||
}, | ||
"azureBotRegion": { | ||
"value": "global" | ||
}, | ||
"botEndpoint": { | ||
"value": "" | ||
}, | ||
"appType": { | ||
"value": "MultiTenant" | ||
}, | ||
"appId": { | ||
"value": "" | ||
}, | ||
"UMSIName": { | ||
"value": "" | ||
}, | ||
"UMSIResourceGroupName": { | ||
"value": "" | ||
}, | ||
"tenantId": { | ||
"value": "" | ||
} | ||
} | ||
} |
48 changes: 48 additions & 0 deletions
48
...ploymentTemplates/deployUseExistResourceGroup/parameters-for-template-BotApp-with-rg.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
{ | ||
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", | ||
"contentVersion": "1.0.0.0", | ||
"parameters": { | ||
"appServiceName": { | ||
"value": "" | ||
}, | ||
"existingAppServicePlanName": { | ||
"value": "" | ||
}, | ||
"existingAppServicePlanLocation": { | ||
"value": "" | ||
}, | ||
"newAppServicePlanName": { | ||
"value": "" | ||
}, | ||
"newAppServicePlanLocation": { | ||
"value": "" | ||
}, | ||
"newAppServicePlanSku": { | ||
"value": { | ||
"name": "S1", | ||
"tier": "Standard", | ||
"size": "S1", | ||
"family": "S", | ||
"capacity": 1 | ||
} | ||
}, | ||
"appType": { | ||
"value": "MultiTenant" | ||
}, | ||
"appId": { | ||
"value": "" | ||
}, | ||
"appSecret": { | ||
"value": "" | ||
}, | ||
"UMSIName": { | ||
"value": "" | ||
}, | ||
"UMSIResourceGroupName": { | ||
"value": "" | ||
}, | ||
"tenantId": { | ||
"value": "" | ||
} | ||
} | ||
} |
48 changes: 48 additions & 0 deletions
48
...t_nodejs/42.scale-out/deploymentTemplates/deployUseExistResourceGroup/readme.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
# Usage | ||
The BotApp must be deployed prior to AzureBot. | ||
|
||
Command line: | ||
- az login | ||
- az deployment group create --resource-group <group-name> --template-file <template-file> --parameters @<parameters-file> | ||
|
||
# parameters-for-template-BotApp-with-rg: | ||
|
||
- **appServiceName**:(required) The Name of the Bot App Service. | ||
|
||
- (choose an existingAppServicePlan or create a new AppServicePlan) | ||
- **existingAppServicePlanName**: The name of the App Service Plan. | ||
- **existingAppServicePlanLocation**: The location of the App Service Plan. | ||
- **newAppServicePlanName**: The name of the App Service Plan. | ||
- **newAppServicePlanLocation**: The location of the App Service Plan. | ||
- **newAppServicePlanSku**: The SKU of the App Service Plan. Defaults to Standard values. | ||
|
||
- **appType**: Type of Bot Authentication. set as MicrosoftAppType in the Web App's Application Settings. **Allowed values are: MultiTenant(default), SingleTenant, UserAssignedMSI.** | ||
|
||
- **appId**:(required) Active Directory App ID or User-Assigned Managed Identity Client ID, set as MicrosoftAppId in the Web App's Application Settings. | ||
|
||
- **appSecret**:(required for MultiTenant and SingleTenant) Active Directory App Password, set as MicrosoftAppPassword in the Web App's Application Settings. | ||
|
||
- **UMSIName**:(required for UserAssignedMSI) The User-Assigned Managed Identity Resource used for the Bot's Authentication. | ||
|
||
- **UMSIResourceGroupName**:(required for UserAssignedMSI) The User-Assigned Managed Identity Resource Group used for the Bot's Authentication. | ||
|
||
- **tenantId**: The Azure AD Tenant ID to use as part of the Bot's Authentication. Only used for SingleTenant and UserAssignedMSI app types. Defaults to <Subscription Tenant ID>. | ||
|
||
MoreInfo: https://docs.microsoft.com/en-us/azure/bot-service/tutorial-provision-a-bot?view=azure-bot-service-4.0&tabs=userassigned%2Cnewgroup#create-an-identity-resource | ||
|
||
|
||
|
||
# parameters-for-template-AzureBot-with-rg: | ||
|
||
- **azureBotId**:(required) The globally unique and immutable bot ID. | ||
- **azureBotSku**: The pricing tier of the Bot Service Registration. **Allowed values are: F0, S1(default)**. | ||
- **azureBotRegion**: Specifies the location of the new AzureBot. **Allowed values are: global(default), westeurope**. | ||
- **botEndpoint**: Use to handle client messages, Such as https://<botappServiceName>.azurewebsites.net/api/messages. | ||
|
||
- **appType**: Type of Bot Authentication. set as MicrosoftAppType in the Web App's Application Settings. **Allowed values are: MultiTenant(default), SingleTenant, UserAssignedMSI.** | ||
- **appId**:(required) Active Directory App ID or User-Assigned Managed Identity Client ID, set as MicrosoftAppId in the Web App's Application Settings. | ||
- **UMSIName**:(required for UserAssignedMSI) The User-Assigned Managed Identity Resource used for the Bot's Authentication. | ||
- **UMSIResourceGroupName**:(required for UserAssignedMSI) The User-Assigned Managed Identity Resource Group used for the Bot's Authentication. | ||
- **tenantId**: The Azure AD Tenant ID to use as part of the Bot's Authentication. Only used for SingleTenant and UserAssignedMSI app types. Defaults to <Subscription Tenant ID>. | ||
|
||
MoreInfo: https://docs.microsoft.com/en-us/azure/bot-service/tutorial-provision-a-bot?view=azure-bot-service-4.0&tabs=userassigned%2Cnewgroup#create-an-identity-resource |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Subclass Store