Skip to content

Commit

Permalink
Ugins globals config and removing custom CORS implementation (#5)
Browse files Browse the repository at this point in the history
  • Loading branch information
lmammino authored Apr 8, 2018
1 parent 622cdc3 commit a01e056
Show file tree
Hide file tree
Showing 11 changed files with 74 additions and 138 deletions.
12 changes: 7 additions & 5 deletions lessons/04-serverless-application-model/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,17 @@ AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: A Mock API to return all gigs or a single gig

Globals:
Function:
Runtime: nodejs8.10

Resources:

listGigs:
Type: AWS::Serverless::Function
Properties:
CodeUri: ./src
Handler: index.listGigs
Runtime: nodejs8.10
Events:
Endpoint:
Type: Api
Expand All @@ -91,7 +94,6 @@ Resources:
Properties:
CodeUri: ./src
Handler: index.gig
Runtime: nodejs8.10
Events:
Endpoint:
Type: Api
Expand All @@ -113,14 +115,14 @@ Let's analyze the content of this file:

- `Description` allows you to specify an arbitratry description for the Cloudformation stack that will be deployed with this template.

- `Globals` allows you to specify attributes that are shared across all your stack. In this case the runtime environment that you want to use in all the lambda functions (Node.js version 8.10). This config acts as a default set of options, in fact, resources might override some of these values if needed (e.g. specifying a different runtime for a given lambda). Find out more about *Globals* in the [official SAM docs](https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#globals-section).

- `Resources` is the most important part of the template and allows us to specify all the different resources that we want to use in our application (in this case 2 lambda functions).

- A Lamdba function in SAM is identified by the `Type` `AWS::Serverless::Function` and a set of `Properties`.
- A Lambda function in SAM is identified by the `Type` `AWS::Serverless::Function` and a set of `Properties`.

- The property `CodeUri` is used to specify where the code for the lambda is stored, while `Handler` is used to indicate which file and function needs to be loaded by to run the Lambda. This parameter uses the format `fileName.functionName`. For example when we specify `index.listGigs`, the Lambda runtime will load the file `index.js` in our code path and from this file import the function `listGigs`.

- `Runtime` indicates which runtime we want to use to run the code (in our case Node.js version 8.10)

- `Events` is a dictionary that describes all the events that will trigger the execution of the Lambda function. Every event is identified by an arbitrary name (in our case we choose `Endpoint`). An event object needs to have a `Type` (in the case of API Gateway it's simply `Api`) and a set of `Properties`. Properties will change based on the type of event, for Api events we specified a `Path` and a `Method`.

- The block `Output` at the end of the file, it's not strictly mandatory but it will help us to retrieve the URL of our API. API Gateway will create a random endpoint URL and by exporting it, we will be able to easily reference to it.
Expand Down
70 changes: 32 additions & 38 deletions lessons/06-purchase-ticket-api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,6 @@ Resources:
Properties:
CodeUri: ./src
Handler: index.purchaseTicket
Runtime: nodejs8.10
Role: !GetAtt GigsApiRole.Arn
Events:
Endpoint:
Expand Down Expand Up @@ -212,62 +211,57 @@ If you did everything correctly in the previous steps, you should see... this er

This errors happens as part of the CORS protocol.

> 💡 **TIP**: CORS stands for [Cross-Origin Resource Sharing](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)
When some JavaScript code in the browser invokes an XHR request to another domain, the browser might issue a [Pre-Flight request](https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request) to check if the destination API can receive call from the current domain (the one where the website originating the request is running).

During Pre-flight the browser sends an OPTIONS request before your actual request gets sent. The browser expects to receive as response to this request a set of headers that specify which domains can invoke the API and which methods (POST, PUT, etc.) can be used (these are often called *"Access Control rules"*). These headers are validated against the current request and if it matches the given rules, then the original request is actually performed.
During Pre-flight the browser sends an OPTIONS request before your actual request gets sent. The browser expects to receive as response to this request a set of headers that specify which domains can invoke the API, which headers ('Content-Type', etc.) can be sent and which HTTP methods (POST, PUT, etc.) can be used. These are often called *"Access Control rules"*. These headers are validated against the current request and if it matches the given rules, then the original request is actually performed.

> 💡 **TIP**: If you need more detail on how CORS actually works, check out this brilliant article: [Understanding CORS and Pre-Flight](http://www.authenticdesign.co.uk/understanding-cors-and-pre-flight/)

In order to make our API work, we need to add CORS support to our application.

There are different ways to do this, and there's also a ["native" way](https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#cors-configuration), but it works only if you define a complete swagger file for your APIs, so we are going to use another method.
The easiest way to do this is to expand our `Global` configuration in the `template.yaml` and add a piece of specific CORS configuration:

Our method is a bit more crafty, but allows us to have more control (and to understand better how CORS actually works). We will need to create a Lambda that can respond to the Pre-Flight Request and map it to any `OPTIONS` request in API Gateway.
```yaml
# template.yaml
# ...

This is the Lambda code you need to add in the `index.js` file:
Globals:
Function:
Runtime: nodejs8.10
Api:
Cors:
AllowMethods: "'*'"
AllowHeaders: "'*'"
AllowOrigin: "'*'"

```javascript
// ...

exports.cors = (event, context, callback) => {
callback(null, {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': '*',
'Access-Control-Allow-Headers': 'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token'
},
body: ''
})
}
# ...
```

And this is the code to add to the template file to expose this Lambda to any `OPTIONS` request.
With this configuration we are enabling Cross Origin Resource Sharing for every method, header and origin.

It's a good practice to be more restrictive and allow only what's strictly necessary to make your app work, so realistically your configuration should look like the following:

```yaml
# template.yaml
# ...

Resources:

# ...
Globals:
Function:
Runtime: nodejs8.10
Api:
Cors:
AllowMethods: "'GET,POST,OPTIONS'"
AllowHeaders: "'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'"
AllowOrigin: "'http://<FRONTEND_BUCKET>.s3-website-<AWS_REGION>.amazonaws.com'"

CORS:
Type: AWS::Serverless::Function
Properties:
CodeUri: ./src
Handler: index.cors
Runtime: nodejs8.10
Events:
Endpoint:
Type: Api
Properties:
Path: /{path+}
Method: options
# ...
```

> 💡 **TIP**: the syntax `/{path+}` in the `Path` parameter means that the request can match any path in order to trigger the lambda as long as the method is `OPTION` (since we are specifying the constraint).
Be sure to replace `<FRONTEND_BUCKET>` and `<AWS_REGION>` with your actual values.

> 💡 **TIP**: You might have noticed that we are *double quoting* the attribute values (`"'some value'"`), this is because the HTTP spec requires the value of these header properties to be quoted strings.
Now we are ready to deploy our app again:

Expand Down
1 change: 0 additions & 1 deletion lessons/08-worker-lambda/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,6 @@ Resources:
Properties:
CodeUri: ./src
Handler: index.sendMailWorker
Runtime: nodejs8.10
Role: !GetAtt SendMailWorkerRole.Arn
Environment:
Variables:
Expand Down
6 changes: 4 additions & 2 deletions resources/lambda/gig-api-dynamodb/template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: Api and services for the ticketless application

Globals:
Function:
Runtime: nodejs8.10

Resources:

GigsApiRole:
Expand Down Expand Up @@ -35,7 +39,6 @@ Resources:
Properties:
CodeUri: ./src
Handler: index.listGigs
Runtime: nodejs8.10
Role: !GetAtt GigsApiRole.Arn

Events:
Expand All @@ -50,7 +53,6 @@ Resources:
Properties:
CodeUri: ./src
Handler: index.gig
Runtime: nodejs8.10
Role: !GetAtt GigsApiRole.Arn
Events:
Endpoint:
Expand Down
6 changes: 4 additions & 2 deletions resources/lambda/gig-api-mock/template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@ AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: Api and services for the ticketless application

Globals:
Function:
Runtime: nodejs8.10

Resources:

listGigs:
Type: AWS::Serverless::Function
Properties:
CodeUri: ./src
Handler: index.listGigs
Runtime: nodejs8.10
Events:
Endpoint:
Type: Api
Expand All @@ -22,7 +25,6 @@ Resources:
Properties:
CodeUri: ./src
Handler: index.gig
Runtime: nodejs8.10
Events:
Endpoint:
Type: Api
Expand Down
13 changes: 0 additions & 13 deletions resources/lambda/purchase-ticket-api/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,16 +195,3 @@ exports.purchaseTicket = (event, context, callback) => {
body: JSON.stringify({success: true})
})
}

exports.cors = (event, context, callback) => {
callback(null, {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': '*',
'Access-Control-Allow-Headers': 'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token'
},
body: ''
})
}
26 changes: 9 additions & 17 deletions resources/lambda/purchase-ticket-api/template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@ AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: Api and services for the ticketless application

Globals:
Function:
Runtime: nodejs8.10
Api:
Cors:
AllowMethods: "'GET,POST,OPTIONS'"
AllowHeaders: "'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'"
AllowOrigin: "'*'"

Resources:

GigsApiRole:
Expand Down Expand Up @@ -35,9 +44,7 @@ Resources:
Properties:
CodeUri: ./src
Handler: index.listGigs
Runtime: nodejs8.10
Role: !GetAtt GigsApiRole.Arn

Events:
Endpoint:
Type: Api
Expand All @@ -50,7 +57,6 @@ Resources:
Properties:
CodeUri: ./src
Handler: index.gig
Runtime: nodejs8.10
Role: !GetAtt GigsApiRole.Arn
Events:
Endpoint:
Expand All @@ -64,7 +70,6 @@ Resources:
Properties:
CodeUri: ./src
Handler: index.purchaseTicket
Runtime: nodejs8.10
Role: !GetAtt GigsApiRole.Arn
Events:
Endpoint:
Expand All @@ -73,19 +78,6 @@ Resources:
Path: /purchase
Method: post

CORS:
Type: AWS::Serverless::Function
Properties:
CodeUri: ./src
Handler: index.cors
Runtime: nodejs8.10
Events:
Endpoint:
Type: Api
Properties:
Path: /{path+}
Method: options

Outputs:
endpoint:
Description: The API Gateway endpoint for ticketless
Expand Down
13 changes: 0 additions & 13 deletions resources/lambda/sns-sqs/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -259,16 +259,3 @@ exports.purchaseTicket = (event, context, callback) => {
})
})
}

exports.cors = (event, context, callback) => {
callback(null, {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': '*',
'Access-Control-Allow-Headers': 'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token'
},
body: ''
})
}
26 changes: 9 additions & 17 deletions resources/lambda/sns-sqs/template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@ AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: Api and services for the ticketless application

Globals:
Function:
Runtime: nodejs8.10
Api:
Cors:
AllowMethods: "'GET,POST,OPTIONS'"
AllowHeaders: "'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'"
AllowOrigin: "'*'"

Resources:

GigsApiRole:
Expand Down Expand Up @@ -71,9 +80,7 @@ Resources:
Properties:
CodeUri: ./src
Handler: index.listGigs
Runtime: nodejs8.10
Role: !GetAtt GigsApiRole.Arn

Events:
Endpoint:
Type: Api
Expand All @@ -86,7 +93,6 @@ Resources:
Properties:
CodeUri: ./src
Handler: index.gig
Runtime: nodejs8.10
Role: !GetAtt GigsApiRole.Arn
Events:
Endpoint:
Expand All @@ -100,7 +106,6 @@ Resources:
Properties:
CodeUri: ./src
Handler: index.purchaseTicket
Runtime: nodejs8.10
Role: !GetAtt GigsApiRole.Arn
Environment:
Variables:
Expand All @@ -112,19 +117,6 @@ Resources:
Path: /purchase
Method: post

CORS:
Type: AWS::Serverless::Function
Properties:
CodeUri: ./src
Handler: index.cors
Runtime: nodejs8.10
Events:
Endpoint:
Type: Api
Properties:
Path: /{path+}
Method: options

Outputs:
endpoint:
Description: The API Gateway endpoint for ticketless
Expand Down
13 changes: 0 additions & 13 deletions resources/lambda/worker-lambda/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -263,19 +263,6 @@ exports.purchaseTicket = (event, context, callback) => {
})
}

exports.cors = (event, context, callback) => {
callback(null, {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': '*',
'Access-Control-Allow-Headers': 'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token'
},
body: ''
})
}

exports.sendMailWorker = (event, context, callback) => {
const receiveMessageParams = {
QueueUrl: process.env.SQS_QUEUE_URL,
Expand Down
Loading

0 comments on commit a01e056

Please sign in to comment.