V1.0
|
@ -0,0 +1,206 @@
|
||||||
|
# SharePoint web part sample with bot framework - Secure
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
[Web parts](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/overview-client-side-web-parts) is a special kind of SharePoint controls that can be supported by the [Bot Framework](https://dev.botframework.com). This sample will show you how to embed a Bot Framework bot into a SharePoint web site with security consideration.
|
||||||
|
|
||||||
|
There are two parts included in this sample:
|
||||||
|
|
||||||
|
1. An echo bot sample
|
||||||
|
1. A web part sample
|
||||||
|
|
||||||
|
The web part sample embeds the echo bot by using a webchat. As web part code is running on client side, [web chat security](https://blog.botframework.com/2018/09/01/using-webchat-with-azure-bot-services-authentication/) needs to be taken into consideration. This sample shows how to secure your conversation including:
|
||||||
|
|
||||||
|
- Use Direct Line token instead of Direct Line secret
|
||||||
|
- Tamper-proof user: for user id, generate it inside client side and detect if the client has changed the user ID and reject the change.
|
||||||
|
|
||||||
|
This demo does not include any threat models and is designed for educational purposes only. When you design a production system, threat-modelling is an important task to make sure your system is secure and provide a way to quickly identify potential source of data breaches. IETF [RFC 6819](https://tools.ietf.org/html/rfc6819) and [OAuth 2.0 for Browser-Based Apps](https://tools.ietf.org/html/draft-ietf-oauth-browser-based-apps-01#section-9) is a good starting point for threat-modelling when using OAuth 2.0.
|
||||||
|
|
||||||
|
![react-bot-framework-secure](./assets/sp-wp-secure.gif)
|
||||||
|
|
||||||
|
## Used SharePoint Framework Version
|
||||||
|
|
||||||
|
![1.0](https://img.shields.io/badge/version-1.0-green.svg)
|
||||||
|
|
||||||
|
## Applies to
|
||||||
|
|
||||||
|
* [SharePoint Framework](https://docs.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
|
||||||
|
* [Office 365 tenant](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-development-environment)
|
||||||
|
* [Microsoft Bot Framework](https://dev.botframework.com/)
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- [Node.js](https://nodejs.org) version 10.19 (Node.js v9.x, v11.x, and v12.x are not currently supported with SharePoint Framework development)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# determine node version
|
||||||
|
node --version
|
||||||
|
```
|
||||||
|
|
||||||
|
## Solution
|
||||||
|
|
||||||
|
Solution|Author(s)
|
||||||
|
--------|---------
|
||||||
|
webpart | STCA BF Channel and ABS (stcabfchannel@microsoft.com) <br/> Stephan Bisser (@stephanbisser, bisser.io)
|
||||||
|
bot | STCA BF Channel and ABS (stcabfchannel@microsoft.com)
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
Version|Date|Comments
|
||||||
|
-------|----|--------
|
||||||
|
1.0|Nov 10, 2020|Initial release
|
||||||
|
|
||||||
|
## Disclaimer
|
||||||
|
|
||||||
|
**THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Minimal Path to Awesome
|
||||||
|
|
||||||
|
### Enlist
|
||||||
|
|
||||||
|
- Clone the repository
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone [Placeholder]
|
||||||
|
```
|
||||||
|
|
||||||
|
- In a terminal, navigate to `[Placeholder]`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd [Placeholder]
|
||||||
|
```
|
||||||
|
|
||||||
|
### [Setup bot](./bot/README.md)
|
||||||
|
|
||||||
|
- Go to ./bot
|
||||||
|
- Install modules
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
- Start the bot. You can use emulator to verify whether the bot works
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm start
|
||||||
|
```
|
||||||
|
|
||||||
|
- Register connections. You can get it done by [deploy your bot to Azure](https://aka.ms/azuredeployment). Save your bot service endpoint like: "https://YOUR_BOT.azurewebsites.net". Save your AAD Id as YOUR_APP_ID and secret as YOUR_APP_PSW also.
|
||||||
|
|
||||||
|
- [Connect to direct line](https://docs.microsoft.com/en-us/azure/bot-service/bot-service-channel-connect-directline?view=azure-bot-service-4.0), copy one of the Secret Key values as YOUR_DIRECT_LINE_SECRET and store this for later. This is your ‘Direct Line Secret’.
|
||||||
|
|
||||||
|
- Add ‘Direct Line Secret’ to an .env config file under ./bot
|
||||||
|
|
||||||
|
```bash
|
||||||
|
MicrosoftAppId=YOUR_APP_ID
|
||||||
|
MicrosoftAppPassword=YOUR_APP_PSW
|
||||||
|
DirectLineSecret=YOUR_DIRECT_LINE_SECRET
|
||||||
|
```
|
||||||
|
|
||||||
|
- Restart your bot in local or redeploy your bot with new config.
|
||||||
|
|
||||||
|
### [Setup web part](./webpart/README.md)
|
||||||
|
|
||||||
|
- Go to ./webpart
|
||||||
|
- Install modules
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
- Start web part
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gulp serve
|
||||||
|
```
|
||||||
|
|
||||||
|
Now web part is running locally in https://localhost:4321.
|
||||||
|
|
||||||
|
- (Opt.) Publish the bot: follow the steps [here](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-deploy-az-cli?view=azure-bot-service-4.0&tabs=csharp)
|
||||||
|
|
||||||
|
- (Opt.) Config CORS \
|
||||||
|
[CORS](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) must be set on bot app service to enable SharePoint client to get resource from bot service. Follow these steps to add your workbench to bot app service CORS configration:
|
||||||
|
1. Go to your azure portal
|
||||||
|
1. Navigate to your bot app service, search for CORS settings
|
||||||
|
1. Add https://localhost:4321 and https://<YOUR_SITE>.sharepoint.com to CORS origins
|
||||||
|
|
||||||
|
- Config bot endpoint \
|
||||||
|
Add the web part, set bot endpoint to https://localhost:4321 (local) or https://YOUR_BOT.azurewebsites.net (remote), refresh this page, then you can successfully connet bot with SharePoint.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
**Web Chat integration with security consideration**
|
||||||
|
|
||||||
|
The SharePoint component will integrate bot with react Web Chat component.
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
public render(): React.ReactElement<IBotFrameworkChatv4Props> {
|
||||||
|
return (
|
||||||
|
<div className={styles.botFrameworkChatv4} style={{ height: 700 }}>
|
||||||
|
<ReactWebChat directLine={directLine} styleOptions={styleSetOptions} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Inside Web Chat, direct line will be used to connect to Bot Service. On Bot Service side, one more endpoint "directline/token" will be added besides "api/messages", which will accept userId passed from client side and return back direct line token.
|
||||||
|
|
||||||
|
For production, this endpoint should also verify if the incoming request is authroized.
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
server.post('/directline/token', (req, res) => {
|
||||||
|
const secret = settings.parsed.DirectLineSecret;
|
||||||
|
const authorization = `Bearer ${secret}`;
|
||||||
|
|
||||||
|
const userId = 'dl_' + GetUserId((req.body || {}).user);
|
||||||
|
const options = {
|
||||||
|
method: 'POST',
|
||||||
|
uri: 'https://directline.botframework.com/v3/directline/tokens/generate',
|
||||||
|
body: JSON.stringify({ user: { id: userId} }),
|
||||||
|
headers: { 'Authorization': authorization, 'Content-Type': 'application/json'}
|
||||||
|
};+
|
||||||
|
|
||||||
|
request.post(options, (error, response, body) => {
|
||||||
|
if (!error && response.statusCode < 300) {
|
||||||
|
res.status(response.statusCode);
|
||||||
|
if (body) { res.send(JSON.parse(body)) }
|
||||||
|
} else {
|
||||||
|
res.status(500);
|
||||||
|
res.send('Call to retrieve token from DirectLine failed');
|
||||||
|
}
|
||||||
|
res.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
On web part side, it will fetch direct line token from bot service side with SharePoint userId then build up the web chat component. The UserId should be encrypted so it won't be easy to get other user's token by bot endpoint.
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
useEffect(() => {
|
||||||
|
const userId = props.context.pageContext.user.loginName;
|
||||||
|
generateToken(props.botEndpoint, md5(userId)).then((token: string) => {
|
||||||
|
if (token) {
|
||||||
|
setDirectLine(createDirectLine({ token }));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
```
|
||||||
|
|
||||||
|
And enable "Enhanced authentication options" can help detect client user Id change then reject the change:
|
||||||
|
![bot framework client web part](./assets/EnhancedAuth.png)\
|
||||||
|
For how to find this option, please refer [connect to direct line](https://docs.microsoft.com/en-us/azure/bot-service/bot-service-channel-connect-directline?view=azure-bot-service-4.0).
|
||||||
|
|
||||||
|
## Further reading
|
||||||
|
|
||||||
|
- [SharePoint Web Parts Development Basics](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/overview-client-side-web-parts)
|
||||||
|
- [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)
|
||||||
|
- [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)
|
||||||
|
- [Channels and Bot Connector Service](https://docs.microsoft.com/en-us/azure/bot-service/bot-concepts?view=azure-bot-service-4.0)
|
||||||
|
- [Restify](https://www.npmjs.com/package/restify)
|
||||||
|
- [Using WebChat with Azure Bot Service’s Authentication](https://blog.botframework.com/2018/09/01/using-webchat-with-azure-bot-services-authentication/)
|
||||||
|
|
||||||
|
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-bot-framework-secure" />
|
After Width: | Height: | Size: 105 KiB |
After Width: | Height: | Size: 5.1 MiB |
|
@ -0,0 +1,35 @@
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
# Build generated files
|
||||||
|
dist
|
||||||
|
lib
|
||||||
|
solution
|
||||||
|
temp
|
||||||
|
*.sppkg
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
|
||||||
|
# OSX
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Visual Studio files
|
||||||
|
.ntvs_analysis.dat
|
||||||
|
.vs
|
||||||
|
.vscode
|
||||||
|
bin
|
||||||
|
obj
|
||||||
|
|
||||||
|
# Resx Generated Code
|
||||||
|
*.resx.ts
|
||||||
|
|
||||||
|
# Styles Generated Code
|
||||||
|
*.scss.ts
|
||||||
|
|
||||||
|
packages/
|
|
@ -0,0 +1,106 @@
|
||||||
|
# Echo bot
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
This bot has been created using [Bot Framework](https://dev.botframework.com). It shows how to create a simple bot that accepts input from the user and echoes it back.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- [Node.js](https://nodejs.org) version 10.14.1 or higher
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# determine node version
|
||||||
|
node --version
|
||||||
|
```
|
||||||
|
|
||||||
|
## To try this sample locally
|
||||||
|
|
||||||
|
- Clone the repository
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone [Placeholder]
|
||||||
|
```
|
||||||
|
|
||||||
|
- In a console, navigate to [Placeholder]
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd [Placeholder]
|
||||||
|
```
|
||||||
|
|
||||||
|
- 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 Bot Framework Emulator version 4.3.0 or greater from [here](https://github.com/Microsoft/BotFramework-Emulator/releases)
|
||||||
|
|
||||||
|
### Connect to the bot using Bot Framework Emulator
|
||||||
|
|
||||||
|
- Launch Bot Framework Emulator
|
||||||
|
- File -> Open Bot
|
||||||
|
- Enter a Bot URL of `http://localhost:3978/api/messages`
|
||||||
|
|
||||||
|
## (Opt.) Deploy the bot to Azure
|
||||||
|
|
||||||
|
To learn more about deploying a bot to Azure, see [Deploy your bot to Azure](https://aka.ms/azuredeployment) for a complete list of deployment instructions.
|
||||||
|
|
||||||
|
## (Opt.) Testing Direct Line token generation
|
||||||
|
|
||||||
|
- [Connect to Direct Line](https://docs.microsoft.com/en-us/azure/bot-service/bot-service-channel-connect-directline?view=azure-bot-service-4.0)
|
||||||
|
|
||||||
|
- Add ‘Direct Line Secret’ to `.env`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
DirectLineSecret=YOUR_DIRECT_LINE_SECRET
|
||||||
|
```
|
||||||
|
|
||||||
|
- Start the bot
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm start
|
||||||
|
```
|
||||||
|
|
||||||
|
- Open [PostMan](https://www.postman.com/) and setup a post request to http://localhost:3978/directline/token
|
||||||
|
with the following json request body:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"user": "USER_ID"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Then you can see the Direct Line token generated with YOUR_DIRECT_LINE_SECRET and USER_ID:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"conversationId": "XXXXX",
|
||||||
|
"token": "XXXXX",
|
||||||
|
"expires_in": 3600
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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)
|
||||||
|
- [Activity processing](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-concept-activity-processing?view=azure-bot-service-4.0)
|
||||||
|
- [Azure Bot Service Introduction](https://docs.microsoft.com/azure/bot-service/bot-service-overview-introduction?view=azure-bot-service-4.0)
|
||||||
|
- [Azure Bot Service Documentation](https://docs.microsoft.com/azure/bot-service/?view=azure-bot-service-4.0)
|
||||||
|
- [Azure CLI](https://docs.microsoft.com/cli/azure/?view=azure-cli-latest)
|
||||||
|
- [Azure Portal](https://portal.azure.com)
|
||||||
|
- [Language Understanding using LUIS](https://docs.microsoft.com/en-us/azure/cognitive-services/luis/)
|
||||||
|
- [Channels and Bot Connector Service](https://docs.microsoft.com/en-us/azure/bot-service/bot-concepts?view=azure-bot-service-4.0)
|
||||||
|
- [TypeScript](https://www.typescriptlang.org)
|
||||||
|
- [Restify](https://www.npmjs.com/package/restify)
|
||||||
|
- [dotenv](https://www.npmjs.com/package/dotenv)
|
|
@ -0,0 +1,21 @@
|
||||||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
// DO NOT MODIFY THIS CODE
|
||||||
|
// This script is run as part of the Post Deploy step when
|
||||||
|
// deploying the bot to Azure. It ensures the Azure Web App
|
||||||
|
// is configured correctly to host a TypeScript authored bot.
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const replace = require('replace');
|
||||||
|
const WEB_CONFIG_FILE = './web.config';
|
||||||
|
|
||||||
|
if (fs.existsSync(path.resolve(WEB_CONFIG_FILE))) {
|
||||||
|
replace({
|
||||||
|
regex: "url=\"index.js\"",
|
||||||
|
replacement: "url=\"lib/index.js\"",
|
||||||
|
paths: ['./web.config'],
|
||||||
|
recursive: false,
|
||||||
|
silent: true,
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
|
||||||
|
"contentVersion": "1.0.0.0",
|
||||||
|
"parameters": {
|
||||||
|
"groupLocation": {
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"groupName": {
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"appId": {
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"appSecret": {
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"botId": {
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"botSku": {
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"newAppServicePlanName": {
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"newAppServicePlanSku": {
|
||||||
|
"value": {
|
||||||
|
"name": "S1",
|
||||||
|
"tier": "Standard",
|
||||||
|
"size": "S1",
|
||||||
|
"family": "S",
|
||||||
|
"capacity": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"newAppServicePlanLocation": {
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"newWebAppName": {
|
||||||
|
"value": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
|
||||||
|
"contentVersion": "1.0.0.0",
|
||||||
|
"parameters": {
|
||||||
|
"appId": {
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"appSecret": {
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"botId": {
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"botSku": {
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"newAppServicePlanName": {
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"newAppServicePlanSku": {
|
||||||
|
"value": {
|
||||||
|
"name": "S1",
|
||||||
|
"tier": "Standard",
|
||||||
|
"size": "S1",
|
||||||
|
"family": "S",
|
||||||
|
"capacity": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"appServicePlanLocation": {
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"existingAppServicePlan": {
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"newWebAppName": {
|
||||||
|
"value": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,183 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||||
|
"contentVersion": "1.0.0.0",
|
||||||
|
"parameters": {
|
||||||
|
"groupLocation": {
|
||||||
|
"type": "string",
|
||||||
|
"metadata": {
|
||||||
|
"description": "Specifies the location of the Resource Group."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"groupName": {
|
||||||
|
"type": "string",
|
||||||
|
"metadata": {
|
||||||
|
"description": "Specifies the name of the Resource Group."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"appId": {
|
||||||
|
"type": "string",
|
||||||
|
"metadata": {
|
||||||
|
"description": "Active Directory App ID, set as MicrosoftAppId in the Web App's Application Settings."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"appSecret": {
|
||||||
|
"type": "string",
|
||||||
|
"metadata": {
|
||||||
|
"description": "Active Directory App Password, set as MicrosoftAppPassword in the Web App's Application Settings."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"botId": {
|
||||||
|
"type": "string",
|
||||||
|
"metadata": {
|
||||||
|
"description": "The globally unique and immutable bot ID. Also used to configure the displayName of the bot, which is mutable."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"botSku": {
|
||||||
|
"type": "string",
|
||||||
|
"metadata": {
|
||||||
|
"description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"newAppServicePlanName": {
|
||||||
|
"type": "string",
|
||||||
|
"metadata": {
|
||||||
|
"description": "The name of the App Service Plan."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"newAppServicePlanSku": {
|
||||||
|
"type": "object",
|
||||||
|
"defaultValue": {
|
||||||
|
"name": "S1",
|
||||||
|
"tier": "Standard",
|
||||||
|
"size": "S1",
|
||||||
|
"family": "S",
|
||||||
|
"capacity": 1
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"description": "The SKU of the App Service Plan. Defaults to Standard values."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"newAppServicePlanLocation": {
|
||||||
|
"type": "string",
|
||||||
|
"metadata": {
|
||||||
|
"description": "The location of the App Service Plan. Defaults to \"westus\"."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"newWebAppName": {
|
||||||
|
"type": "string",
|
||||||
|
"defaultValue": "",
|
||||||
|
"metadata": {
|
||||||
|
"description": "The globally unique name of the Web App. Defaults to the value passed in for \"botId\"."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"variables": {
|
||||||
|
"appServicePlanName": "[parameters('newAppServicePlanName')]",
|
||||||
|
"resourcesLocation": "[parameters('newAppServicePlanLocation')]",
|
||||||
|
"webAppName": "[if(empty(parameters('newWebAppName')), parameters('botId'), parameters('newWebAppName'))]",
|
||||||
|
"siteHost": "[concat(variables('webAppName'), '.azurewebsites.net')]",
|
||||||
|
"botEndpoint": "[concat('https://', variables('siteHost'), '/api/messages')]",
|
||||||
|
"resourceGroupId": "[concat(subscription().id, '/resourceGroups/', parameters('groupName'))]"
|
||||||
|
},
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"name": "[parameters('groupName')]",
|
||||||
|
"type": "Microsoft.Resources/resourceGroups",
|
||||||
|
"apiVersion": "2018-05-01",
|
||||||
|
"location": "[parameters('groupLocation')]",
|
||||||
|
"properties": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Microsoft.Resources/deployments",
|
||||||
|
"apiVersion": "2018-05-01",
|
||||||
|
"name": "storageDeployment",
|
||||||
|
"resourceGroup": "[parameters('groupName')]",
|
||||||
|
"dependsOn": [
|
||||||
|
"[resourceId('Microsoft.Resources/resourceGroups/', parameters('groupName'))]"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"mode": "Incremental",
|
||||||
|
"template": {
|
||||||
|
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||||
|
"contentVersion": "1.0.0.0",
|
||||||
|
"parameters": {},
|
||||||
|
"variables": {},
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"comments": "Create a new App Service Plan",
|
||||||
|
"type": "Microsoft.Web/serverfarms",
|
||||||
|
"name": "[variables('appServicePlanName')]",
|
||||||
|
"apiVersion": "2018-02-01",
|
||||||
|
"location": "[variables('resourcesLocation')]",
|
||||||
|
"sku": "[parameters('newAppServicePlanSku')]",
|
||||||
|
"properties": {
|
||||||
|
"name": "[variables('appServicePlanName')]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"comments": "Create a Web App using the new App Service Plan",
|
||||||
|
"type": "Microsoft.Web/sites",
|
||||||
|
"apiVersion": "2015-08-01",
|
||||||
|
"location": "[variables('resourcesLocation')]",
|
||||||
|
"kind": "app",
|
||||||
|
"dependsOn": [
|
||||||
|
"[concat(variables('resourceGroupId'), '/providers/Microsoft.Web/serverfarms/', variables('appServicePlanName'))]"
|
||||||
|
],
|
||||||
|
"name": "[variables('webAppName')]",
|
||||||
|
"properties": {
|
||||||
|
"name": "[variables('webAppName')]",
|
||||||
|
"serverFarmId": "[variables('appServicePlanName')]",
|
||||||
|
"siteConfig": {
|
||||||
|
"appSettings": [
|
||||||
|
{
|
||||||
|
"name": "WEBSITE_NODE_DEFAULT_VERSION",
|
||||||
|
"value": "10.14.1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "MicrosoftAppId",
|
||||||
|
"value": "[parameters('appId')]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "MicrosoftAppPassword",
|
||||||
|
"value": "[parameters('appSecret')]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"cors": {
|
||||||
|
"allowedOrigins": [
|
||||||
|
"https://botservice.hosting.portal.azure.net",
|
||||||
|
"https://hosting.onecloud.azure-test.net/"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"apiVersion": "2017-12-01",
|
||||||
|
"type": "Microsoft.BotService/botServices",
|
||||||
|
"name": "[parameters('botId')]",
|
||||||
|
"location": "global",
|
||||||
|
"kind": "bot",
|
||||||
|
"sku": {
|
||||||
|
"name": "[parameters('botSku')]"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"name": "[parameters('botId')]",
|
||||||
|
"displayName": "[parameters('botId')]",
|
||||||
|
"endpoint": "[variables('botEndpoint')]",
|
||||||
|
"msaAppId": "[parameters('appId')]",
|
||||||
|
"developerAppInsightsApplicationId": null,
|
||||||
|
"developerAppInsightKey": null,
|
||||||
|
"publishingCredentials": null,
|
||||||
|
"storageResourceId": null
|
||||||
|
},
|
||||||
|
"dependsOn": [
|
||||||
|
"[concat(variables('resourceGroupId'), '/providers/Microsoft.Web/sites/', variables('webAppName'))]"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,154 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||||
|
"contentVersion": "1.0.0.0",
|
||||||
|
"parameters": {
|
||||||
|
"appId": {
|
||||||
|
"type": "string",
|
||||||
|
"metadata": {
|
||||||
|
"description": "Active Directory App ID, set as MicrosoftAppId in the Web App's Application Settings."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"appSecret": {
|
||||||
|
"type": "string",
|
||||||
|
"metadata": {
|
||||||
|
"description": "Active Directory App Password, set as MicrosoftAppPassword in the Web App's Application Settings. Defaults to \"\"."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"botId": {
|
||||||
|
"type": "string",
|
||||||
|
"metadata": {
|
||||||
|
"description": "The globally unique and immutable bot ID. Also used to configure the displayName of the bot, which is mutable."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"botSku": {
|
||||||
|
"defaultValue": "F0",
|
||||||
|
"type": "string",
|
||||||
|
"metadata": {
|
||||||
|
"description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"newAppServicePlanName": {
|
||||||
|
"type": "string",
|
||||||
|
"defaultValue": "",
|
||||||
|
"metadata": {
|
||||||
|
"description": "The name of the new App Service Plan."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"newAppServicePlanSku": {
|
||||||
|
"type": "object",
|
||||||
|
"defaultValue": {
|
||||||
|
"name": "S1",
|
||||||
|
"tier": "Standard",
|
||||||
|
"size": "S1",
|
||||||
|
"family": "S",
|
||||||
|
"capacity": 1
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"description": "The SKU of the App Service Plan. Defaults to Standard values."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"appServicePlanLocation": {
|
||||||
|
"type": "string",
|
||||||
|
"metadata": {
|
||||||
|
"description": "The location of the App Service Plan."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"existingAppServicePlan": {
|
||||||
|
"type": "string",
|
||||||
|
"defaultValue": "",
|
||||||
|
"metadata": {
|
||||||
|
"description": "Name of the existing App Service Plan used to create the Web App for the bot."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"newWebAppName": {
|
||||||
|
"type": "string",
|
||||||
|
"defaultValue": "",
|
||||||
|
"metadata": {
|
||||||
|
"description": "The globally unique name of the Web App. Defaults to the value passed in for \"botId\"."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"variables": {
|
||||||
|
"defaultAppServicePlanName": "[if(empty(parameters('existingAppServicePlan')), 'createNewAppServicePlan', parameters('existingAppServicePlan'))]",
|
||||||
|
"useExistingAppServicePlan": "[not(equals(variables('defaultAppServicePlanName'), 'createNewAppServicePlan'))]",
|
||||||
|
"servicePlanName": "[if(variables('useExistingAppServicePlan'), parameters('existingAppServicePlan'), parameters('newAppServicePlanName'))]",
|
||||||
|
"resourcesLocation": "[parameters('appServicePlanLocation')]",
|
||||||
|
"webAppName": "[if(empty(parameters('newWebAppName')), parameters('botId'), parameters('newWebAppName'))]",
|
||||||
|
"siteHost": "[concat(variables('webAppName'), '.azurewebsites.net')]",
|
||||||
|
"botEndpoint": "[concat('https://', variables('siteHost'), '/api/messages')]"
|
||||||
|
},
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"comments": "Create a new App Service Plan if no existing App Service Plan name was passed in.",
|
||||||
|
"type": "Microsoft.Web/serverfarms",
|
||||||
|
"condition": "[not(variables('useExistingAppServicePlan'))]",
|
||||||
|
"name": "[variables('servicePlanName')]",
|
||||||
|
"apiVersion": "2018-02-01",
|
||||||
|
"location": "[variables('resourcesLocation')]",
|
||||||
|
"sku": "[parameters('newAppServicePlanSku')]",
|
||||||
|
"properties": {
|
||||||
|
"name": "[variables('servicePlanName')]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"comments": "Create a Web App using an App Service Plan",
|
||||||
|
"type": "Microsoft.Web/sites",
|
||||||
|
"apiVersion": "2015-08-01",
|
||||||
|
"location": "[variables('resourcesLocation')]",
|
||||||
|
"kind": "app",
|
||||||
|
"dependsOn": [
|
||||||
|
"[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]"
|
||||||
|
],
|
||||||
|
"name": "[variables('webAppName')]",
|
||||||
|
"properties": {
|
||||||
|
"name": "[variables('webAppName')]",
|
||||||
|
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]",
|
||||||
|
"siteConfig": {
|
||||||
|
"appSettings": [
|
||||||
|
{
|
||||||
|
"name": "WEBSITE_NODE_DEFAULT_VERSION",
|
||||||
|
"value": "10.14.1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "MicrosoftAppId",
|
||||||
|
"value": "[parameters('appId')]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "MicrosoftAppPassword",
|
||||||
|
"value": "[parameters('appSecret')]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"cors": {
|
||||||
|
"allowedOrigins": [
|
||||||
|
"https://botservice.hosting.portal.azure.net",
|
||||||
|
"https://hosting.onecloud.azure-test.net/"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"apiVersion": "2017-12-01",
|
||||||
|
"type": "Microsoft.BotService/botServices",
|
||||||
|
"name": "[parameters('botId')]",
|
||||||
|
"location": "global",
|
||||||
|
"kind": "bot",
|
||||||
|
"sku": {
|
||||||
|
"name": "[parameters('botSku')]"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"name": "[parameters('botId')]",
|
||||||
|
"displayName": "[parameters('botId')]",
|
||||||
|
"endpoint": "[variables('botEndpoint')]",
|
||||||
|
"msaAppId": "[parameters('appId')]",
|
||||||
|
"developerAppInsightsApplicationId": null,
|
||||||
|
"developerAppInsightKey": null,
|
||||||
|
"publishingCredentials": null,
|
||||||
|
"storageResourceId": null
|
||||||
|
},
|
||||||
|
"dependsOn": [
|
||||||
|
"[resourceId('Microsoft.Web/sites/', variables('webAppName'))]"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
{
|
||||||
|
"name": "echobot",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Bot Builder v4 echo bot sample",
|
||||||
|
"author": "Microsoft",
|
||||||
|
"license": "MIT",
|
||||||
|
"main": "./lib/index.js",
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc --build",
|
||||||
|
"lint": "tslint -c tslint.json 'src/**/*.ts'",
|
||||||
|
"lintfix": "tslint -c tslint.json 'src/**/*.ts' --fix",
|
||||||
|
"postinstall": "npm run build && node ./deploymentScripts/webConfigPrep.js",
|
||||||
|
"start": "tsc --build && node ./lib/index.js",
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"watch": "nodemon --watch ./src -e ts --exec \"npm run start\""
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"botbuilder": "~4.9.1",
|
||||||
|
"dotenv": "^8.2.0",
|
||||||
|
"express": "^4.17.1",
|
||||||
|
"replace": "~1.1.1",
|
||||||
|
"restify": "~8.5.1",
|
||||||
|
"restify-cors-middleware": "^1.1.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/dotenv": "6.1.1",
|
||||||
|
"@types/restify": "8.4.2",
|
||||||
|
"@types/restify-cors-middleware": "^1.0.1",
|
||||||
|
"nodemon": "~2.0.4",
|
||||||
|
"tslint": "~6.1.2",
|
||||||
|
"typescript": "~3.9.2"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
import { ActivityHandler, MessageFactory } from 'botbuilder';
|
||||||
|
|
||||||
|
export 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 replyText = `Echo: ${ context.activity.text }`;
|
||||||
|
await context.sendActivity(MessageFactory.text(replyText, replyText));
|
||||||
|
// By calling next() you ensure that the next BotHandler is run.
|
||||||
|
await next();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.onMembersAdded(async (context, next) => {
|
||||||
|
const membersAdded = context.activity.membersAdded;
|
||||||
|
const welcomeText = 'Hello and welcome!';
|
||||||
|
for (const member of membersAdded) {
|
||||||
|
if (member.id !== context.activity.recipient.id) {
|
||||||
|
await context.sendActivity(MessageFactory.text(welcomeText, welcomeText));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// By calling next() you ensure that the next BotHandler is run.
|
||||||
|
await next();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,131 @@
|
||||||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
import * as path from 'path';
|
||||||
|
|
||||||
|
import { config } from 'dotenv';
|
||||||
|
const ENV_FILE = path.join(__dirname, '..', '.env');
|
||||||
|
const settings = config({ path: ENV_FILE });
|
||||||
|
|
||||||
|
import * as restify from 'restify';
|
||||||
|
|
||||||
|
import * as request from 'request';
|
||||||
|
|
||||||
|
import * as express from 'express';
|
||||||
|
|
||||||
|
import * as corsMiddleware from 'restify-cors-middleware';
|
||||||
|
|
||||||
|
// Import required bot services.
|
||||||
|
// See https://aka.ms/bot-services to learn more about the different parts of a bot.
|
||||||
|
import { BotFrameworkAdapter } from 'botbuilder';
|
||||||
|
|
||||||
|
// This bot's main dialog.
|
||||||
|
import { EchoBot } from './bot';
|
||||||
|
|
||||||
|
import { Guid } from './util';
|
||||||
|
|
||||||
|
// Add '*' origins for test only. You should update with your own origins in production code.
|
||||||
|
const cors = corsMiddleware({
|
||||||
|
origins: ['*'],
|
||||||
|
allowHeaders: ['*'],
|
||||||
|
exposeHeaders: ['*']
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create HTTP server.
|
||||||
|
const server = restify.createServer();
|
||||||
|
server.pre(cors.preflight);
|
||||||
|
server.use(cors.actual);
|
||||||
|
server.use(express.json());
|
||||||
|
server.listen(process.env.port || process.env.PORT || 3978, () => {
|
||||||
|
console.log(`\n${server.name} listening to ${server.url}`);
|
||||||
|
console.log('\nGet Bot Framework Emulator: https://aka.ms/botframework-emulator');
|
||||||
|
console.log('\nTo talk to your bot, open the emulator select "Open Bot"');
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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
|
||||||
|
});
|
||||||
|
|
||||||
|
// Catch-all for errors.
|
||||||
|
const onTurnErrorHandler = async (context, error) => {
|
||||||
|
// This check writes out errors to console log .vs. app insights.
|
||||||
|
// NOTE: In production environment, you should consider logging this to Azure
|
||||||
|
// application insights.
|
||||||
|
console.error(`\n [onTurnError] unhandled error: ${ error }`);
|
||||||
|
|
||||||
|
// Send a trace activity, which will be displayed in Bot Framework Emulator
|
||||||
|
await context.sendTraceActivity(
|
||||||
|
'OnTurnError Trace',
|
||||||
|
`${ error }`,
|
||||||
|
'https://www.botframework.com/schemas/error',
|
||||||
|
'TurnError'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Send a message to the user
|
||||||
|
await context.sendActivity('The bot encountered an error or bug.');
|
||||||
|
await context.sendActivity('To continue to run this bot, please fix the bot source code.');
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set the onTurnError for the singleton BotFrameworkAdapter.
|
||||||
|
adapter.onTurnError = onTurnErrorHandler;
|
||||||
|
|
||||||
|
// Create the main dialog.
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const GetUserId = (userName : string): string => {
|
||||||
|
const userId = userName? userName : Guid.newGuid();
|
||||||
|
return userId;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Listen for incoming token request.
|
||||||
|
server.post('/directline/token', (req, res) => {
|
||||||
|
const secret = settings.parsed.DirectLineSecret;
|
||||||
|
const authorization = `Bearer ${secret}`;
|
||||||
|
|
||||||
|
const userId = 'dl_' + GetUserId((req.body || {}).user);
|
||||||
|
const options = {
|
||||||
|
method: 'POST',
|
||||||
|
uri: 'https://directline.botframework.com/v3/directline/tokens/generate',
|
||||||
|
body: JSON.stringify({ user: { id: userId} }),
|
||||||
|
headers: { 'Authorization': authorization, 'Content-Type': 'application/json'}
|
||||||
|
};
|
||||||
|
|
||||||
|
request.post(options, (error, response, body) => {
|
||||||
|
if (!error && response.statusCode < 300) {
|
||||||
|
res.status(response.statusCode);
|
||||||
|
if (body) { res.send(JSON.parse(body)) }
|
||||||
|
} else {
|
||||||
|
res.status(500);
|
||||||
|
res.send('Call to retrieve token from DirectLine failed');
|
||||||
|
}
|
||||||
|
res.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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 myBot.run(context);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,12 @@
|
||||||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
export class Guid {
|
||||||
|
static newGuid() {
|
||||||
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
||||||
|
const r = Math.random() * 16 | 0,
|
||||||
|
v = c === 'x' ? r : (r & 0x3 | 0x8);
|
||||||
|
return v.toString(16);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"declaration": true,
|
||||||
|
"target": "es2016",
|
||||||
|
"module": "commonjs",
|
||||||
|
"outDir": "./lib",
|
||||||
|
"rootDir": "./src",
|
||||||
|
"sourceMap": true,
|
||||||
|
"incremental": true,
|
||||||
|
"tsBuildInfoFile": "./lib/.tsbuildinfo"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"defaultSeverity": "error",
|
||||||
|
"extends": [
|
||||||
|
"tslint:recommended"
|
||||||
|
],
|
||||||
|
"jsRules": {},
|
||||||
|
"rules": {
|
||||||
|
"interface-name" : [true, "never-prefix"],
|
||||||
|
"max-line-length": [false],
|
||||||
|
"no-console": [false, "log", "error"],
|
||||||
|
"no-var-requires": false,
|
||||||
|
"quotemark": [true, "single"],
|
||||||
|
"one-variable-per-declaration": false,
|
||||||
|
"curly": [true, "ignore-same-line"],
|
||||||
|
"trailing-comma": [true, {"multiline": "never", "singleline": "never"}],
|
||||||
|
"no-bitwise": [false]
|
||||||
|
},
|
||||||
|
"rulesDirectory": []
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
# EditorConfig helps developers define and maintain consistent
|
||||||
|
# coding styles between different editors and IDEs
|
||||||
|
# editorconfig.org
|
||||||
|
|
||||||
|
root = true
|
||||||
|
|
||||||
|
|
||||||
|
[*]
|
||||||
|
|
||||||
|
# change these settings to your own preference
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
# we recommend you to keep these unchanged
|
||||||
|
end_of_line = lf
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
[{package,bower}.json]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
|
@ -0,0 +1 @@
|
||||||
|
* text=auto
|
|
@ -0,0 +1,32 @@
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
# Build generated files
|
||||||
|
dist
|
||||||
|
lib
|
||||||
|
solution
|
||||||
|
temp
|
||||||
|
*.sppkg
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
|
||||||
|
# OSX
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Visual Studio files
|
||||||
|
.ntvs_analysis.dat
|
||||||
|
.vs
|
||||||
|
bin
|
||||||
|
obj
|
||||||
|
|
||||||
|
# Resx Generated Code
|
||||||
|
*.resx.ts
|
||||||
|
|
||||||
|
# Styles Generated Code
|
||||||
|
*.scss.ts
|
|
@ -0,0 +1,14 @@
|
||||||
|
# Folders
|
||||||
|
.vscode
|
||||||
|
coverage
|
||||||
|
node_modules
|
||||||
|
sharepoint
|
||||||
|
src
|
||||||
|
temp
|
||||||
|
|
||||||
|
# Files
|
||||||
|
*.csproj
|
||||||
|
.git*
|
||||||
|
.yo-rc.json
|
||||||
|
gulpfile.js
|
||||||
|
tsconfig.json
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"@microsoft/generator-sharepoint": {
|
||||||
|
"isCreatingSolution": false,
|
||||||
|
"environment": "spo",
|
||||||
|
"version": "1.10.0",
|
||||||
|
"libraryName": "react-bot-framework",
|
||||||
|
"libraryId": "1e3d14a8-d863-4a3d-b83a-dbd188d52015",
|
||||||
|
"packageManager": "npm",
|
||||||
|
"isDomainIsolated": false,
|
||||||
|
"componentType": "webpart"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
# Microsoft Bot Framework Web Chat
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
A web part sample that uses the [botframework-webchat module](https://www.npmjs.com/package/botframework-webchat) to create a React component to render the Bot Framework v4 webchat component. This web part is able to render Text and rich attachments (Images, Cards, Adaptive Cards, ...) and has settings for branding purposes.
|
||||||
|
|
||||||
|
## Used SharePoint Framework Version
|
||||||
|
|
||||||
|
![1.0](https://img.shields.io/badge/drop-1.0-green.svg)
|
||||||
|
|
||||||
|
## Applies to
|
||||||
|
|
||||||
|
* [SharePoint Framework Web Parts](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/overview-client-side-web-parts)
|
||||||
|
* [Office 365 developer tenant](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/set-up-your-developer-tenant)
|
||||||
|
* [Microsoft Bot Framework](http://dev.botframework.com)
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
> You need to have this [bot](../bot/) created and registered using the Microsoft Bot Framework and registered to use the Direct Line Channel, which will give you the token generation endpoint needed when adding this web part to the page. For more information on creating a bot and registering the channel you can see the official web site at [dev.botframework.com](http://dev.botframework.com).
|
||||||
|
|
||||||
|
## Disclaimer
|
||||||
|
|
||||||
|
**THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Minimal Path to Awesome
|
||||||
|
|
||||||
|
- Clone this repository
|
||||||
|
- in the command line run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
gulp serve
|
||||||
|
```
|
||||||
|
|
||||||
|
- Set the bot service URI as Bot EndPoint.
|
||||||
|
|
||||||
|
- Config bot endpoint \
|
||||||
|
Add the webpart, set bot endpoint to https://YOUR_BOT.azurewebsites.net, refresh this page, then you can successfully connect bot with SharePoint.
|
||||||
|
|
||||||
|
## Deploy
|
||||||
|
|
||||||
|
If you want to deploy the bot follow the steps [here](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/get-started/hosting-webpart-from-office-365-cdn)
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
This Web Part illustrates the following concepts on top of the SharePoint Framework:
|
||||||
|
|
||||||
|
- Connecting and communicating with a bot built on the Microsoft Bot Framework using the Direct Line Channel
|
||||||
|
- Validating Property Pane Settings
|
||||||
|
- Office UI Fabric
|
||||||
|
- React
|
After Width: | Height: | Size: 7.9 KiB |
After Width: | Height: | Size: 81 KiB |
After Width: | Height: | Size: 105 KiB |
After Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 90 KiB |
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json",
|
||||||
|
"version": "2.0",
|
||||||
|
"bundles": {
|
||||||
|
"bot-framework-chatv-4-web-part": {
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"entrypoint": "./lib/webparts/botFrameworkChatv4/BotFrameworkChatv4WebPart.js",
|
||||||
|
"manifest": "./src/webparts/botFrameworkChatv4/BotFrameworkChatv4WebPart.manifest.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"externals": {},
|
||||||
|
"localizedResources": {
|
||||||
|
"BotFrameworkChatv4WebPartStrings": "lib/webparts/botFrameworkChatv4/loc/{locale}.js"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/copy-assets.schema.json",
|
||||||
|
"deployCdnPath": "temp/deploy"
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/deploy-azure-storage.schema.json",
|
||||||
|
"workingDir": "./temp/deploy/",
|
||||||
|
"account": "<!-- STORAGE ACCOUNT NAME -->",
|
||||||
|
"container": "react-bot-framework",
|
||||||
|
"accessKey": "<!-- ACCESS KEY -->"
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
|
||||||
|
"solution": {
|
||||||
|
"name": "react-bot-framework-client-side-solution",
|
||||||
|
"id": "1e3d14a8-d863-4a3d-b83a-dbd188d52015",
|
||||||
|
"version": "1.0.0.0",
|
||||||
|
"includeClientSideAssets": true,
|
||||||
|
"isDomainIsolated": false
|
||||||
|
},
|
||||||
|
"paths": {
|
||||||
|
"zippedPackage": "solution/react-bot-framework.sppkg"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/core-build/serve.schema.json",
|
||||||
|
"port": 4321,
|
||||||
|
"https": true,
|
||||||
|
"initialPage": "https://localhost:5432/workbench",
|
||||||
|
"api": {
|
||||||
|
"port": 5432,
|
||||||
|
"entryPath": "node_modules/@microsoft/sp-webpart-workbench/lib/api/"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/write-manifests.schema.json",
|
||||||
|
"cdnBasePath": "<!-- PATH TO CDN -->"
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const gulp = require('gulp');
|
||||||
|
const build = require('@microsoft/sp-build-web');
|
||||||
|
build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase and will not be type-safe.`);
|
||||||
|
|
||||||
|
build.initialize(gulp);
|
|
@ -0,0 +1,48 @@
|
||||||
|
{
|
||||||
|
"name": "sp-bot-chat-webpart",
|
||||||
|
"version": "0.0.2",
|
||||||
|
"private": true,
|
||||||
|
"main": "lib/index.js",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "gulp bundle",
|
||||||
|
"clean": "gulp clean",
|
||||||
|
"test": "gulp test",
|
||||||
|
"lint": "tslint -c tslint.json 'src/**/*.{ts,tsx}'",
|
||||||
|
"lintfix": "tslint -c tslint.json 'src/**/*.{ts,tsx}' --fix"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@microsoft/sp-core-library": "1.10.0",
|
||||||
|
"@microsoft/sp-lodash-subset": "1.10.0",
|
||||||
|
"@microsoft/sp-office-ui-fabric-core": "1.10.0",
|
||||||
|
"@microsoft/sp-property-pane": "1.10.0",
|
||||||
|
"@microsoft/sp-webpart-base": "1.10.0",
|
||||||
|
"@types/es6-promise": "0.0.33",
|
||||||
|
"@types/react": "16.8.8",
|
||||||
|
"@types/react-dom": "16.8.3",
|
||||||
|
"@types/webpack-env": "1.13.1",
|
||||||
|
"blueimp-md5": "^2.16.0",
|
||||||
|
"botframework-directlinejs": "^0.11.4",
|
||||||
|
"botframework-webchat": "^4.5.2",
|
||||||
|
"office-ui-fabric-react": "6.189.2",
|
||||||
|
"react": "16.8.5",
|
||||||
|
"react-dom": "16.8.5",
|
||||||
|
"request": "^2.88.0",
|
||||||
|
"swagger-client": "^2.1.23"
|
||||||
|
},
|
||||||
|
"resolutions": {
|
||||||
|
"@types/react": "16.8.8"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@microsoft/rush-stack-compiler-3.3": "0.3.5",
|
||||||
|
"@microsoft/sp-build-web": "1.10.0",
|
||||||
|
"@microsoft/sp-module-interfaces": "1.10.0",
|
||||||
|
"@microsoft/sp-webpart-workbench": "1.10.0",
|
||||||
|
"@types/chai": "3.4.34",
|
||||||
|
"@types/mocha": "2.2.38",
|
||||||
|
"ajv": "~5.2.2",
|
||||||
|
"gulp": "~3.9.1"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
// A file is required to be in the root of the /src directory by the TypeScript compiler
|
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
|
||||||
|
"id": "7aa6d70f-2a9f-4e92-ad01-a45c582e82be",
|
||||||
|
"alias": "BotFrameworkChatv4WebPart",
|
||||||
|
"componentType": "WebPart",
|
||||||
|
|
||||||
|
// The "*" signifies that the version should be taken from the package.json
|
||||||
|
"version": "*",
|
||||||
|
"manifestVersion": 2,
|
||||||
|
|
||||||
|
// If true, the component can only be installed on sites where Custom Script is allowed.
|
||||||
|
// Components that allow authors to embed arbitrary script code should set this to true.
|
||||||
|
// https://support.office.com/en-us/article/Turn-scripting-capabilities-on-or-off-1f2c515f-5d7e-448a-9fd7-835da935584f
|
||||||
|
"requiresCustomScript": false,
|
||||||
|
"supportedHosts": ["SharePointWebPart"],
|
||||||
|
"preconfiguredEntries": [{
|
||||||
|
"groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Other
|
||||||
|
"group": { "default": "Other" },
|
||||||
|
"title": { "default": "BotFrameworkChatv4" },
|
||||||
|
"description": { "default": "Use the botframework-webchat v4 React component to render the webchat with the new v4 UI" },
|
||||||
|
"officeFabricIconFontName": "Page",
|
||||||
|
"properties": {
|
||||||
|
"description": "BotFrameworkChatv4"
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import * as ReactDom from 'react-dom';
|
||||||
|
import { Version } from '@microsoft/sp-core-library';
|
||||||
|
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
|
||||||
|
import { IPropertyPaneConfiguration, PropertyPaneTextField } from '@microsoft/sp-property-pane';
|
||||||
|
import { BotFrameworkChatv4 } from './components/BotFrameworkChatv4';
|
||||||
|
import { IBotFrameworkChatv4Props } from './components/IBotFrameworkChatv4Props';
|
||||||
|
import * as strings from 'BotFrameworkChatv4WebPartStrings';
|
||||||
|
|
||||||
|
export interface IBotFrameworkChatv4WebPartProps {
|
||||||
|
botEndpoint: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class BotFrameworkChatv4WebPart extends BaseClientSideWebPart<IBotFrameworkChatv4WebPartProps> {
|
||||||
|
public render(): void {
|
||||||
|
if (this.renderedOnce === false) {
|
||||||
|
const element: React.ReactElement<IBotFrameworkChatv4Props> = React.createElement(BotFrameworkChatv4, {
|
||||||
|
botEndpoint: this.properties.botEndpoint,
|
||||||
|
botName: strings.BotName,
|
||||||
|
context: this.context,
|
||||||
|
});
|
||||||
|
|
||||||
|
ReactDom.render(element, this.domElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected onDispose(): void {
|
||||||
|
ReactDom.unmountComponentAtNode(this.domElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected get dataVersion(): Version {
|
||||||
|
return Version.parse('1.0');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
|
||||||
|
return {
|
||||||
|
pages: [
|
||||||
|
{
|
||||||
|
header: {
|
||||||
|
description: strings.PropertyPaneDescription
|
||||||
|
},
|
||||||
|
groups: [
|
||||||
|
{
|
||||||
|
groupName: strings.BasicGroupName,
|
||||||
|
groupFields: [
|
||||||
|
PropertyPaneTextField('botEndpoint', {
|
||||||
|
label: strings.BotEndpointLabel
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
@import '~office-ui-fabric-react/dist/sass/References.scss';
|
||||||
|
|
||||||
|
.botFrameworkChatv4 {
|
||||||
|
.container {
|
||||||
|
max-width: 700px;
|
||||||
|
margin: 0px auto;
|
||||||
|
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
@include ms-Grid-row;
|
||||||
|
@include ms-fontColor-white;
|
||||||
|
background-color: $ms-color-themeDark;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column {
|
||||||
|
@include ms-Grid-col;
|
||||||
|
@include ms-lg10;
|
||||||
|
@include ms-xl8;
|
||||||
|
@include ms-xlPush2;
|
||||||
|
@include ms-lgPush1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
@include ms-font-xl;
|
||||||
|
@include ms-fontColor-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subTitle {
|
||||||
|
@include ms-font-l;
|
||||||
|
@include ms-fontColor-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
@include ms-font-l;
|
||||||
|
@include ms-fontColor-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
// Our button
|
||||||
|
text-decoration: none;
|
||||||
|
height: 32px;
|
||||||
|
|
||||||
|
// Primary Button
|
||||||
|
min-width: 80px;
|
||||||
|
background-color: $ms-color-themePrimary;
|
||||||
|
border-color: $ms-color-themePrimary;
|
||||||
|
color: $ms-color-white;
|
||||||
|
|
||||||
|
// Basic Button
|
||||||
|
outline: transparent;
|
||||||
|
position: relative;
|
||||||
|
font-family: "Segoe UI WestEuropean","Segoe UI",-apple-system,BlinkMacSystemFont,Roboto,"Helvetica Neue",sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
font-size: $ms-font-size-m;
|
||||||
|
font-weight: $ms-font-weight-regular;
|
||||||
|
border-width: 0;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0 16px;
|
||||||
|
|
||||||
|
.label {
|
||||||
|
font-weight: $ms-font-weight-semibold;
|
||||||
|
font-size: $ms-font-size-m;
|
||||||
|
height: 32px;
|
||||||
|
line-height: 32px;
|
||||||
|
margin: 0 4px;
|
||||||
|
vertical-align: top;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import { useState, useMemo, useEffect } from 'react';
|
||||||
|
import styles from './BotFrameworkChatv4.module.scss';
|
||||||
|
import { IBotFrameworkChatv4Props } from './IBotFrameworkChatv4Props';
|
||||||
|
import ReactWebChat, { createDirectLine } from 'botframework-webchat';
|
||||||
|
import * as md5 from 'blueimp-md5';
|
||||||
|
|
||||||
|
export const BotFrameworkChatv4: React.FunctionComponent<IBotFrameworkChatv4Props> = (props) => {
|
||||||
|
const user = props.context.pageContext.user;
|
||||||
|
const [directLine, setDirectLine] = useState(createDirectLine({}));
|
||||||
|
const styleSetOptions = useMemo(
|
||||||
|
() => {
|
||||||
|
return {
|
||||||
|
userAvatarInitials: user.displayName,
|
||||||
|
botAvatarInitials: props.botName,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[props]
|
||||||
|
);
|
||||||
|
|
||||||
|
const generateToken = async (botEndpoint: string, userId?: string): Promise<string> => {
|
||||||
|
const token = await window
|
||||||
|
.fetch(`${botEndpoint}/directline/token`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({ user: userId ? userId : '' }),
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
})
|
||||||
|
.then(
|
||||||
|
async (response: any): Promise<string> => {
|
||||||
|
if (response.ok) {
|
||||||
|
const tokenResponse = await response.clone().json();
|
||||||
|
return tokenResponse.token;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return token;
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
generateToken(props.botEndpoint, md5(user.loginName)).then((token: string) => {
|
||||||
|
if (token) {
|
||||||
|
setDirectLine(createDirectLine({ token }));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.botFrameworkChatv4} style={{ height: 700 }}>
|
||||||
|
<ReactWebChat directLine={directLine} styleOptions={styleSetOptions} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { WebPartContext } from '@microsoft/sp-webpart-base';
|
||||||
|
|
||||||
|
export interface IBotFrameworkChatv4Props {
|
||||||
|
botEndpoint: string;
|
||||||
|
botName: string;
|
||||||
|
context: WebPartContext;
|
||||||
|
}
|
8
samples/react-bot-framework-secure/webpart/src/webparts/botFrameworkChatv4/loc/en-us.js
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
define([], function() {
|
||||||
|
return {
|
||||||
|
"PropertyPaneDescription": "Set up bot configuration.",
|
||||||
|
"BasicGroupName": "Config",
|
||||||
|
"BotEndpointLabel": "Bot Endpoint (For example: https://YOUR_BOT.azurewebsites.net)",
|
||||||
|
"BotName": "Bot"
|
||||||
|
}
|
||||||
|
});
|
11
samples/react-bot-framework-secure/webpart/src/webparts/botFrameworkChatv4/loc/mystrings.d.ts
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
declare interface IBotFrameworkChatv4WebPartStrings {
|
||||||
|
PropertyPaneDescription: string;
|
||||||
|
BasicGroupName: string;
|
||||||
|
BotEndpointLabel: string;
|
||||||
|
BotName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'BotFrameworkChatv4WebPartStrings' {
|
||||||
|
const strings: IBotFrameworkChatv4WebPartStrings;
|
||||||
|
export = strings;
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.2/MicrosoftTeams.schema.json",
|
||||||
|
"manifestVersion": "1.2",
|
||||||
|
"packageName": "BotFrameworkChatv4",
|
||||||
|
"id": "7aa6d70f-2a9f-4e92-ad01-a45c582e82be",
|
||||||
|
"version": "0.1",
|
||||||
|
"developer": {
|
||||||
|
"name": "SPFx + Teams Dev",
|
||||||
|
"websiteUrl": "https://products.office.com/en-us/sharepoint/collaboration",
|
||||||
|
"privacyUrl": "https://privacy.microsoft.com/en-us/privacystatement",
|
||||||
|
"termsOfUseUrl": "https://www.microsoft.com/en-us/servicesagreement"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"short": "BotFrameworkChatv4"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"short": "Use the botframework-webchat v4 React component to render the webchat with the new v4 UI",
|
||||||
|
"full": "Use the botframework-webchat v4 React component to render the webchat with the new v4 UI"
|
||||||
|
},
|
||||||
|
"icons": {
|
||||||
|
"outline": "tab20x20.png",
|
||||||
|
"color": "tab96x96.png"
|
||||||
|
},
|
||||||
|
"accentColor": "#004578",
|
||||||
|
"configurableTabs": [
|
||||||
|
{
|
||||||
|
"configurationUrl": "https://{teamSiteDomain}{teamSitePath}/_layouts/15/TeamsLogon.aspx?SPFX=true&dest={teamSitePath}/_layouts/15/teamshostedapp.aspx%3FopenPropertyPane=true%26teams%26componentId=7aa6d70f-2a9f-4e92-ad01-a45c582e82be",
|
||||||
|
"canUpdateConfiguration": false,
|
||||||
|
"scopes": [
|
||||||
|
"team"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"validDomains": [
|
||||||
|
"*.login.microsoftonline.com",
|
||||||
|
"*.sharepoint.com",
|
||||||
|
"*.sharepoint-df.com",
|
||||||
|
"spoppe-a.akamaihd.net",
|
||||||
|
"spoprod-a.akamaihd.net",
|
||||||
|
"resourceseng.blob.core.windows.net",
|
||||||
|
"msft.spoppe.com"
|
||||||
|
],
|
||||||
|
"webApplicationInfo": {
|
||||||
|
"resource": "https://{teamSiteDomain}",
|
||||||
|
"id": "00000003-0000-0ff1-ce00-000000000000"
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 933 B |
After Width: | Height: | Size: 2.5 KiB |
|
@ -0,0 +1,40 @@
|
||||||
|
{
|
||||||
|
"extends": "./node_modules/@microsoft/rush-stack-compiler-3.3/includes/tsconfig-web.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5",
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"jsx": "react",
|
||||||
|
"declaration": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"outDir": "lib",
|
||||||
|
"inlineSources": false,
|
||||||
|
"strictNullChecks": false,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"typeRoots": [
|
||||||
|
"./node_modules/@types",
|
||||||
|
"./node_modules/@microsoft"
|
||||||
|
],
|
||||||
|
"types": [
|
||||||
|
"es6-promise",
|
||||||
|
"webpack-env"
|
||||||
|
],
|
||||||
|
"lib": [
|
||||||
|
"es5",
|
||||||
|
"dom",
|
||||||
|
"es2015.collection"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*.ts",
|
||||||
|
"src/**/*.tsx"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules",
|
||||||
|
"lib"
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
"extends": "@microsoft/sp-tslint-rules/base-tslint.json",
|
||||||
|
"rules": {
|
||||||
|
"class-name": false,
|
||||||
|
"export-name": false,
|
||||||
|
"forin": false,
|
||||||
|
"label-position": false,
|
||||||
|
"member-access": true,
|
||||||
|
"no-arg": false,
|
||||||
|
"no-console": false,
|
||||||
|
"no-construct": false,
|
||||||
|
"no-duplicate-variable": true,
|
||||||
|
"no-eval": false,
|
||||||
|
"no-function-expression": true,
|
||||||
|
"no-internal-module": true,
|
||||||
|
"no-shadowed-variable": true,
|
||||||
|
"no-switch-case-fall-through": true,
|
||||||
|
"no-unnecessary-semicolons": true,
|
||||||
|
"no-unused-expression": true,
|
||||||
|
"no-use-before-declare": true,
|
||||||
|
"no-with-statement": true,
|
||||||
|
"semicolon": true,
|
||||||
|
"trailing-comma": false,
|
||||||
|
"typedef": false,
|
||||||
|
"typedef-whitespace": false,
|
||||||
|
"use-named-parameter": true,
|
||||||
|
"variable-name": false,
|
||||||
|
"whitespace": false
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
// Type definitions for Microsoft ODSP projects
|
||||||
|
// Project: ODSP
|
||||||
|
|
||||||
|
/* Global definition for UNIT_TEST builds */
|
||||||
|
declare const UNIT_TEST: boolean;
|
15
samples/react-bot-framework-secure/webpart/typings/assertion-error/assertion-error.d.ts
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
// Type definitions for assertion-error 1.0.0
|
||||||
|
// Project: https://github.com/chaijs/assertion-error
|
||||||
|
// Definitions by: Bart van der Schoor <https://github.com/Bartvds>
|
||||||
|
// Definitions: https://github.com/borisyankov/DefinitelyTyped
|
||||||
|
|
||||||
|
declare module 'assertion-error' {
|
||||||
|
class AssertionError implements Error {
|
||||||
|
constructor(message: string, props?: any, ssf?: Function);
|
||||||
|
name: string;
|
||||||
|
message: string;
|
||||||
|
showDiff: boolean;
|
||||||
|
stack: string;
|
||||||
|
}
|
||||||
|
export = AssertionError;
|
||||||
|
}
|
|
@ -0,0 +1,631 @@
|
||||||
|
// Type definitions for Knockout v3.2.0
|
||||||
|
// Project: http://knockoutjs.com
|
||||||
|
// Definitions by: Boris Yankov <https://github.com/borisyankov/>, Igor Oleinikov <https://github.com/Igorbek/>, Clément Bourgeois <https://github.com/moonpyk/>
|
||||||
|
// Definitions: https://github.com/borisyankov/DefinitelyTyped
|
||||||
|
|
||||||
|
|
||||||
|
interface KnockoutSubscribableFunctions<T> {
|
||||||
|
[key: string]: KnockoutBindingHandler;
|
||||||
|
|
||||||
|
notifySubscribers(valueToWrite?: T, event?: string): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface KnockoutComputedFunctions<T> {
|
||||||
|
[key: string]: KnockoutBindingHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface KnockoutObservableFunctions<T> {
|
||||||
|
[key: string]: KnockoutBindingHandler;
|
||||||
|
|
||||||
|
equalityComparer(a: any, b: any): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface KnockoutObservableArrayFunctions<T> {
|
||||||
|
// General Array functions
|
||||||
|
indexOf(searchElement: T, fromIndex?: number): number;
|
||||||
|
slice(start: number, end?: number): T[];
|
||||||
|
splice(start: number): T[];
|
||||||
|
splice(start: number, deleteCount: number, ...items: T[]): T[];
|
||||||
|
pop(): T;
|
||||||
|
push(...items: T[]): void;
|
||||||
|
shift(): T;
|
||||||
|
unshift(...items: T[]): number;
|
||||||
|
reverse(): KnockoutObservableArray<T>;
|
||||||
|
sort(): KnockoutObservableArray<T>;
|
||||||
|
sort(compareFunction: (left: T, right: T) => number): KnockoutObservableArray<T>;
|
||||||
|
|
||||||
|
// Ko specific
|
||||||
|
[key: string]: KnockoutBindingHandler;
|
||||||
|
|
||||||
|
replace(oldItem: T, newItem: T): void;
|
||||||
|
|
||||||
|
remove(item: T): T[];
|
||||||
|
remove(removeFunction: (item: T) => boolean): T[];
|
||||||
|
removeAll(items: T[]): T[];
|
||||||
|
removeAll(): T[];
|
||||||
|
|
||||||
|
destroy(item: T): void;
|
||||||
|
destroy(destroyFunction: (item: T) => boolean): void;
|
||||||
|
destroyAll(items: T[]): void;
|
||||||
|
destroyAll(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface KnockoutSubscribableStatic {
|
||||||
|
fn: KnockoutSubscribableFunctions<any>;
|
||||||
|
|
||||||
|
new <T>(): KnockoutSubscribable<T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface KnockoutSubscription {
|
||||||
|
dispose(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface KnockoutSubscribable<T> extends KnockoutSubscribableFunctions<T> {
|
||||||
|
subscribe(callback: (newValue: T) => void, target?: any, event?: string): KnockoutSubscription;
|
||||||
|
subscribe<TEvent>(callback: (newValue: TEvent) => void, target: any, event: string): KnockoutSubscription;
|
||||||
|
extend(requestedExtenders: { [key: string]: any; }): KnockoutSubscribable<T>;
|
||||||
|
getSubscriptionsCount(): number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface KnockoutComputedStatic {
|
||||||
|
fn: KnockoutComputedFunctions<any>;
|
||||||
|
|
||||||
|
<T>(): KnockoutComputed<T>;
|
||||||
|
<T>(func: () => T, context?: any, options?: any): KnockoutComputed<T>;
|
||||||
|
<T>(def: KnockoutComputedDefine<T>, context?: any): KnockoutComputed<T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface KnockoutComputed<T> extends KnockoutObservable<T>, KnockoutComputedFunctions<T> {
|
||||||
|
fn: KnockoutComputedFunctions<any>;
|
||||||
|
|
||||||
|
dispose(): void;
|
||||||
|
isActive(): boolean;
|
||||||
|
getDependenciesCount(): number;
|
||||||
|
extend(requestedExtenders: { [key: string]: any; }): KnockoutComputed<T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface KnockoutObservableArrayStatic {
|
||||||
|
fn: KnockoutObservableArrayFunctions<any>;
|
||||||
|
|
||||||
|
<T>(value?: T[]): KnockoutObservableArray<T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface KnockoutObservableArray<T> extends KnockoutObservable<T[]>, KnockoutObservableArrayFunctions<T> {
|
||||||
|
extend(requestedExtenders: { [key: string]: any; }): KnockoutObservableArray<T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface KnockoutObservableStatic {
|
||||||
|
fn: KnockoutObservableFunctions<any>;
|
||||||
|
|
||||||
|
<T>(value?: T): KnockoutObservable<T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface KnockoutObservable<T> extends KnockoutSubscribable<T>, KnockoutObservableFunctions<T> {
|
||||||
|
(): T;
|
||||||
|
(value: T): void;
|
||||||
|
|
||||||
|
peek(): T;
|
||||||
|
valueHasMutated?:{(): void;};
|
||||||
|
valueWillMutate?:{(): void;};
|
||||||
|
extend(requestedExtenders: { [key: string]: any; }): KnockoutObservable<T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface KnockoutComputedDefine<T> {
|
||||||
|
read(): T;
|
||||||
|
write? (value: T): void;
|
||||||
|
disposeWhenNodeIsRemoved?: Node;
|
||||||
|
disposeWhen? (): boolean;
|
||||||
|
owner?: any;
|
||||||
|
deferEvaluation?: boolean;
|
||||||
|
pure?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface KnockoutBindingContext {
|
||||||
|
$parent: any;
|
||||||
|
$parents: any[];
|
||||||
|
$root: any;
|
||||||
|
$data: any;
|
||||||
|
$rawData: any | KnockoutObservable<any>;
|
||||||
|
$index?: KnockoutObservable<number>;
|
||||||
|
$parentContext?: KnockoutBindingContext;
|
||||||
|
$component: any;
|
||||||
|
$componentTemplateNodes: Node[];
|
||||||
|
|
||||||
|
extend(properties: any): any;
|
||||||
|
createChildContext(dataItemOrAccessor: any, dataItemAlias?: any, extendCallback?: Function): any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface KnockoutAllBindingsAccessor {
|
||||||
|
(): any;
|
||||||
|
get(name: string): any;
|
||||||
|
has(name: string): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface KnockoutBindingHandler {
|
||||||
|
after?: Array<string>;
|
||||||
|
init?: (element: any, valueAccessor: () => any, allBindingsAccessor?: KnockoutAllBindingsAccessor, viewModel?: any, bindingContext?: KnockoutBindingContext) => void | { controlsDescendantBindings: boolean; };
|
||||||
|
update?: (element: any, valueAccessor: () => any, allBindingsAccessor?: KnockoutAllBindingsAccessor, viewModel?: any, bindingContext?: KnockoutBindingContext) => void;
|
||||||
|
options?: any;
|
||||||
|
preprocess?: (value: string, name: string, addBindingCallback?: (name: string, value: string) => void) => string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface KnockoutBindingHandlers {
|
||||||
|
[bindingHandler: string]: KnockoutBindingHandler;
|
||||||
|
|
||||||
|
// Controlling text and appearance
|
||||||
|
visible: KnockoutBindingHandler;
|
||||||
|
text: KnockoutBindingHandler;
|
||||||
|
html: KnockoutBindingHandler;
|
||||||
|
css: KnockoutBindingHandler;
|
||||||
|
style: KnockoutBindingHandler;
|
||||||
|
attr: KnockoutBindingHandler;
|
||||||
|
|
||||||
|
// Control Flow
|
||||||
|
foreach: KnockoutBindingHandler;
|
||||||
|
if: KnockoutBindingHandler;
|
||||||
|
ifnot: KnockoutBindingHandler;
|
||||||
|
with: KnockoutBindingHandler;
|
||||||
|
|
||||||
|
// Working with form fields
|
||||||
|
click: KnockoutBindingHandler;
|
||||||
|
event: KnockoutBindingHandler;
|
||||||
|
submit: KnockoutBindingHandler;
|
||||||
|
enable: KnockoutBindingHandler;
|
||||||
|
disable: KnockoutBindingHandler;
|
||||||
|
value: KnockoutBindingHandler;
|
||||||
|
textInput: KnockoutBindingHandler;
|
||||||
|
hasfocus: KnockoutBindingHandler;
|
||||||
|
checked: KnockoutBindingHandler;
|
||||||
|
options: KnockoutBindingHandler;
|
||||||
|
selectedOptions: KnockoutBindingHandler;
|
||||||
|
uniqueName: KnockoutBindingHandler;
|
||||||
|
|
||||||
|
// Rendering templates
|
||||||
|
template: KnockoutBindingHandler;
|
||||||
|
|
||||||
|
// Components (new for v3.2)
|
||||||
|
component: KnockoutBindingHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface KnockoutMemoization {
|
||||||
|
memoize(callback: () => string): string;
|
||||||
|
unmemoize(memoId: string, callbackParams: any[]): boolean;
|
||||||
|
unmemoizeDomNodeAndDescendants(domNode: any, extraCallbackParamsArray: any[]): boolean;
|
||||||
|
parseMemoText(memoText: string): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface KnockoutVirtualElement {}
|
||||||
|
|
||||||
|
interface KnockoutVirtualElements {
|
||||||
|
allowedBindings: { [bindingName: string]: boolean; };
|
||||||
|
emptyNode(node: KnockoutVirtualElement ): void;
|
||||||
|
firstChild(node: KnockoutVirtualElement ): KnockoutVirtualElement;
|
||||||
|
insertAfter( container: KnockoutVirtualElement, nodeToInsert: Node, insertAfter: Node ): void;
|
||||||
|
nextSibling(node: KnockoutVirtualElement): Node;
|
||||||
|
prepend(node: KnockoutVirtualElement, toInsert: Node ): void;
|
||||||
|
setDomNodeChildren(node: KnockoutVirtualElement, newChildren: { length: number;[index: number]: Node; } ): void;
|
||||||
|
childNodes(node: KnockoutVirtualElement ): Node[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface KnockoutExtenders {
|
||||||
|
throttle(target: any, timeout: number): KnockoutComputed<any>;
|
||||||
|
notify(target: any, notifyWhen: string): any;
|
||||||
|
|
||||||
|
rateLimit(target: any, timeout: number): any;
|
||||||
|
rateLimit(target: any, options: { timeout: number; method?: string; }): any;
|
||||||
|
|
||||||
|
trackArrayChanges(target: any): any;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// NOTE TO MAINTAINERS AND CONTRIBUTORS : pay attention to only include symbols that are
|
||||||
|
// publicly exported in the minified version of ko, without that you can give the false
|
||||||
|
// impression that some functions will be available in production builds.
|
||||||
|
//
|
||||||
|
interface KnockoutUtils {
|
||||||
|
//////////////////////////////////
|
||||||
|
// utils.domData.js
|
||||||
|
//////////////////////////////////
|
||||||
|
|
||||||
|
domData: {
|
||||||
|
get (node: Element, key: string): any;
|
||||||
|
|
||||||
|
set (node: Element, key: string, value: any): void;
|
||||||
|
|
||||||
|
getAll(node: Element, createIfNotFound: boolean): any;
|
||||||
|
|
||||||
|
clear(node: Element): boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
//////////////////////////////////
|
||||||
|
// utils.domNodeDisposal.js
|
||||||
|
//////////////////////////////////
|
||||||
|
|
||||||
|
domNodeDisposal: {
|
||||||
|
addDisposeCallback(node: Element, callback: Function): void;
|
||||||
|
|
||||||
|
removeDisposeCallback(node: Element, callback: Function): void;
|
||||||
|
|
||||||
|
cleanNode(node: Node): Element;
|
||||||
|
|
||||||
|
removeNode(node: Node): void;
|
||||||
|
};
|
||||||
|
|
||||||
|
addOrRemoveItem<T>(array: T[] | KnockoutObservable<T>, value: T, included: T): void;
|
||||||
|
|
||||||
|
arrayFilter<T>(array: T[], predicate: (item: T) => boolean): T[];
|
||||||
|
|
||||||
|
arrayFirst<T>(array: T[], predicate: (item: T) => boolean, predicateOwner?: any): T;
|
||||||
|
|
||||||
|
arrayForEach<T>(array: T[], action: (item: T, index: number) => void): void;
|
||||||
|
|
||||||
|
arrayGetDistinctValues<T>(array: T[]): T[];
|
||||||
|
|
||||||
|
arrayIndexOf<T>(array: T[], item: T): number;
|
||||||
|
|
||||||
|
arrayMap<T, U>(array: T[], mapping: (item: T) => U): U[];
|
||||||
|
|
||||||
|
arrayPushAll<T>(array: T[] | KnockoutObservableArray<T>, valuesToPush: T[]): T[];
|
||||||
|
|
||||||
|
arrayRemoveItem(array: any[], itemToRemove: any): void;
|
||||||
|
|
||||||
|
compareArrays<T>(a: T[], b: T[]): Array<KnockoutArrayChange<T>>;
|
||||||
|
|
||||||
|
extend(target: Object, source: Object): Object;
|
||||||
|
|
||||||
|
fieldsIncludedWithJsonPost: any[];
|
||||||
|
|
||||||
|
getFormFields(form: any, fieldName: string): any[];
|
||||||
|
|
||||||
|
objectForEach(obj: any, action: (key: any, value: any) => void): void;
|
||||||
|
|
||||||
|
parseHtmlFragment(html: string): any[];
|
||||||
|
|
||||||
|
parseJson(jsonString: string): any;
|
||||||
|
|
||||||
|
postJson(urlOrForm: any, data: any, options: any): void;
|
||||||
|
|
||||||
|
peekObservable<T>(value: KnockoutObservable<T>): T;
|
||||||
|
|
||||||
|
range(min: any, max: any): any;
|
||||||
|
|
||||||
|
registerEventHandler(element: any, eventType: any, handler: Function): void;
|
||||||
|
|
||||||
|
setHtml(node: Element, html: () => string): void;
|
||||||
|
|
||||||
|
setHtml(node: Element, html: string): void;
|
||||||
|
|
||||||
|
setTextContent(element: any, textContent: string | KnockoutObservable<string>): void;
|
||||||
|
|
||||||
|
stringifyJson(data: any, replacer?: Function, space?: string): string;
|
||||||
|
|
||||||
|
toggleDomNodeCssClass(node: any, className: string, shouldHaveClass: boolean): void;
|
||||||
|
|
||||||
|
triggerEvent(element: any, eventType: any): void;
|
||||||
|
|
||||||
|
unwrapObservable<T>(value: KnockoutObservable<T> | T): T;
|
||||||
|
|
||||||
|
// NOT PART OF THE MINIFIED API SURFACE (ONLY IN knockout-{version}.debug.js) https://github.com/SteveSanderson/knockout/issues/670
|
||||||
|
// forceRefresh(node: any): void;
|
||||||
|
// ieVersion: number;
|
||||||
|
// isIe6: boolean;
|
||||||
|
// isIe7: boolean;
|
||||||
|
// jQueryHtmlParse(html: string): any[];
|
||||||
|
// makeArray(arrayLikeObject: any): any[];
|
||||||
|
// moveCleanedNodesToContainerElement(nodes: any[]): HTMLElement;
|
||||||
|
// replaceDomNodes(nodeToReplaceOrNodeArray: any, newNodesArray: any[]): void;
|
||||||
|
// setDomNodeChildren(domNode: any, childNodes: any[]): void;
|
||||||
|
// setElementName(element: any, name: string): void;
|
||||||
|
// setOptionNodeSelectionState(optionNode: any, isSelected: boolean): void;
|
||||||
|
// simpleHtmlParse(html: string): any[];
|
||||||
|
// stringStartsWith(str: string, startsWith: string): boolean;
|
||||||
|
// stringTokenize(str: string, delimiter: string): string[];
|
||||||
|
// stringTrim(str: string): string;
|
||||||
|
// tagNameLower(element: any): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface KnockoutArrayChange<T> {
|
||||||
|
status: string;
|
||||||
|
value: T;
|
||||||
|
index: number;
|
||||||
|
moved?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////
|
||||||
|
// templateSources.js
|
||||||
|
//////////////////////////////////
|
||||||
|
|
||||||
|
interface KnockoutTemplateSourcesDomElement {
|
||||||
|
text(): any;
|
||||||
|
text(value: any): void;
|
||||||
|
|
||||||
|
data(key: string): any;
|
||||||
|
data(key: string, value: any): any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface KnockoutTemplateAnonymous extends KnockoutTemplateSourcesDomElement {
|
||||||
|
nodes(): any;
|
||||||
|
nodes(value: any): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface KnockoutTemplateSources {
|
||||||
|
|
||||||
|
domElement: {
|
||||||
|
prototype: KnockoutTemplateSourcesDomElement
|
||||||
|
new (element: Element): KnockoutTemplateSourcesDomElement
|
||||||
|
};
|
||||||
|
|
||||||
|
anonymousTemplate: {
|
||||||
|
prototype: KnockoutTemplateAnonymous;
|
||||||
|
new (element: Element): KnockoutTemplateAnonymous;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////
|
||||||
|
// nativeTemplateEngine.js
|
||||||
|
//////////////////////////////////
|
||||||
|
|
||||||
|
interface KnockoutNativeTemplateEngine {
|
||||||
|
|
||||||
|
renderTemplateSource(templateSource: Object, bindingContext?: KnockoutBindingContext, options?: Object): any[];
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////
|
||||||
|
// templateEngine.js
|
||||||
|
//////////////////////////////////
|
||||||
|
|
||||||
|
interface KnockoutTemplateEngine extends KnockoutNativeTemplateEngine {
|
||||||
|
|
||||||
|
createJavaScriptEvaluatorBlock(script: string): string;
|
||||||
|
|
||||||
|
makeTemplateSource(template: any, templateDocument?: Document): any;
|
||||||
|
|
||||||
|
renderTemplate(template: any, bindingContext: KnockoutBindingContext, options: Object, templateDocument: Document): any;
|
||||||
|
|
||||||
|
isTemplateRewritten(template: any, templateDocument: Document): boolean;
|
||||||
|
|
||||||
|
rewriteTemplate(template: any, rewriterCallback: Function, templateDocument: Document): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////
|
||||||
|
|
||||||
|
interface KnockoutStatic {
|
||||||
|
utils: KnockoutUtils;
|
||||||
|
memoization: KnockoutMemoization;
|
||||||
|
|
||||||
|
bindingHandlers: KnockoutBindingHandlers;
|
||||||
|
getBindingHandler(handler: string): KnockoutBindingHandler;
|
||||||
|
|
||||||
|
virtualElements: KnockoutVirtualElements;
|
||||||
|
extenders: KnockoutExtenders;
|
||||||
|
|
||||||
|
applyBindings(viewModelOrBindingContext?: any, rootNode?: any): void;
|
||||||
|
applyBindingsToDescendants(viewModelOrBindingContext: any, rootNode: any): void;
|
||||||
|
applyBindingAccessorsToNode(node: Node, bindings: (bindingContext: KnockoutBindingContext, node: Node) => {}, bindingContext: KnockoutBindingContext): void;
|
||||||
|
applyBindingAccessorsToNode(node: Node, bindings: {}, bindingContext: KnockoutBindingContext): void;
|
||||||
|
applyBindingAccessorsToNode(node: Node, bindings: (bindingContext: KnockoutBindingContext, node: Node) => {}, viewModel: any): void;
|
||||||
|
applyBindingAccessorsToNode(node: Node, bindings: {}, viewModel: any): void;
|
||||||
|
applyBindingsToNode(node: Node, bindings: any, viewModelOrBindingContext?: any): any;
|
||||||
|
|
||||||
|
subscribable: KnockoutSubscribableStatic;
|
||||||
|
observable: KnockoutObservableStatic;
|
||||||
|
|
||||||
|
computed: KnockoutComputedStatic;
|
||||||
|
pureComputed<T>(evaluatorFunction: () => T, context?: any): KnockoutComputed<T>;
|
||||||
|
pureComputed<T>(options: KnockoutComputedDefine<T>, context?: any): KnockoutComputed<T>;
|
||||||
|
|
||||||
|
observableArray: KnockoutObservableArrayStatic;
|
||||||
|
|
||||||
|
contextFor(node: any): any;
|
||||||
|
isSubscribable(instance: any): boolean;
|
||||||
|
toJSON(viewModel: any, replacer?: Function, space?: any): string;
|
||||||
|
toJS(viewModel: any): any;
|
||||||
|
isObservable(instance: any): boolean;
|
||||||
|
isWriteableObservable(instance: any): boolean;
|
||||||
|
isComputed(instance: any): boolean;
|
||||||
|
dataFor(node: any): any;
|
||||||
|
removeNode(node: Element): void;
|
||||||
|
cleanNode(node: Element): Element;
|
||||||
|
renderTemplate(template: Function, viewModel: any, options?: any, target?: any, renderMode?: any): any;
|
||||||
|
renderTemplate(template: string, viewModel: any, options?: any, target?: any, renderMode?: any): any;
|
||||||
|
unwrap<T>(value: KnockoutObservable<T> | T): T;
|
||||||
|
|
||||||
|
computedContext: KnockoutComputedContext;
|
||||||
|
|
||||||
|
//////////////////////////////////
|
||||||
|
// templateSources.js
|
||||||
|
//////////////////////////////////
|
||||||
|
|
||||||
|
templateSources: KnockoutTemplateSources;
|
||||||
|
|
||||||
|
//////////////////////////////////
|
||||||
|
// templateEngine.js
|
||||||
|
//////////////////////////////////
|
||||||
|
|
||||||
|
templateEngine: {
|
||||||
|
|
||||||
|
prototype: KnockoutTemplateEngine;
|
||||||
|
|
||||||
|
new (): KnockoutTemplateEngine;
|
||||||
|
};
|
||||||
|
|
||||||
|
//////////////////////////////////
|
||||||
|
// templateRewriting.js
|
||||||
|
//////////////////////////////////
|
||||||
|
|
||||||
|
templateRewriting: {
|
||||||
|
|
||||||
|
ensureTemplateIsRewritten(template: Node, templateEngine: KnockoutTemplateEngine, templateDocument: Document): any;
|
||||||
|
ensureTemplateIsRewritten(template: string, templateEngine: KnockoutTemplateEngine, templateDocument: Document): any;
|
||||||
|
|
||||||
|
memoizeBindingAttributeSyntax(htmlString: string, templateEngine: KnockoutTemplateEngine): any;
|
||||||
|
|
||||||
|
applyMemoizedBindingsToNextSibling(bindings: any, nodeName: string): string;
|
||||||
|
};
|
||||||
|
|
||||||
|
//////////////////////////////////
|
||||||
|
// nativeTemplateEngine.js
|
||||||
|
//////////////////////////////////
|
||||||
|
|
||||||
|
nativeTemplateEngine: {
|
||||||
|
|
||||||
|
prototype: KnockoutNativeTemplateEngine;
|
||||||
|
|
||||||
|
new (): KnockoutNativeTemplateEngine;
|
||||||
|
|
||||||
|
instance: KnockoutNativeTemplateEngine;
|
||||||
|
};
|
||||||
|
|
||||||
|
//////////////////////////////////
|
||||||
|
// jqueryTmplTemplateEngine.js
|
||||||
|
//////////////////////////////////
|
||||||
|
|
||||||
|
jqueryTmplTemplateEngine: {
|
||||||
|
|
||||||
|
prototype: KnockoutTemplateEngine;
|
||||||
|
|
||||||
|
renderTemplateSource(templateSource: Object, bindingContext: KnockoutBindingContext, options: Object): Node[];
|
||||||
|
|
||||||
|
createJavaScriptEvaluatorBlock(script: string): string;
|
||||||
|
|
||||||
|
addTemplate(templateName: string, templateMarkup: string): void;
|
||||||
|
};
|
||||||
|
|
||||||
|
//////////////////////////////////
|
||||||
|
// templating.js
|
||||||
|
//////////////////////////////////
|
||||||
|
|
||||||
|
setTemplateEngine(templateEngine: KnockoutNativeTemplateEngine): void;
|
||||||
|
|
||||||
|
renderTemplate(template: Function, dataOrBindingContext: KnockoutBindingContext, options: Object, targetNodeOrNodeArray: Node, renderMode: string): any;
|
||||||
|
renderTemplate(template: any, dataOrBindingContext: KnockoutBindingContext, options: Object, targetNodeOrNodeArray: Node, renderMode: string): any;
|
||||||
|
renderTemplate(template: Function, dataOrBindingContext: any, options: Object, targetNodeOrNodeArray: Node, renderMode: string): any;
|
||||||
|
renderTemplate(template: any, dataOrBindingContext: any, options: Object, targetNodeOrNodeArray: Node, renderMode: string): any;
|
||||||
|
renderTemplate(template: Function, dataOrBindingContext: KnockoutBindingContext, options: Object, targetNodeOrNodeArray: Node[], renderMode: string): any;
|
||||||
|
renderTemplate(template: any, dataOrBindingContext: KnockoutBindingContext, options: Object, targetNodeOrNodeArray: Node[], renderMode: string): any;
|
||||||
|
renderTemplate(template: Function, dataOrBindingContext: any, options: Object, targetNodeOrNodeArray: Node[], renderMode: string): any;
|
||||||
|
renderTemplate(template: any, dataOrBindingContext: any, options: Object, targetNodeOrNodeArray: Node[], renderMode: string): any;
|
||||||
|
|
||||||
|
renderTemplateForEach(template: Function, arrayOrObservableArray: any[], options: Object, targetNode: Node, parentBindingContext: KnockoutBindingContext): any;
|
||||||
|
renderTemplateForEach(template: any, arrayOrObservableArray: any[], options: Object, targetNode: Node, parentBindingContext: KnockoutBindingContext): any;
|
||||||
|
renderTemplateForEach(template: Function, arrayOrObservableArray: KnockoutObservable<any>, options: Object, targetNode: Node, parentBindingContext: KnockoutBindingContext): any;
|
||||||
|
renderTemplateForEach(template: any, arrayOrObservableArray: KnockoutObservable<any>, options: Object, targetNode: Node, parentBindingContext: KnockoutBindingContext): any;
|
||||||
|
|
||||||
|
expressionRewriting: {
|
||||||
|
bindingRewriteValidators: any;
|
||||||
|
parseObjectLiteral: { (objectLiteralString: string): any[] }
|
||||||
|
};
|
||||||
|
|
||||||
|
/////////////////////////////////
|
||||||
|
|
||||||
|
bindingProvider: {
|
||||||
|
instance: KnockoutBindingProvider;
|
||||||
|
new (): KnockoutBindingProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////
|
||||||
|
// selectExtensions.js
|
||||||
|
/////////////////////////////////
|
||||||
|
|
||||||
|
selectExtensions: {
|
||||||
|
|
||||||
|
readValue(element: HTMLElement): any;
|
||||||
|
|
||||||
|
writeValue(element: HTMLElement, value: any): void;
|
||||||
|
};
|
||||||
|
|
||||||
|
components: KnockoutComponents;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface KnockoutBindingProvider {
|
||||||
|
nodeHasBindings(node: Node): boolean;
|
||||||
|
getBindings(node: Node, bindingContext: KnockoutBindingContext): {};
|
||||||
|
getBindingAccessors?(node: Node, bindingContext: KnockoutBindingContext): { [key: string]: string; };
|
||||||
|
}
|
||||||
|
|
||||||
|
interface KnockoutComputedContext {
|
||||||
|
getDependenciesCount(): number;
|
||||||
|
isInitial: () => boolean;
|
||||||
|
isSleeping: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// refactored types into a namespace to reduce global pollution
|
||||||
|
// and used Union Types to simplify overloads (requires TypeScript 1.4)
|
||||||
|
//
|
||||||
|
declare module KnockoutComponentTypes {
|
||||||
|
|
||||||
|
interface Config {
|
||||||
|
viewModel?: ViewModelFunction | ViewModelSharedInstance | ViewModelFactoryFunction | AMDModule;
|
||||||
|
template: string | Node[]| DocumentFragment | TemplateElement | AMDModule;
|
||||||
|
synchronous?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ComponentConfig {
|
||||||
|
viewModel?: ViewModelFunction | ViewModelSharedInstance | ViewModelFactoryFunction | AMDModule;
|
||||||
|
template: any;
|
||||||
|
createViewModel?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EmptyConfig {
|
||||||
|
}
|
||||||
|
|
||||||
|
// common AMD type
|
||||||
|
interface AMDModule {
|
||||||
|
require: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// viewmodel types
|
||||||
|
interface ViewModelFunction {
|
||||||
|
(params?: any): any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ViewModelSharedInstance {
|
||||||
|
instance: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ViewModelFactoryFunction {
|
||||||
|
createViewModel: (params?: any, componentInfo?: ComponentInfo) => any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ComponentInfo {
|
||||||
|
element: Node;
|
||||||
|
templateNodes: Node[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TemplateElement {
|
||||||
|
element: string | Node;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Loader {
|
||||||
|
getConfig? (componentName: string, callback: (result: ComponentConfig) => void): void;
|
||||||
|
loadComponent? (componentName: string, config: ComponentConfig, callback: (result: Definition) => void): void;
|
||||||
|
loadTemplate? (componentName: string, templateConfig: any, callback: (result: Node[]) => void): void;
|
||||||
|
loadViewModel? (componentName: string, viewModelConfig: any, callback: (result: any) => void): void;
|
||||||
|
suppressLoaderExceptions?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Definition {
|
||||||
|
template: Node[];
|
||||||
|
createViewModel? (params: any, options: { element: Node; }): any;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface KnockoutComponents {
|
||||||
|
// overloads for register method:
|
||||||
|
register(componentName: string, config: KnockoutComponentTypes.Config | KnockoutComponentTypes.EmptyConfig): void;
|
||||||
|
|
||||||
|
isRegistered(componentName: string): boolean;
|
||||||
|
unregister(componentName: string): void;
|
||||||
|
get(componentName: string, callback: (definition: KnockoutComponentTypes.Definition) => void): void;
|
||||||
|
clearCachedDefinition(componentName: string): void
|
||||||
|
defaultLoader: KnockoutComponentTypes.Loader;
|
||||||
|
loaders: KnockoutComponentTypes.Loader[];
|
||||||
|
getComponentNameForNode(node: Node): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare var ko: KnockoutStatic;
|
||||||
|
|
||||||
|
declare module "knockout" {
|
||||||
|
export = ko;
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
/// <reference path="@ms/odsp.d.ts" />
|
||||||
|
/// <reference path="assertion-error/assertion-error.d.ts" />
|
||||||
|
/// <reference path="knockout/knockout.d.ts" />
|