From cec5073c23e46d1edc23888eece0f84d0099dbd0 Mon Sep 17 00:00:00 2001 From: Matt Kruse Date: Mon, 5 Feb 2018 22:32:33 -0600 Subject: [PATCH 1/5] Add general support for upcoming request types Allows alexa-app to handle future unknown request types by assigning a general on() function to map request types to handlers. --- README.md | 16 ++++++++++++++++ index.js | 16 ++++++++++++++-- types/index.d.ts | 3 +++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 11291fd..15a2483 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ * [Display.ElementSelected](#display-element-selected) * [AudioPlayer Event Request](#audioplayer-event-request) * [PlaybackController Event Request](#playbackcontroller-event-request) + * [Other Event Request](#other-event-request) * [Execute Code On Every Request](#execute-code-on-every-request) * [pre()](#pre) * [post()](#post) @@ -228,6 +229,7 @@ response.audioPlayerStop() response.audioPlayerClearQueue([ String clearBehavior ]) // tell Alexa whether the user's session is over; sessions end by default +// pass null or undefined to leave shouldEndSession undefined in the response, to satisfy newer API's // you can optionally pass a reprompt message response.shouldEndSession(boolean end [, String reprompt] ) @@ -546,6 +548,20 @@ app.playbackController('NextCommandIssued', (request, response) => { Note that some device interactions don't always produce PlaybackController events. See the [PlaybackController Interface Introduction](https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/custom-playbackcontroller-interface-reference#introduction) for more details. +### Other Event Request + +Handle any new requests that don't have an explicit handler type available (such as new or pre-release features) using the general on() and passing the event type. + +The following example will handle an imaginary request of type `DeviceEngine.InputHandler` if was added to the Alexa API. + +```javascript +app.on('DeviceEngine.InputHandler', (request, response, request_json) => { + response.say("You triggered an event from device "+request_json.request.event.deviceName); +}); +``` + +Note that the raw request json is sent as the 3rd parameter to make sure the handler function has access to all data in the case that the request format differs from other handler types. + ## Execute Code On Every Request In addition to specific event handlers, you can define functions that will run on every request. diff --git a/index.js b/index.js index 201535e..7665a10 100644 --- a/index.js +++ b/index.js @@ -112,7 +112,12 @@ alexa.response = function(session) { return this; }; this.shouldEndSession = function(bool, reprompt) { - this.response.response.shouldEndSession = bool; + if (bool===null || typeof bool=="undefined") { + delete this.response.response.shouldEndSession; + } + else { + this.response.response.shouldEndSession = bool; + } if (reprompt) { this.reprompt(reprompt); } @@ -457,7 +462,12 @@ alexa.app = function(name) { } self.intents[intentName] = new alexa.intent(intentName, schema, func); }; - + // handle custom/future request types + this.requestHandlers = {}; + this.on = function(handlerName, handler) { + self.requestHandlers[handlerName] = handler; + }; + this.customSlots = {}; this.customSlot = function(slotName, values) { self.customSlots[slotName] = []; @@ -598,6 +608,8 @@ alexa.app = function(name) { } else { throw "NO_DISPLAY_ELEMENT_SELECTED_FUNCTION"; } + } else if (typeof self.requestHandlers[requestType] === "function") { + return Promise.resolve(self.requestHandlers[requestType](request, response, request_json)); } else { throw "INVALID_REQUEST_TYPE"; } diff --git a/types/index.d.ts b/types/index.d.ts index a16ec9f..6422dd4 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -78,6 +78,9 @@ export class app { playbackControllerEventHandlers: {[name: string]: PlaybackController}; playbackController: (eventName: string, func: RequestHandler) => void; + requestHandlers: {[name: string]: handler}; + on: (handlerName: string, handler: RequestHandler) => void; + /** TODO: Figure out what the promise actually contains */ request: (requestJSON: alexa.Request) => Promise; From c90db65e0d0f4b743d9094cd8d56878c9366206e Mon Sep 17 00:00:00 2001 From: Mike Date: Wed, 4 Apr 2018 17:27:03 +0200 Subject: [PATCH 2/5] Fix TypeScript type definition typo --- types/index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/index.d.ts b/types/index.d.ts index 6422dd4..2d61e3d 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -78,7 +78,7 @@ export class app { playbackControllerEventHandlers: {[name: string]: PlaybackController}; playbackController: (eventName: string, func: RequestHandler) => void; - requestHandlers: {[name: string]: handler}; + requestHandlers: {[name: string]: RequestHandler}; on: (handlerName: string, handler: RequestHandler) => void; /** TODO: Figure out what the promise actually contains */ From e850e4c69937e2142d1cc0c9062e4fefcba7d3c5 Mon Sep 17 00:00:00 2001 From: Mike Date: Wed, 4 Apr 2018 17:39:36 +0200 Subject: [PATCH 3/5] Fix index.d.ts trailing whitespace --- types/index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/index.d.ts b/types/index.d.ts index 2d61e3d..14abb85 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -80,7 +80,7 @@ export class app { requestHandlers: {[name: string]: RequestHandler}; on: (handlerName: string, handler: RequestHandler) => void; - + /** TODO: Figure out what the promise actually contains */ request: (requestJSON: alexa.Request) => Promise; From eb72047f0ec75d808f397e4fef7e2bc7d212e186 Mon Sep 17 00:00:00 2001 From: Matt Kruse Date: Wed, 4 Apr 2018 11:07:38 -0500 Subject: [PATCH 4/5] Tests for PR #313 --- test/fixtures/intent_request_on.json | 42 ++++++++++++++ test/test_alexa_app_on_request.js | 84 ++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 test/fixtures/intent_request_on.json create mode 100644 test/test_alexa_app_on_request.js diff --git a/test/fixtures/intent_request_on.json b/test/fixtures/intent_request_on.json new file mode 100644 index 0000000..c49e14f --- /dev/null +++ b/test/fixtures/intent_request_on.json @@ -0,0 +1,42 @@ +{ + "version": "1.0", + "session": { + "new": true, + "sessionId": "amzn1.echo-api.session.abeee1a7-aee0-41e6-8192-e6faaed9f5ef", + "application": { + "applicationId": "amzn1.echo-sdk-ams.app.000000-d0ed-0000-ad00-000000d00ebe" + }, + "attributes": {}, + "user": { + "userId": "amzn1.account.AM3B227HF3FAM1B261HK7FFM3A2" + } + }, + "request": { + "type": "GameEngine.InputHandlerEvent", + "requestId": "amzn1.echo-api.request.9cdaa4db-f20e-4c58-8d01-c75322d6c423", + "timestamp": "2015-05-13T12:34:56Z", + "events": [ + { + "name": "myEventName", + "inputEvents": [ + { + "gadgetId": "someGadgetId1", + "timestamp": "2015-05-13T12:34:56Z", + "action": "down", + "color": "FF0000" + } + ] + } + ] + }, + "context": { + "System": { + "user": { + "userId": "amzn1.account.AM3B227HF3FAM1B261HK7FFM3A2" + }, + "application": { + "applicationId": "amzn1.echo-sdk-ams.app.000000-d0ed-0000-ad00-000000d00ebe" + } + } + } +} diff --git a/test/test_alexa_app_on_request.js b/test/test_alexa_app_on_request.js new file mode 100644 index 0000000..cccd28a --- /dev/null +++ b/test/test_alexa_app_on_request.js @@ -0,0 +1,84 @@ +/*jshint expr: true*/ +"use strict"; +var chai = require("chai"); +var chaiAsPromised = require("chai-as-promised"); +var mockHelper = require("./helpers/mock_helper"); +chai.use(chaiAsPromised); +var expect = chai.expect; +chai.config.includeStack = true; + +import * as Alexa from '..'; + +describe("Alexa", function() { + describe("app", function() { + var testApp = new Alexa.app("testApp"); + + beforeEach(function() { + testApp = new Alexa.app("testApp"); + }); + + describe("#request", function() { + describe("response", function() { + var mockRequest = mockHelper.load("intent_request_on.json"); + + context("with a request of type GameEngine.InputHandlerEvent", function() { + context("with no type handler", function() { + describe("outputSpeech", function() { + it("responds with INVALID_REQUEST_TYPE message", function() { + var subject = testApp.request(mockRequest).then(function(response) { + return response.response.outputSpeech; + }); + + return expect(subject).to.eventually.become({ + ssml: "" + testApp.messages.INVALID_REQUEST_TYPE + "", + type: "SSML" + }); + }); + }); + }); + + context("with a matching type handler", function() { + var expectedMessage="Valid Response"; + testApp.on('GameEngine.InputHandlerEvent',function(req,res) { + res.say(expectedMessage); + }); + describe("outputSpeech", function() { + it("responds with expected message", function() { + testApp.on('GameEngine.InputHandlerEvent',function(req, res) { + res.say(expectedMessage); + }); + + var subject = testApp.request(mockRequest).then(function(response) { + return response.response.outputSpeech; + }); + + return expect(subject).to.eventually.become({ + ssml: "" + expectedMessage + "", + type: "SSML" + }); + }); + + it("responds with expected message for promise", function() { + testApp.on('GameEngine.InputHandlerEvent',function(req, res) { + return Promise.resolve().then(function() { + res.say(expectedMessage); + }); + }); + + var subject = testApp.request(mockRequest).then(function(response) { + return response.response.outputSpeech; + }); + + return expect(subject).to.eventually.become({ + ssml: "" + expectedMessage + "", + type: "SSML" + }); + }); + + }); + }); + }); + }); + }); + }); +}); From f52184d8880a6f8d4a60ebc14e070f6cf24c80e1 Mon Sep 17 00:00:00 2001 From: Matt Kruse Date: Thu, 5 Apr 2018 23:04:44 -0500 Subject: [PATCH 5/5] Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ffe803c..a5358fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ * [#314](https://github.com/alexa-js/alexa-app/pull/314): Fix handling of `undefined` slot values - [@User1m](https://github.com/user1m). * [#322](https://github.com/alexa-js/alexa-app/pull/322): Included Skill Builder example in example.ejs - [@funkydan2](https://github.com/funkydan2). * [#329](https://github.com/alexa-js/alexa-app/pull/329): Run dtslint only on the latest node version - [@kobim](https://github.com/kobim). +* [#313](https://github.com/alexa-js/alexa-app/pull/313): Add general support for upcoming request types - [@matt-kruse](https://github.com/matt-kruse). * Your contribution here. ### 4.2.1 (February 1, 2018)