From 6ac7c2ee77c07a1c1df21c30ce5141261d18041e Mon Sep 17 00:00:00 2001 From: Shinji Takae Date: Wed, 23 Oct 2019 15:08:25 +0900 Subject: [PATCH 1/6] Change FB graph API version to 4.0 --- lib/facebook/setup.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/facebook/setup.js b/lib/facebook/setup.js index 1df238a..97f138d 100644 --- a/lib/facebook/setup.js +++ b/lib/facebook/setup.js @@ -47,7 +47,7 @@ module.exports = function fbSetup(api, bot, logError, optionalParser, optionalRe stageName: lambdaDetails.alias }).then(data => { if (options['configure-fb-bot']) { - let token, pageAccessToken; + let token, pageAccessToken, pageId, subscribedFields; return Promise.resolve().then(() => { if (data.variables && data.variables.facebookVerifyToken) @@ -71,11 +71,18 @@ module.exports = function fbSetup(api, bot, logError, optionalParser, optionalRe console.log(`\nYour webhook URL is: ${color.cyan}${lambdaDetails.apiUrl}/facebook${color.reset}\n`); console.log(`Your verify token is: ${color.cyan}${token}${color.reset}\n`); - return prompt(['Facebook page access token', 'Facebook App Secret']); + return prompt([ + 'Facebook page access token', + 'Facebook App Secret', + 'Facebook page ID', + 'Subscribed fields (comma separated with no spaces)' + ]); }) .then(results => { console.log('\n'); pageAccessToken = results['Facebook page access token']; + pageId = results['Facebook page ID']; + subscribedFields = results['Subscribed fields'].split(',').map(field => `'${field}'`).join(','); const deployment = { restApiId: lambdaDetails.apiId, stageName: lambdaDetails.alias, @@ -90,7 +97,7 @@ module.exports = function fbSetup(api, bot, logError, optionalParser, optionalRe return utils.apiGatewayPromise.createDeploymentPromise(deployment); }) - .then(() => rp.post(`https://graph.facebook.com/v2.6/me/subscribed_apps?access_token=${pageAccessToken}`)); + .then(() => rp.post(`https://graph.facebook.com/v4.0/${pageId}/subscribed_apps?access_token=${pageAccessToken}&subscribed_fields=[${subscribedFields}]`)); } }); }) From a4f2e55d190f8b024d6a209b233abf6bb1cebdbe Mon Sep 17 00:00:00 2001 From: Shinji Takae Date: Wed, 23 Oct 2019 15:17:25 +0900 Subject: [PATCH 2/6] Fix key to get subscribed fields --- lib/facebook/setup.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/facebook/setup.js b/lib/facebook/setup.js index 97f138d..b672c8a 100644 --- a/lib/facebook/setup.js +++ b/lib/facebook/setup.js @@ -82,7 +82,7 @@ module.exports = function fbSetup(api, bot, logError, optionalParser, optionalRe console.log('\n'); pageAccessToken = results['Facebook page access token']; pageId = results['Facebook page ID']; - subscribedFields = results['Subscribed fields'].split(',').map(field => `'${field}'`).join(','); + subscribedFields = results['Subscribed fields (comma separated with no spaces)'].split(',').map(field => `'${field}'`).join(','); const deployment = { restApiId: lambdaDetails.apiId, stageName: lambdaDetails.alias, From b4b4b29b2ea7fd8484b4274c3236aa40c12f3d2b Mon Sep 17 00:00:00 2001 From: Shinji Takae Date: Wed, 23 Oct 2019 16:30:11 +0900 Subject: [PATCH 3/6] Remove spaces in subscribed fileds --- lib/facebook/setup.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/facebook/setup.js b/lib/facebook/setup.js index b672c8a..1659d4d 100644 --- a/lib/facebook/setup.js +++ b/lib/facebook/setup.js @@ -75,14 +75,14 @@ module.exports = function fbSetup(api, bot, logError, optionalParser, optionalRe 'Facebook page access token', 'Facebook App Secret', 'Facebook page ID', - 'Subscribed fields (comma separated with no spaces)' + 'Subscribed fields (comma separated)' ]); }) .then(results => { console.log('\n'); pageAccessToken = results['Facebook page access token']; pageId = results['Facebook page ID']; - subscribedFields = results['Subscribed fields (comma separated with no spaces)'].split(',').map(field => `'${field}'`).join(','); + subscribedFields = results['Subscribed fields (comma separated)'].split(',').map(field => `'${field.trim()}'`).join(','); const deployment = { restApiId: lambdaDetails.apiId, stageName: lambdaDetails.alias, From 254adde62b6cdd8bd43baa9e90e4faabbb79bd54 Mon Sep 17 00:00:00 2001 From: Shinji Takae Date: Wed, 23 Oct 2019 16:58:18 +0900 Subject: [PATCH 4/6] Fix indent --- lib/facebook/setup.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/facebook/setup.js b/lib/facebook/setup.js index 1659d4d..41a258c 100644 --- a/lib/facebook/setup.js +++ b/lib/facebook/setup.js @@ -97,7 +97,7 @@ module.exports = function fbSetup(api, bot, logError, optionalParser, optionalRe return utils.apiGatewayPromise.createDeploymentPromise(deployment); }) - .then(() => rp.post(`https://graph.facebook.com/v4.0/${pageId}/subscribed_apps?access_token=${pageAccessToken}&subscribed_fields=[${subscribedFields}]`)); + .then(() => rp.post(`https://graph.facebook.com/v4.0/${pageId}/subscribed_apps?access_token=${pageAccessToken}&subscribed_fields=[${subscribedFields}]`)); } }); }) From 41a1c5b782f8e00eea013347f65ebb091f8d7214 Mon Sep 17 00:00:00 2001 From: Shinji Takae Date: Fri, 25 Oct 2019 00:43:26 +0900 Subject: [PATCH 5/6] Add test to facebook setup --- spec/facebook/facebook-setup-spec.js | 90 +++++++++++++++++++++++++++- 1 file changed, 88 insertions(+), 2 deletions(-) diff --git a/spec/facebook/facebook-setup-spec.js b/spec/facebook/facebook-setup-spec.js index 84a066b..406e44e 100644 --- a/spec/facebook/facebook-setup-spec.js +++ b/spec/facebook/facebook-setup-spec.js @@ -1,6 +1,7 @@ -/*global require, describe, it, expect, beforeEach, jasmine*/ +/*global require, describe, it, expect, beforeEach, jasmine, spyOn*/ 'use strict'; var underTest = require('../../lib/facebook/setup'); +var https = require('https'); describe('Facebook setup', () => { var api, bot, logError, parser, responder, botPromise, botResolve, botReject; beforeEach(() => { @@ -283,6 +284,91 @@ describe('Facebook setup', () => { }); }); }); - + }); + describe('post deploy step processor', () => { + it('is called once', () => { + expect(api.addPostDeployStep.calls.count()).toEqual(1); + expect(api.addPostDeployStep).toHaveBeenCalledWith('facebook', jasmine.any(Function)); + }); + describe('processing user input', () => { + const singleInput = (data) => { + return new Promise((resolve, reject) => { + setTimeout(() => { + process.stdin.emit('data', data); + resolve(); + }, 500); + }); + }; + const emulateUserInputPromise = (accessToken, appSecret, pageId, subscribedField) => { + return new Promise((resolve, reject) => { + singleInput(accessToken) + .then(() => singleInput(appSecret)) + .then(() => singleInput(pageId)) + .then(() => singleInput(subscribedField)) + .then(() => setTimeout(() => resolve()), 500); + }); + }; + let options = {'configure-fb-bot': true}; + let lambdaDetails = {apiUrl: 'https://testlambdafunction.com/latest'}; + let getStagePromise = () => Promise.resolve({variables: {}}); + let createDeploymentPromise = () => Promise.resolve(); + let utils = {apiGatewayPromise: {getStagePromise: getStagePromise, createDeploymentPromise: createDeploymentPromise}}; + var handler; + beforeEach(() => { + handler = api.addPostDeployStep.calls.argsFor(0)[1]; + }); + it('returns webhook URL if all parameters are valid', (done) => { + handler(options, lambdaDetails, utils).then(url => { + expect(url).toEqual(`${lambdaDetails.apiUrl}/facebook`); + done(); + }); + emulateUserInputPromise('accessToken\n', 'appSecret\n', 'pageId\n', 'messages, messaging_postbacks\n').then(() => { + https.request.calls[0].respond(200, 'OK'); + }); + }); + it('returns webhook URL if apiGateway already has facebookVerifyToken', (done) => { + getStagePromise = () => Promise.resolve({variables: {facebookVerifyToken: 'facebookVerityToken'}}); + utils = {apiGatewayPromise: {getStagePromise: getStagePromise, createDeploymentPromise: createDeploymentPromise}}; + handler(options, lambdaDetails, utils).then(url => { + expect(url).toEqual(`${lambdaDetails.apiUrl}/facebook`); + done(); + }); + emulateUserInputPromise('accessToken\n', 'appSecret\n', 'pageId\n', 'messages, messaging_postbacks\n').then(() => { + https.request.calls[0].respond(200, 'OK'); + }); + }); + it('shows deprecation warning and returns webhook URL if apiGateway does not return \'variables\' key', (done) => { + spyOn(console, 'log'); + getStagePromise = () => Promise.resolve({}); + utils = {apiGatewayPromise: {getStagePromise: getStagePromise, createDeploymentPromise: createDeploymentPromise}}; + handler(options, lambdaDetails, utils).then(url => { + expect(console.log.calls.mostRecent().args[0]).toMatch(/Deprecation warning/); + expect(url).toEqual(`${lambdaDetails.apiUrl}/facebook`); + done(); + }); + emulateUserInputPromise('accessToken\n', 'appSecret\n', 'pageId\n', 'messages, messaging_postbacks\n').then(() => { + https.request.calls[0].respond(200, 'OK'); + }); + }); + it('returns webhook URL even if \'configure-fb-bot\' option is not specified', (done) => { + options = { 'configure-fb-bot': false }; + handler(options, lambdaDetails, utils).then(url => { + expect(url).toEqual(`${lambdaDetails.apiUrl}/facebook`); + done(); + }); + emulateUserInputPromise('accessToken\n', 'appSecret\n', 'pageId\n', 'messages, messaging_postbacks\n').then(() => { + https.request.calls[0].respond(200, 'OK'); + }); + }); + it('returns webhook URL even if subscribed fields ended with comma and space', (done) => { + handler(options, lambdaDetails, utils).then(url => { + expect(url).toEqual(`${lambdaDetails.apiUrl}/facebook`); + done(); + }); + emulateUserInputPromise('accessToken\n', 'appSecret\n', 'pageId\n', 'messages, \n').then(() => { + https.request.calls[0].respond(200, 'OK'); + }); + }); + }); }); }); From 31dc77bb943343593fb11e0cb4538eb1b3395514 Mon Sep 17 00:00:00 2001 From: Shinji Takae Date: Fri, 25 Oct 2019 00:58:14 +0900 Subject: [PATCH 6/6] Remove unused 'reject' param to pass the test --- spec/facebook/facebook-setup-spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/facebook/facebook-setup-spec.js b/spec/facebook/facebook-setup-spec.js index 406e44e..6c23e56 100644 --- a/spec/facebook/facebook-setup-spec.js +++ b/spec/facebook/facebook-setup-spec.js @@ -292,7 +292,7 @@ describe('Facebook setup', () => { }); describe('processing user input', () => { const singleInput = (data) => { - return new Promise((resolve, reject) => { + return new Promise((resolve) => { setTimeout(() => { process.stdin.emit('data', data); resolve(); @@ -300,7 +300,7 @@ describe('Facebook setup', () => { }); }; const emulateUserInputPromise = (accessToken, appSecret, pageId, subscribedField) => { - return new Promise((resolve, reject) => { + return new Promise((resolve) => { singleInput(accessToken) .then(() => singleInput(appSecret)) .then(() => singleInput(pageId))