Skip to content

Commit e773f21

Browse files
author
Gavin Barron
committed
initial commit
0 parents  commit e773f21

27 files changed

+1316
-0
lines changed

.gitignore

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
*.js.map
3+
package-lock.json
4+
**/node_modules/
5+
bin
6+
obj

AcceptMeeting/function.json

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"bindings": [
3+
{
4+
"name": "myQueueItem",
5+
"type": "queueTrigger",
6+
"direction": "in",
7+
"queueName": "accept-meeting",
8+
"connection": "travlerbotvkzmzc_STORAGE"
9+
}
10+
],
11+
"disabled": false
12+
}

AcceptMeeting/index.js

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
const https = require('https');
2+
3+
module.exports = function (context, myQueueItem) {
4+
context.log('Starting accept call');
5+
6+
/**
7+
* Generates a POST request (of Content-type ```application/json```)
8+
* @param {string} path the path, relative to the host, to which this request will be sent
9+
* @param {string} token the access token with which the request should be authenticated
10+
* @param {string} data the data which will be 'POST'ed
11+
* @param {callback} callback
12+
*/
13+
function postData (path, token, data, callback) {
14+
var self = this;
15+
const options = {
16+
host: 'graph.microsoft.com',
17+
path: path,
18+
method: 'POST',
19+
headers: {
20+
'Content-Type': 'application/json',
21+
Authorization: 'Bearer ' + token,
22+
'Content-Length': data.length
23+
}
24+
};
25+
26+
const req = https.request(options, res => {
27+
let responseData = '';
28+
context.log('In response');
29+
30+
res.on('data', chunk => {
31+
// context.log('got data');
32+
(responseData += chunk);
33+
});
34+
res.on('end', () => {
35+
// context.log('data end');
36+
// context.log(res.statusCode);
37+
// context.log(responseData);
38+
// context.log(JSON.parse(responseData));
39+
if (res.statusCode === 201) callback(null, JSON.parse(responseData));
40+
else callback(JSON.parse(responseData), null);
41+
});
42+
});
43+
44+
req.write(data);
45+
req.end();
46+
context.log('Made request');
47+
48+
req.on('error', error => {
49+
context.log('Bad error');
50+
callback(error, null)
51+
});
52+
}
53+
54+
const acceptData = {
55+
comment: '',
56+
sendResponse: true
57+
};
58+
const data = JSON.stringify(acceptData);
59+
context.log(`Accept call ${data}`);
60+
61+
postData(`/v1.0/${myQueueItem.meeting}/accept`, myQueueItem.accessToken, data, (error, data) => {
62+
if(error){
63+
context.done(error);
64+
return;
65+
}
66+
context.done();
67+
});
68+
};
69+

CalendarWebHookHandler/function.json

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"bindings": [
3+
{
4+
"type": "httpTrigger",
5+
"direction": "in",
6+
"name": "req",
7+
"authLevel": "function"
8+
},
9+
{
10+
"type": "http",
11+
"direction": "out",
12+
"name": "res"
13+
}
14+
],
15+
"disabled": false
16+
}

CalendarWebHookHandler/index.js

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
2+
const azureStorage = require('azure-storage');
3+
const cfg = require('../common/config')
4+
5+
module.exports = function (context, data) {
6+
context.log('Calendar Webhook Handler was triggered!');
7+
8+
// Handle subscription request
9+
if (data.query.validationToken) {
10+
context.log('registering new hook');
11+
context.res = {
12+
status: 200,
13+
body: data.query.validationToken
14+
};
15+
context.done();
16+
return;
17+
}
18+
19+
const storageConnectionString = process.env.storageConnectionString || 'DefaultEndpointsProtocol=https;AccountName=travelerbot;AccountKey=b2cmbtSrWvJ2ebxO0PD1CQ+svlaxjUY3xtC8DdzJyeFXRGVgnsGhZkqU82rvgMiXE0ybEaWlTZpEsf/65drmNA==;EndpointSuffix=core.windows.net';
20+
const queueSvc = azureStorage.createQueueService(storageConnectionString);
21+
// Write incoming notifications onto a queue.
22+
queueSvc.createQueueIfNotExists('hook-recieved', (err, result, response) => {
23+
if (err){
24+
// this should be a log for the dev, not a message to the user
25+
session.send('There was an error creating the hook-recieved queue: '+ err);
26+
context.done(err);
27+
return;
28+
}
29+
// enqueue a message to process the webhook request.
30+
const request = data.body;
31+
context.log(request);
32+
const notificationCount = request.value.length;
33+
let processed = 0;
34+
// web hook requests can contain multiple notifications
35+
for (let hook of request.value) {
36+
let msg = JSON.stringify(hook);
37+
context.log(msg);
38+
var queueMessageBuffer = new Buffer(msg).toString('base64');
39+
// only take action when the clientState is vaild.
40+
// return the 202 and do notthing if the clientState is vaild.
41+
if (hook.clientState !== cfg.CLIENT_STATE) {
42+
// add a log entry so that this can be investigated
43+
context.log('Unknown sender! Investigate the source of this message')
44+
context.log(data);
45+
context.res = { status: 202, body: 'All notifications processed' };
46+
context.done();
47+
return;
48+
break;
49+
}
50+
queueSvc.createMessage('hook-recieved', queueMessageBuffer, (err, result, response) => {
51+
processed++;
52+
// fail on any message not getting queued properly
53+
if (err) {
54+
context.log('error sending webhook messge on hook-recieved queue');
55+
context.done(err);
56+
return;
57+
}
58+
context.log('webhook messge on put onto hook-recieved queue');
59+
if (processed == notificationCount) {
60+
context.res = { status: 202, body: 'All notifications processed' };
61+
context.done();
62+
}
63+
});
64+
}
65+
});
66+
}

CalendarWebHookHandler/package.json

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"name": "calendar-webhook-handler",
3+
"version": "1.0.0",
4+
"description": "Handles incoming webhook requests",
5+
"main": "index.js",
6+
"author": "Gavin Barron",
7+
"license": "MIT",
8+
"private": false,
9+
"dependencies": {
10+
"azure-storage": "^2.8.0"
11+
}
12+
}

CreateTravelBooking/function.json

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"bindings": [
3+
{
4+
"name": "myQueueItem",
5+
"type": "queueTrigger",
6+
"direction": "in",
7+
"queueName": "add-travel-meeting",
8+
"connection": "travlerbotvkzmzc_STORAGE"
9+
}
10+
],
11+
"disabled": false
12+
}

CreateTravelBooking/index.js

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
const https = require('https');
2+
3+
module.exports = function (context, myQueueItem) {
4+
context.log('Starting travel meeting booking');
5+
6+
/**
7+
* Generates a POST request (of Content-type ```application/json```)
8+
* @param {string} path the path, relative to the host, to which this request will be sent
9+
* @param {string} token the access token with which the request should be authenticated
10+
* @param {string} data the data which will be 'POST'ed
11+
* @param {callback} callback
12+
*/
13+
function postData (path, token, data, callback) {
14+
var self = this;
15+
const options = {
16+
host: 'graph.microsoft.com',
17+
path: path,
18+
method: 'POST',
19+
headers: {
20+
'Content-Type': 'application/json',
21+
Authorization: 'Bearer ' + token,
22+
'Content-Length': data.length
23+
}
24+
};
25+
26+
const req = https.request(options, res => {
27+
let subscriptionData = '';
28+
29+
res.on('data', chunk => (subscriptionData += chunk));
30+
res.on('end', () => {
31+
if (res.statusCode === 201) callback(null, JSON.parse(subscriptionData));
32+
else callback(JSON.parse(subscriptionData), null);
33+
});
34+
});
35+
36+
req.write(data);
37+
req.end();
38+
39+
req.on('error', error => callback(error, null));
40+
}
41+
context.log(myQueueItem.start);
42+
context.log(myQueueItem.durationInMins)
43+
const start = new Date(myQueueItem.start);
44+
const MS_PER_MINUTE = 60000;
45+
const end = new Date(start.getTime() + myQueueItem.durationInMins * MS_PER_MINUTE).toISOString();
46+
47+
context.log(myQueueItem.start);
48+
context.log(end);
49+
50+
const meeting = {
51+
subject: "Travel time",
52+
start: {
53+
dateTime: myQueueItem.start,
54+
timeZone: "UTC"
55+
},
56+
end: {
57+
dateTime: end,
58+
timeZone: "UTC"
59+
}
60+
}
61+
62+
postData(`/v1.0/me/events`, myQueueItem.accessToken, JSON.stringify(meeting), (error, data) => {
63+
if(error){
64+
context.done(error);
65+
return;
66+
}
67+
context.done();
68+
});
69+
};

PostDeployScripts/runGulp.cmd

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
@echo off
2+
setlocal
3+
4+
rem enumerate each folder under root and check for existence of gulpfile.cs
5+
rem if gulpfile exists, run the default gulp task
6+
for /d %%d in (..\wwwroot\*) do (
7+
echo check %%d
8+
pushd %%d
9+
if exist package.json (
10+
echo npm install --production
11+
call npm install --production
12+
) else (
13+
echo no package.json found
14+
)
15+
16+
if exist gulpfile.js (
17+
echo run gulp
18+
gulp
19+
) else (
20+
echo no gulpfile.js found
21+
)
22+
popd
23+
)
24+
25+
echo record deployment timestamp
26+
date /t >> ..\deployment.log
27+
time /t >> ..\deployment.log
28+
echo ---------------------- >> ..\deployment.log
29+
echo Deployment done
30+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
var DocDBUtils = {
2+
getOrCreateDatabase: function(client, databaseId, callback) {
3+
var querySpec = {
4+
query: 'SELECT * FROM root r WHERE r.id=@id',
5+
parameters: [{
6+
name: '@id',
7+
value: databaseId
8+
}]
9+
};
10+
11+
client.queryDatabases(querySpec).toArray(function(err, results) {
12+
if (err) {
13+
callback(err);
14+
}
15+
16+
if (!err && results.length === 0) {
17+
client.createDatabase({
18+
id: databaseId
19+
}, function(err, created) {
20+
callback(null, created);
21+
});
22+
} else {
23+
callback(null, results[0]);
24+
}
25+
});
26+
},
27+
28+
getOrCreateCollection: function(client, databaseLink, collectionId, callback) {
29+
var querySpec = {
30+
query: 'SELECT * FROM root r WHERE r.id=@id',
31+
parameters: [{
32+
name: '@id',
33+
value: collectionId
34+
}]
35+
};
36+
37+
client.queryCollections(databaseLink, querySpec).toArray(function(err, results) {
38+
if (err) {
39+
callback(err);
40+
}
41+
42+
if (!err && results.length === 0) {
43+
client.createCollection(databaseLink, {
44+
id: collectionId
45+
}, function(err, created) {
46+
callback(null, created);
47+
});
48+
} else {
49+
callback(null, results[0]);
50+
}
51+
});
52+
}
53+
};
54+
55+
module.exports = DocDBUtils;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"bindings": [
3+
{
4+
"name": "myQueueItem",
5+
"type": "queueTrigger",
6+
"direction": "in",
7+
"queueName": "hook-recieved",
8+
"connection": "travlerbotvkzmzc_STORAGE"
9+
},
10+
{
11+
"type": "http",
12+
"name": "res",
13+
"direction": "out"
14+
},
15+
{
16+
"type": "bot",
17+
"name": "$return",
18+
"botId": "functionbot",
19+
"direction": "out"
20+
}
21+
],
22+
"disabled": false
23+
}

0 commit comments

Comments
 (0)