Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a642519
Merge pull request #1 from horike37/master
theburningmonk Aug 12, 2019
a415e9a
feat: added schema validation for S3
theburningmonk Aug 14, 2019
c7dc7b5
feat: added schema validation for S3
theburningmonk Aug 14, 2019
5adbf7c
feat: added compiling iam role for S3
theburningmonk Aug 14, 2019
9846c63
refactor: removed unnecessary async-await
theburningmonk Aug 14, 2019
183ae59
feat: add compile methods to S3
theburningmonk Aug 17, 2019
923b40d
feat: add 500 response mapping
theburningmonk Aug 17, 2019
ff3f89d
feat: add s3 proxy to index
theburningmonk Aug 17, 2019
0122a96
chore: add .vscode to gitignore
theburningmonk Aug 17, 2019
3424143
Merge branch 'master' into feature/support_s3
theburningmonk Aug 18, 2019
3d2436d
Merge pull request #2 from horike37/master
theburningmonk Aug 17, 2019
981f004
Merge branch 'master' into feature/support_s3
theburningmonk Aug 17, 2019
ca8dcf0
feat: kinesis and sqs covers 500 response
theburningmonk Aug 17, 2019
a9ff5db
fix: s3 perm requires ARN not bucket name
theburningmonk Aug 17, 2019
c9dbb59
feat: include s3 in validate.js
theburningmonk Aug 17, 2019
9ed8ef3
fix: remove cors from schema as it's already covered
theburningmonk Aug 17, 2019
4598829
fix: give Get/Put/DeleteObject* perms
theburningmonk Aug 17, 2019
17ac6a2
fix: fixed bugs identified by int test
theburningmonk Aug 17, 2019
f5c9ce3
test: added single-integration s3 test
theburningmonk Aug 18, 2019
05760c5
fix: map 204 to 200
theburningmonk Aug 18, 2019
894682f
test: added multiple proxies s3 int test
theburningmonk Aug 18, 2019
4fa75ef
fix: fixed package-lock.json
theburningmonk Aug 18, 2019
bed787c
docs: updated README
theburningmonk Aug 19, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,4 @@ tmp
tmpdirs-serverless
.eslintcache
.serverless
.vscode
57 changes: 53 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,15 @@ Please pull request if you are intersted in it.

- Kinesis Streams
- SQS
- S3

## How to use

Define settings of the AWS services you want to integrate under `custom > apiGatewayServiceProxies` and run `serverless deploy`.

### Kinesis

Sample syntax for Kinesis proxy in serverless.yml.
Sample syntax for Kinesis proxy in `serverless.yml`.

```yaml
custom:
Expand All @@ -49,12 +50,12 @@ resources:
Sample request after deploying.

```bash
curl -XPOST https://xxxxxxx.execute-api.us-east-1.amazonaws.com/dev/kinesis -d '{"Data": "some data","PartitionKey": "some key"}' -H 'Content-Type:application/json'
curl -X POST https://xxxxxxx.execute-api.us-east-1.amazonaws.com/dev/kinesis -d '{"Data": "some data","PartitionKey": "some key"}' -H 'Content-Type:application/json'
```

### SQS

Sample syntax for SQS proxy in serverless.yml.
Sample syntax for SQS proxy in `serverless.yml`.

```yaml
custom:
Expand All @@ -74,7 +75,55 @@ resources:
Sample request after deploying.

```bash
curl -XPOST https://xxxxxx.execute-api.us-east-1.amazonaws.com/dev/sqs -d '{"message": "testtest"}' -H 'Content-Type:application/json'
curl -X POST https://xxxxxx.execute-api.us-east-1.amazonaws.com/dev/sqs -d '{"message": "testtest"}' -H 'Content-Type:application/json'
```

### S3

Sample syntax for S3 proxy in `serverless.yml`.

```yaml
custom:
apiGatewayServiceProxies:
- s3:
path: /s3
method: post
action: PutObject
bucket:
Ref: S3Bucket
key: static-key.json # use static key
cors: true

- s3:
path: /s3/{myKey} # use path param
method: get
action: GetObject
bucket:
Ref: S3Bucket
key:
pathParam: myKey
cors: true

- s3:
path: /s3
method: delete
action: DeleteObject
bucket:
Ref: S3Bucket
key:
queryStringParam: key # use query string param
cors: true

resources:
Resources:
S3Bucket:
Type: 'AWS::S3::Bucket'
```

Sample request after deploying.

```bash
curl -X POST https://xxxxxx.execute-api.us-east-1.amazonaws.com/dev/s3 -d '{"message": "testtest"}' -H 'Content-Type:application/json'
```

## Common API Gateway features
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
service: multiple-s3-proxy

provider:
name: aws
runtime: nodejs10.x

plugins:
localPath: './../../../../../../'
modules:
- serverless-apigateway-service-proxy

custom:
apiGatewayServiceProxies:
- s3:
path: /s3
method: post
action: PutObject
bucket:
Ref: S3Bucket
key: my-test-object.json # static key
cors: true

- s3:
path: /s3/{key} # path param
method: get
action: GetObject
bucket:
Ref: S3Bucket
key:
pathParam: key
cors: true

- s3:
path: /s3
method: delete
action: DeleteObject
bucket:
Ref: S3Bucket
key:
queryStringParam: key # query string param
cors: true

resources:
Resources:
S3Bucket:
Type: 'AWS::S3::Bucket'

Outputs:
S3BucketName:
Value:
Ref: S3Bucket
69 changes: 69 additions & 0 deletions __tests__/integration/s3/multiple-integrations/tests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
'use strict'

const expect = require('chai').expect
const fetch = require('node-fetch')
const {
deployWithRandomStage,
removeService,
getS3Object,
deleteS3Object
} = require('../../../utils')

describe('Multiple S3 Proxies Integration Test', () => {
let endpoint
let stage
let bucket
const config = '__tests__/integration/s3/multiple-integrations/service/serverless.yml'
const key = 'my-test-object.json'

beforeAll(async () => {
const result = await deployWithRandomStage(config)

stage = result.stage
endpoint = result.endpoint
bucket = result.outputs.S3BucketName
})

afterAll(async () => {
await deleteS3Object(bucket, key)
removeService(stage, config)
})

it('should get correct response from s3 put endpoint', async () => {
const putEndpoint = `${endpoint}/s3`

const putResponse = await fetch(putEndpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message: 'test' })
})
expect(putResponse.headers.get('access-control-allow-origin')).to.deep.equal('*')
expect(putResponse.status).to.be.equal(200)

const uploadedObject = await getS3Object(bucket, key)
expect(uploadedObject.toString()).to.equal(JSON.stringify({ message: 'test' }))
})

it('should get correct response from s3 get endpoint', async () => {
const getEndpoint = `${endpoint}/s3/${key}`

const getResponse = await fetch(getEndpoint, {
method: 'GET',
headers: { Accept: 'application/json' }
})
expect(getResponse.headers.get('access-control-allow-origin')).to.deep.equal('*')
expect(getResponse.status).to.be.equal(200)

const body = await getResponse.json()
expect(body).to.deep.equal({ message: 'test' })
})

it('should get correct response from s3 delete endpoint', async () => {
const deleteEndpoint = `${endpoint}/s3?key=${key}`

const deleteResponse = await fetch(deleteEndpoint, {
method: 'DELETE'
})
expect(deleteResponse.status).to.be.equal(200)
})
})
32 changes: 32 additions & 0 deletions __tests__/integration/s3/single-integration/service/serverless.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
service: s3-proxy

provider:
name: aws
runtime: nodejs10.x

plugins:
localPath: './../../../../../../'
modules:
- serverless-apigateway-service-proxy

custom:
apiGatewayServiceProxies:
- s3:
path: /s3/{key}
method: post
action: PutObject
bucket:
Ref: S3Bucket
key:
pathParam: key
cors: true

resources:
Resources:
S3Bucket:
Type: 'AWS::S3::Bucket'

Outputs:
S3BucketName:
Value:
Ref: S3Bucket
46 changes: 46 additions & 0 deletions __tests__/integration/s3/single-integration/tests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
'use strict'

const expect = require('chai').expect
const fetch = require('node-fetch')
const {
deployWithRandomStage,
removeService,
getS3Object,
deleteS3Object
} = require('../../../utils')

describe('Single S3 Proxy Integration Test', () => {
let endpoint
let stage
let bucket
const config = '__tests__/integration/s3/single-integration/service/serverless.yml'
const key = 'my-test-object.json'

beforeAll(async () => {
const result = await deployWithRandomStage(config)

stage = result.stage
endpoint = result.endpoint
bucket = result.outputs.S3BucketName
})

afterAll(async () => {
await deleteS3Object(bucket, key)
removeService(stage, config)
})

it('should get correct response from s3 proxy endpoint', async () => {
const testEndpoint = `${endpoint}/s3/${key}`

const response = await fetch(testEndpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message: 'test' })
})
expect(response.headers.get('access-control-allow-origin')).to.deep.equal('*')
expect(response.status).to.be.equal(200)

const uploadedObject = await getS3Object(bucket, key)
expect(uploadedObject.toString()).to.equal(JSON.stringify({ message: 'test' }))
})
})
45 changes: 37 additions & 8 deletions __tests__/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,41 @@ const fs = require('fs')
const path = require('path')
const execSync = require('child_process').execSync
const aws = require('aws-sdk')
const s3 = new aws.S3()
const cloudformation = new aws.CloudFormation({ region: 'us-east-1' })

async function getApiGatewayEndpoint(stackName) {
function getApiGatewayEndpoint(outputs) {
return outputs.ServiceEndpoint.match(/https:\/\/.+\.execute-api\..+\.amazonaws\.com.+/)[0]
}

async function getStackOutputs(stackName) {
const result = await cloudformation.describeStacks({ StackName: stackName }).promise()
const stack = result.Stacks[0]

const keys = stack.Outputs.map((x) => x.OutputKey)
const values = stack.Outputs.map((x) => x.OutputValue)

return _.zipObject(keys, values)
}

async function getS3Object(bucket, key) {
const resp = await s3
.getObject({
Bucket: bucket,
Key: key
})
.promise()

return resp.Body
}

const endpointOutput = _.find(result.Stacks[0].Outputs, { OutputKey: 'ServiceEndpoint' })
.OutputValue
return endpointOutput.match(/https:\/\/.+\.execute-api\..+\.amazonaws\.com.+/)[0]
async function deleteS3Object(bucket, key) {
await s3
.deleteObject({
Bucket: bucket,
Key: key
})
.promise()
}

function deployService(stage, config) {
Expand All @@ -37,14 +64,16 @@ async function deployWithRandomStage(config) {
.substring(2)
const stackName = `${serviceName}-${stage}`
deployService(stage, config)
const endpoint = await getApiGatewayEndpoint(stackName)
const outputs = await getStackOutputs(stackName)
const endpoint = getApiGatewayEndpoint(outputs)

return { stage, endpoint }
return { stackName, stage, outputs, endpoint }
}

module.exports = {
getApiGatewayEndpoint,
deployService,
removeService,
deployWithRandomStage
deployWithRandomStage,
getS3Object,
deleteS3Object
}
9 changes: 7 additions & 2 deletions lib/apiGateway/methods.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict'

module.exports = {
async getMethodResponses(http) {
getMethodResponses(http) {
const methodResponse = {
Properties: {
MethodResponses: [
Expand All @@ -14,6 +14,11 @@ module.exports = {
ResponseParameters: {},
ResponseModels: {},
StatusCode: 400
},
{
ResponseParameters: {},
ResponseModels: {},
StatusCode: 500
}
]
}
Expand All @@ -25,7 +30,7 @@ module.exports = {
origin = http.cors.origins.join(',')
}

methodResponse.Properties.MethodResponses.forEach(async (val, i) => {
methodResponse.Properties.MethodResponses.forEach((val, i) => {
methodResponse.Properties.MethodResponses[i].ResponseParameters = {
'method.response.header.Access-Control-Allow-Origin': `'${origin}'`
}
Expand Down
Loading