Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Seed the project with deploy to Azure botton and basic action response
- Loading branch information
Showing
with
266 additions
and 60 deletions.
- +26 −60 .gitignore
- +3 −0 .gitmodules
- +4 −0 README.md
- +65 −0 action/ActionProcessor.js
- +16 −0 action/function.json
- +20 −0 action/index.js
- +116 −0 azuredeploy.json
- +1 −0 host.json
- +15 −0 package.json
| @@ -1,61 +1,27 @@ | ||
| # Logs | ||
| logs | ||
| *.log | ||
| npm-debug.log* | ||
| yarn-debug.log* | ||
| yarn-error.log* | ||
|
|
||
| # Runtime data | ||
| pids | ||
| *.pid | ||
| *.seed | ||
| *.pid.lock | ||
|
|
||
| # Directory for instrumented libs generated by jscoverage/JSCover | ||
| lib-cov | ||
|
|
||
| # Coverage directory used by tools like istanbul | ||
| coverage | ||
|
|
||
| # nyc test coverage | ||
| .nyc_output | ||
|
|
||
| # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) | ||
| .grunt | ||
|
|
||
| # Bower dependency directory (https://bower.io/) | ||
| bower_components | ||
|
|
||
| # node-waf configuration | ||
| .lock-wscript | ||
|
|
||
| # Compiled binary addons (https://nodejs.org/api/addons.html) | ||
| build/Release | ||
|
|
||
| # Dependency directories | ||
| node_modules/ | ||
| jspm_packages/ | ||
|
|
||
| # TypeScript v1 declaration files | ||
| typings/ | ||
|
|
||
| # Optional npm cache directory | ||
| .npm | ||
|
|
||
| # Optional eslint cache | ||
| .eslintcache | ||
|
|
||
| # Optional REPL history | ||
| .node_repl_history | ||
|
|
||
| # Output of 'npm pack' | ||
| *.tgz | ||
|
|
||
| # Yarn Integrity file | ||
| .yarn-integrity | ||
|
|
||
| # dotenv environment variables file | ||
| .env | ||
|
|
||
| # next.js build output | ||
| .next | ||
| bin | ||
| obj | ||
| csx | ||
| .vs | ||
| edge | ||
| Publish | ||
| .vscode | ||
|
|
||
| *.user | ||
| *.suo | ||
| *.cscfg | ||
| *.Cache | ||
| project.lock.json | ||
|
|
||
| /packages | ||
| /node_modules | ||
| /TestResults | ||
|
|
||
| /tools/NuGet.exe | ||
| /App_Data | ||
| /secrets | ||
| /data | ||
| .secrets | ||
| appsettings.json | ||
| local.settings.json | ||
| npm-debug.log |
| @@ -0,0 +1,3 @@ | ||
| [submodule "outlook-actionable-messages-node-token-validation"] | ||
| path = outlook-actionable-messages-node-token-validation | ||
| url = git@github.com:OfficeDev/outlook-actionable-messages-node-token-validation.git |
| @@ -0,0 +1,65 @@ | ||
| var validation = require('../outlook-actionable-messages-node-token-validation/ActionableMessageTokenValidator'); | ||
|
|
||
| class ActionProcessor{ | ||
| static process(headers, body, cb, logFun = null){ | ||
| var auth = headers['authorization'].trim().split(' '); | ||
| if (auth.length == 2 && auth[0].toLowerCase() == 'bearer') { | ||
| var token = auth[1]; | ||
| }else{ | ||
| cb({ | ||
| status: 401, | ||
| body: "Invalid auth header" | ||
| }); | ||
| return; | ||
| } | ||
|
|
||
| var validator = new validation.ActionableMessageTokenValidator(); | ||
|
|
||
| // Set targetUrl with your service domain URL. | ||
| // For example, if the service URL is https://api.xyz.com/finance/expense?id=1234, | ||
| // then set it to https://api.xyz.com | ||
| var targetUrl = null; | ||
|
|
||
| // validateToken will verify the following | ||
| // 1. The token is issued by Microsoft and its digital signature is valid. | ||
| // 2. The token has not expired. | ||
| // 3. The audience claim matches the service domain URL. | ||
| validator.validateToken( | ||
| token, | ||
| null, | ||
| function (err, result) { | ||
| if (err) { | ||
| logFun('error: ' + err.message); | ||
| cb({ | ||
| status: 401, | ||
| body: err.message | ||
| }); | ||
| return; | ||
| } | ||
| // We have a valid token. We will verify the sender and the action performer. | ||
| // You should replace the code below with your own validation logic. | ||
| // In this example, we verify that the email is sent by expense@contoso.com | ||
| // and the action performer is someone with a @contoso.com email address. | ||
| // | ||
| // You should also return the CARD-ACTION-STATUS header in the response. | ||
| // The value of the header will be displayed to the user. | ||
|
|
||
| var card = { | ||
| "@type": "MessageCard", | ||
| "@context": "http://schema.org/extensions", | ||
| "summary": "Hello " + result.actionPerformer + ". Your action email was from " + result.sender, | ||
| "title": "Hello " + result.actionPerformer, | ||
| "themeColor": "009F9C" | ||
| } | ||
|
|
||
| cb({ | ||
| status: 200, | ||
| headers: {'CARD-UPDATE-IN-BODY': 'true'}, | ||
| body: card | ||
| }) | ||
| }); | ||
|
|
||
| } | ||
| } | ||
|
|
||
| exports.ActionProcessor = ActionProcessor; |
| @@ -0,0 +1,16 @@ | ||
| { | ||
| "disabled": false, | ||
| "bindings": [ | ||
| { | ||
| "authLevel": "function", | ||
| "type": "httpTrigger", | ||
| "direction": "in", | ||
| "name": "req" | ||
| }, | ||
| { | ||
| "type": "http", | ||
| "direction": "out", | ||
| "name": "res" | ||
| } | ||
| ] | ||
| } |
| @@ -0,0 +1,20 @@ | ||
| var processor = require('./ActionProcessor'); | ||
|
|
||
| module.exports = function (context, req) { | ||
| context.log('JavaScript HTTP trigger function processed a request.'); | ||
|
|
||
| if (req.body) { | ||
|
|
||
| processor.ActionProcessor.process(req.headers, req.body, function(res){ | ||
| context.res = res; | ||
| context.done(); | ||
| }, context.log) | ||
| } | ||
| else { | ||
| context.res = { | ||
| status: 400, | ||
| body: "Invalid request" | ||
| }; | ||
| context.done(); | ||
| } | ||
| }; |
| @@ -0,0 +1,116 @@ | ||
| { | ||
| "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", | ||
| "contentVersion": "1.0.0.0", | ||
| "parameters": { | ||
| "appName": { | ||
| "type": "string", | ||
| "metadata": { | ||
| "description": "The name of the function app that you wish to create." | ||
| } | ||
| }, | ||
| "storageAccountType": { | ||
| "type": "string", | ||
| "defaultValue": "Standard_LRS", | ||
| "allowedValues": [ | ||
| "Standard_LRS", | ||
| "Standard_GRS", | ||
| "Standard_RAGRS" | ||
| ], | ||
| "metadata": { | ||
| "description": "Storage Account type" | ||
| } | ||
| }, | ||
| "repoUrl": { | ||
| "type": "string" | ||
| }, | ||
| "branch": { | ||
| "type": "string" | ||
| } | ||
| }, | ||
| "variables": { | ||
| "functionAppName": "[parameters('appName')]", | ||
| "hostingPlanName": "[parameters('appName')]", | ||
| "storageAccountName": "[concat(uniquestring(resourceGroup().id), 'azfunctions')]", | ||
| "storageAccountid": "[concat(resourceGroup().id,'/providers/','Microsoft.Storage/storageAccounts/', variables('storageAccountName'))]" | ||
| }, | ||
| "resources": [ | ||
| { | ||
| "type": "Microsoft.Storage/storageAccounts", | ||
| "name": "[variables('storageAccountName')]", | ||
| "apiVersion": "2016-12-01", | ||
| "location": "[resourceGroup().location]", | ||
| "kind": "Storage", | ||
| "sku": { | ||
| "name": "[parameters('storageAccountType')]" | ||
| } | ||
| }, | ||
| { | ||
| "type": "Microsoft.Web/serverfarms", | ||
| "apiVersion": "2015-04-01", | ||
| "name": "[variables('hostingPlanName')]", | ||
| "location": "[resourceGroup().location]", | ||
| "properties": { | ||
| "name": "[variables('hostingPlanName')]", | ||
| "computeMode": "Dynamic", | ||
| "sku": "Dynamic" | ||
| } | ||
| }, | ||
| { | ||
| "apiVersion": "2015-08-01", | ||
| "type": "Microsoft.Web/sites", | ||
| "name": "[variables('functionAppName')]", | ||
| "location": "[resourceGroup().location]", | ||
| "kind": "functionapp", | ||
| "dependsOn": [ | ||
| "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]", | ||
| "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]" | ||
| ], | ||
| "properties": { | ||
| "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]", | ||
| "siteConfig": { | ||
| "appSettings": [ | ||
| { | ||
| "name": "AzureWebJobsDashboard", | ||
| "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';AccountKey=', listKeys(variables('storageAccountid'),'2015-05-01-preview').key1)]" | ||
| }, | ||
| { | ||
| "name": "AzureWebJobsStorage", | ||
| "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';AccountKey=', listKeys(variables('storageAccountid'),'2015-05-01-preview').key1)]" | ||
| }, | ||
| { | ||
| "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING", | ||
| "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';AccountKey=', listKeys(variables('storageAccountid'),'2015-05-01-preview').key1)]" | ||
| }, | ||
| { | ||
| "name": "WEBSITE_CONTENTSHARE", | ||
| "value": "[toLower(variables('functionAppName'))]" | ||
| }, | ||
| { | ||
| "name": "FUNCTIONS_EXTENSION_VERSION", | ||
| "value": "~1" | ||
| }, | ||
| { | ||
| "name": "WEBSITE_NODE_DEFAULT_VERSION", | ||
| "value": "6.5.0" | ||
| } | ||
| ] | ||
| } | ||
| }, | ||
| "resources": [ | ||
| { | ||
| "apiVersion": "2015-08-01", | ||
| "name": "web", | ||
| "type": "sourcecontrols", | ||
| "dependsOn": [ | ||
| "[resourceId('Microsoft.Web/sites/', parameters('appName'))]" | ||
| ], | ||
| "properties": { | ||
| "RepoUrl": "[parameters('repoUrl')]", | ||
| "branch": "[parameters('branch')]", | ||
| "IsManualIntegration": "true" | ||
| } | ||
| } | ||
| ] | ||
| } | ||
| ] | ||
| } |
| @@ -0,0 +1 @@ | ||
| { } |
| @@ -0,0 +1,15 @@ | ||
| { | ||
| "name": "helloactionablemessages", | ||
| "version": "1.0.0", | ||
| "description": "Actionable Message Sample", | ||
| "scripts": { | ||
| "code": "func host start --debug vscode" | ||
| }, | ||
| "dependencies": { | ||
| "jsonwebtoken": "7.x", | ||
| "request": "2.x", | ||
| "rsa-pem-from-mod-exp": "^0.8.4", | ||
| "base64url": "^1.0.6" | ||
| }, | ||
| "license": "MIT" | ||
| } |