Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Always enfore strict header checking [broken] #174

Closed
wants to merge 11 commits into from
9 changes: 7 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
## Changelog

### 3.0.1 (Next)

### 4.0.0 (Next)
* Your contribution here.

### 3.1.0 (February 13, 2017)

* [#174](https://github.com/alexa-js/alexa-app/pull/174): Always enfore strict header checking - [@tejashah88](https://github.com/tejashah88).
* [#162](https://github.com/alexa-js/alexa-app/issues/162): Fix: do not generate empty slots in schema - [@dblock](https://github.com/dblock).
* [#134](https://github.com/alexa-js/alexa-app/pull/134): Adding deprecation notices for plan to use Promises for async functionality - [ajcrites](https://github.com/ajcrites).

### 3.0.0 (February 6, 2017)

* [#152](https://github.com/alexa-js/alexa-app/issues/152): Mounted a JSON body-parser after verifier middleware and removed `bodyParser.urlencoded` in Express integration - [@dblock](https://github.com/dblock).
Expand Down
55 changes: 34 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ A Node module to simplify the development of Alexa skills (applications.)
[![Build Status](https://travis-ci.org/alexa-js/alexa-app.svg?branch=master)](https://travis-ci.org/alexa-js/alexa-app)
[![Coverage Status](https://coveralls.io/repos/github/alexa-js/alexa-app/badge.svg?branch=master)](https://coveralls.io/github/alexa-js/alexa-app?branch=master)

### Stable Release

You're reading the documentation for the next release of alexa-app. Please see [CHANGELOG](CHANGELOG.md) and make sure to read [UPGRADING](UPGRADING.md) when upgrading from a previous version. The current stable release is [3.1.0](https://github.com/alexa-js/alexa-app/tree/v3.1.0).

### Introduction

This module parses HTTP JSON requests from the Alexa platform and builds the JSON response that consumed by an Alexa-compatible device, such as the Echo.

It provides a DSL for defining intents, convenience methods to more easily build the response, handle session objects, and add cards.
Expand Down Expand Up @@ -35,8 +41,8 @@ You need to make sure that the Handler is set to `index.handler`, which is the d
var app = new alexa.app("sample");

app.intent("number", {
"slots": { "number": "NUMBER" },
"utterances": ["say the number {1-100|number}"]
"slots": { "number": "AMAZON.NUMBER" },
"utterances": ["say the number {-|number}"]
},
function(request, response) {
var number = request.slot("number");
Expand All @@ -63,8 +69,8 @@ var express_app = express();
var app = new alexa.app("sample");

app.intent("number", {
"slots": { "number": "NUMBER" },
"utterances": ["say the number {1-100|number}"]
"slots": { "number": "AMAZON.NUMBER" },
"utterances": ["say the number {-|number}"]
},
function(request, response) {
var number = request.slot("number");
Expand Down Expand Up @@ -217,9 +223,11 @@ app.launch(function(request, response) {

Define the handler for multiple intents using multiple calls to `intent()`.
Intent schema and sample utterances can also be passed to `intent()`, which is detailed below.
Intent handlers that don't return an immediate response (because they do some asynchronous operation) must return `false`.
Intent handlers that don't return an immediate response (because they do some asynchronous operation) must return `false` (**deprecated**) or a `Promise`.
See example further below.

**Note:** Using `return false` to signify an asynchronous intent handler function is deprecated and will be removed in the next major version. Instead, return a `Promise`.

```javascript
app.intent("live", {
"slots": {
Expand Down Expand Up @@ -252,7 +260,7 @@ app.sessionEnded(function(request, response) {

### AudioPlayer Event Request

Define the handler for multiple events using multiple calls to `audioPlayer()`. You can define only one handler per event. Event handlers that don't return an immediate response (because they do some asynchronous operation) must return false.
Define the handler for multiple events using multiple calls to `audioPlayer()`. You can define only one handler per event. Event handlers that don't return an immediate response (because they do some asynchronous operation) must return false (**deprecated**) or a Promise.

You can define handlers for the following events:

Expand Down Expand Up @@ -284,7 +292,8 @@ See an example of asynchronous response below.
```javascript
app.audioPlayer("PlaybackFinished", function(request, response) {
// async response
getNextSongFromDB(function(url, token) {
return getNextSongFromDBAsync()
.then(function(url, token) {
var stream = {
"url": url,
"token": token,
Expand All @@ -294,7 +303,6 @@ app.audioPlayer("PlaybackFinished", function(request, response) {
response.audioPlayerPlayStream("ENQUEUE", stream);
response.send();
});
return false;
});
```

Expand Down Expand Up @@ -350,7 +358,7 @@ app.intent("sampleIntent", {
"AGE": "AMAZON.NUMBER"
},
"utterances": [
"my {name is|name's} {NAME} and {I am|I'm} {1-100|AGE}{ years old|}"
"my {name is|name's} {NAME} and {I am|I'm} {-|AGE}{ years old|}"
]
},
function(request, response) { ... }
Expand Down Expand Up @@ -510,13 +518,28 @@ app.error = function(exception, request, response) {

## Asynchronous Intent Handler Example

If an intent or other request handler will return a response later, it must return ether `false` or a `Promise` (object with a `.then` function). This tells the alexa-app library not to send the response automatically.
If an intent or other request handler will return a response later, it must return either `false` (**deprecated**) or a `Promise` (object with a `.then` function). This tells the alexa-app library not to send the response automatically.

**Note:** Using `return false` to signify an asynchronous intent handler function is deprecated and will be removed in the next major version. Instead, return a `Promise`.

A callback is also passed to the handler. When this callback is called with no first argument, the response will be sent. If something is passed to the first argument, it is treated as an error.

**Note:** Using the callback is also deprecated and will be removed in the next major version. Instead, use promises.

If you return a Promise from the handler, you do not need to call the callback. If the Promise resolves, the response will be sent. If it is rejected, it is treated as an error.

```javascript
app.intent("checkStatus", function(request, response) {
// `getAsync` returns a Promise in this example. When
// returning a Promise, the response is sent after it
// resolves. If rejected, it is treated as an error.
return http.getAsync("http://server.com/status.html").then(function (rc) {
response.say(rc.statusText);
});
});

// **NOTE** this example is deprecated and will not work
// after the next major version
app.intent("checkStatus", function(request, response, callback) {
http.get("http://server.com/status.html", function(rc) {
// this is async and will run after the http call returns
Expand All @@ -532,18 +555,8 @@ app.intent("checkStatus", function(request, response, callback) {
// return false immediately so alexa-app doesn't send the response
return false;
});

app.intent("checkStatus", function(request, response) {
// `getAsync` returns a Promise in this example. When
// returning a Promise, the response is sent after it
// resolves. If rejected, it is treated as an error.
return http.getAsync("http://server.com/status.html").then(function (rc) {
response.say(rc.statusText);
});
});
```


### Customizing Default Error Messages

```javascript
Expand Down Expand Up @@ -598,6 +611,6 @@ All named apps can be found in the `alexa.apps` object, keyed by name. The value

## License

Copyright (c) 2016 Matt Kruse
Copyright (c) 2016-2017 Matt Kruse

MIT License, see [LICENSE](LICENSE.md) for details.
39 changes: 39 additions & 0 deletions UPGRADING.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,44 @@
# Upgrading Alexa-app

### Upgrading to >= 4.0.0

#### Changes to Asynchronous Support

Support for asynchronous `pre` and `post` as well as all handlers such as the intent and launch handlers is now done through promises. This allows `pre` and `post` to be asynchronous.

You can no longer make these handlers asynchronous by using `return false`. A callback is no longer taken as an argument to these handlers. Instead if you want your handler to be asynchronous you may return a promise.

`response.send` and `response.fail` now return promises. If you call either, you must return them in order to continue the promise chain for any asynchronous functionality. If you return a promise but do not explicitly call `response.send` it will be called automatically when the returned promise resolves. If you want to trigger a failure, `return response.fail(error)` and `throw error` have the same effect.

Before:
```javascript
app.intent("tellme", (request, response) => {
http.get(url, rc => {
if (rc.statusText >= 400) {
response.fail();
} else {
response.send(rc.body);
}
});

return false;
});
```

After:
```javascript
app.intent("tellme", (request, response) => {
// `getAsync` returns a Promise in this example
return http.getAsync(url).then(rc => {
if (rc.statusText >= 400) {
return response.fail();
} else {
return response.send(rc.body);
}
});
});
```

### Upgrading to >= 3.0.0

#### Changes in Express integration interface
Expand Down
30 changes: 18 additions & 12 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ var AlexaUtterances = require("alexa-utterances");
var SSML = require("./to-ssml");
var alexa = {};
var defaults = require("lodash.defaults");
var verifierMiddleware = require("alexa-verifier-middleware");
var verifier = require("alexa-verifier-middleware");
var bodyParser = require('body-parser');

alexa.response = function(session) {
Expand Down Expand Up @@ -428,6 +428,8 @@ alexa.app = function(name) {
Promise.resolve(intentResult).asCallback(callbackHandler);
} else if (false !== intentResult) {
callbackHandler();
} else {
console.trace("NOTE: using `return false` for async intent requests is deprecated and will not work after the next major version");
}
} else {
throw "NO_INTENT_FOUND";
Expand All @@ -439,6 +441,8 @@ alexa.app = function(name) {
Promise.resolve(launchResult).asCallback(callbackHandler);
} else if (false !== launchResult) {
callbackHandler();
} else {
console.trace("NOTE: using `return false` for async launch requests is deprecated and will not work after the next major version");
}
} else {
throw "NO_LAUNCH_FUNCTION";
Expand All @@ -450,6 +454,8 @@ alexa.app = function(name) {
Promise.resolve(sessionEndedResult).asCallback(callbackHandler);
} else if (false !== sessionEndedResult) {
callbackHandler();
} else {
console.trace("NOTE: using `return false` for async session ended requests is deprecated and will not work after the next major version");
}
} else {
response.send();
Expand All @@ -463,6 +469,8 @@ alexa.app = function(name) {
Promise.resolve(eventHandlerResult).asCallback(callbackHandler);
} else if (false !== eventHandlerResult) {
callbackHandler();
} else {
console.trace("NOTE: using `return false` for async audio player requests is deprecated and will not work after the next major version");
}
} else {
response.send();
Expand All @@ -486,17 +494,15 @@ alexa.app = function(name) {
for (intentName in self.intents) {
intent = self.intents[intentName];
var intentSchema = {
"intent": intent.name,
"slots": []
"intent": intent.name
};
if (intent.schema) {
if (intent.schema.slots) {
for (key in intent.schema.slots) {
intentSchema.slots.push({
"name": key,
"type": intent.schema.slots[key]
});
}
if (intent.schema && intent.schema.slots && Object.keys(intent.schema.slots).length > 0) {
intentSchema["slots"] = [];
for (key in intent.schema.slots) {
intentSchema.slots.push({
"name": key,
"type": intent.schema.slots[key]
});
}
}
schema.intents.push(intentSchema);
Expand Down Expand Up @@ -588,7 +594,7 @@ alexa.app = function(name) {
}

if (options.checkCert) {
options.router.use(verifierMiddleware({ strictHeaderCheck: true }));
options.router.use(verifier);
} else {
options.router.use(bodyParser.json());
}
Expand Down
28 changes: 14 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "alexa-app",
"version": "3.0.1",
"version": "3.1.0",
"description": "A module to simplify creation of Alexa (Amazon Echo) apps (Skills) using Node.js",
"main": "index.js",
"author": "Matt Kruse <[email protected]> (http://mattkruse.com)",
Expand Down Expand Up @@ -29,27 +29,27 @@
},
"license": "MIT",
"dependencies": {
"alexa-utterances": "^0.2.0",
"alexa-verifier-middleware": "^0.1.9",
"bluebird": "^2.10.2",
"alexa-utterances": "^0.2.1",
"alexa-verifier-middleware": "^0.2.1",
"bluebird": "^3.4.7",
"lodash.defaults": "^4.2.0",
"numbered": "^1.0.0",
"body-parser": "^1.15.2"
"body-parser": "^1.16.1"
},
"devDependencies": {
"chai": "^3.4.1",
"chai-as-promised": "^5.3.0",
"chai": "^3.5.0",
"chai-as-promised": "^6.0.0",
"chai-string": "^1.3.0",
"coveralls": "^2.11.9",
"danger": "0.6.10",
"coveralls": "^2.11.16",
"danger": "0.11.4",
"ejs": "^2.5.5",
"eslint": "^2.9.0",
"eslint": "^3.15.0",
"esprima": "^3.1.3",
"express": "^4.14.0",
"istanbul": "^0.4.3",
"mocha": "^2.3.4",
"express": "^4.14.1",
"istanbul": "^0.4.5",
"mocha": "^3.2.0",
"sinon": "^1.17.7",
"sinon-chai": "^2.8.0",
"supertest": "^2.0.1"
"supertest": "^3.0.0"
}
}
27 changes: 0 additions & 27 deletions test/fixtures/expected_intent_schema.json

This file was deleted.

Loading