Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -13,6 +13,7 @@ lib-cov

# Coverage directory used by tools like istanbul
coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
Expand Down
22 changes: 22 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
language: node_js

matrix:
include:
- node_js: '8.9'
- node_js: '10.6'
- node_js: '10.6'
env:
- DISABLE_TESTS=true
- LINTING=true

sudo: false

install:
- travis_retry npm install

script:
- if [[ -z "$DISABLE_TESTS" ]]; then npm run test; fi
- if [[ ! -z "$DISABLE_TESTS" && ! -z "$LINTING" ]]; then npm run lint; fi

after_success:
- cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
![serverless](http://public.serverless.com/badges/v3.svg)
[![Build Status](https://travis-ci.org/horike37/serverless-apigateway-service-proxy.svg?branch=master)](https://travis-ci.org/horike37/serverless-apigateway-service-proxy) [![npm version](https://badge.fury.io/js/serverless-apigateway-service-proxy.svg)](https://badge.fury.io/js/serverless-apigateway-service-proxy) [![Coverage Status](https://coveralls.io/repos/github/horike37/serverless-apigateway-service-proxy/badge.svg?branch=master)](https://coveralls.io/github/horike37/serverless-apigateway-service-proxy?branch=master) [![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](LICENSE)

# Serverless APIGateway Service Proxy(BETA)
This Serverless Framewrok plugin supports the AWS service proxy integration feature of API Gateway. You can directly connect API Gateway to AWS services without Lambda.
Expand Down
64 changes: 64 additions & 0 deletions lib/apiGateway/methods.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
'use strict'

const chai = require('chai')
const Serverless = require('serverless/lib/Serverless')
const AwsProvider = require('serverless/lib/plugins/aws/provider/awsProvider')
const ServerlessApigatewayServiceProxy = require('./../index')

chai.use(require('chai-as-promised'))
const expect = require('chai').expect

describe('#getAllServiceProxies()', () => {
let serverless
let serverlessApigatewayServiceProxy

beforeEach(() => {
serverless = new Serverless()
serverless.servicePath = true
serverless.service.service = 'apigw-service-proxy'
const options = {
stage: 'dev',
region: 'us-east-1'
}
serverless.setProvider('aws', new AwsProvider(serverless))
serverlessApigatewayServiceProxy = new ServerlessApigatewayServiceProxy(serverless, options)
})

describe('#getMethodResponses()', () => {
it('should return a corresponding methodResponses resource', async () => {
await expect(
serverlessApigatewayServiceProxy.getMethodResponses()
).to.eventually.have.nested.property('Properties.MethodResponses')
})

it('should set Access-Control-Allow-Origin header when cors is true', async () => {
await expect(
serverlessApigatewayServiceProxy.getMethodResponses({
cors: {
origin: '*'
}
})
).to.be.fulfilled.then((json) => {
expect(
json.Properties.MethodResponses[0].ResponseParameters[
'method.response.header.Access-Control-Allow-Origin'
]
).to.equal("'*'")
})

await expect(
serverlessApigatewayServiceProxy.getMethodResponses({
cors: {
origins: ['*', 'http://example.com']
}
})
).to.be.fulfilled.then((json) => {
expect(
json.Properties.MethodResponses[0].ResponseParameters[
'method.response.header.Access-Control-Allow-Origin'
]
).to.equal("'*,http://example.com'")
})
})
})
})
79 changes: 49 additions & 30 deletions lib/apiGateway/validate.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,36 @@ module.exports = {
const corsPreflight = {}
await BbPromise.all(
this.getAllServiceProxies().map(async (serviceProxy) => {
Object.keys(serviceProxy).forEach(async (functionName) => {
await this.checkAllowedService(functionName)
const http = serviceProxy[functionName]
http.path = await this.getProxyPath(serviceProxy[functionName])
http.method = await this.getProxyMethod(serviceProxy[functionName])

if (serviceProxy[functionName].cors) {
http.cors = await this.getCors(serviceProxy[functionName])

const cors = corsPreflight[http.path] || {}

cors.headers = _.union(http.cors.headers, cors.headers)
cors.methods = _.union(http.cors.methods, cors.methods)
cors.origins = _.union(http.cors.origins, cors.origins)
cors.origin = http.cors.origin || '*'
cors.allowCredentials = cors.allowCredentials || http.cors.allowCredentials

// when merging, last one defined wins
if (_.has(http.cors, 'maxAge')) {
cors.maxAge = http.cors.maxAge
}
const serviceName = this.getServiceName(serviceProxy)
await this.checkAllowedService(serviceName)
const http = serviceProxy[serviceName]
http.path = await this.getProxyPath(serviceProxy[serviceName], serviceName)
http.method = await this.getProxyMethod(serviceProxy[serviceName], serviceName)

if (serviceProxy[serviceName].cors) {
http.cors = await this.getCors(serviceProxy[serviceName])

const cors = corsPreflight[http.path] || {}

cors.headers = _.union(http.cors.headers, cors.headers)
cors.methods = _.union(http.cors.methods, cors.methods)
cors.origins = _.union(http.cors.origins, cors.origins)
cors.origin = http.cors.origin || '*'
cors.allowCredentials = cors.allowCredentials || http.cors.allowCredentials

// when merging, last one defined wins
if (_.has(http.cors, 'maxAge')) {
cors.maxAge = http.cors.maxAge
}

corsPreflight[http.path] = cors
if (_.has(http.cors, 'cacheControl')) {
cors.cacheControl = http.cors.cacheControl
}

events.push({ functionName, http })
})
corsPreflight[http.path] = cors
}

events.push({ serviceName, http })
})
)
return {
Expand All @@ -55,15 +58,20 @@ module.exports = {
}
},

async getProxyPath(proxy) {
if (typeof proxy.path === 'string') {
async getProxyPath(proxy, serviceName) {
if (proxy.path && typeof proxy.path === 'string') {
return proxy.path.replace(/^\//, '').replace(/\/$/, '')
}
return BbPromise.reject(new this.serverless.classes.Error('Invalid service proxy syntax'))

return BbPromise.reject(
new this.serverless.classes.Error(
`Missing or invalid "path" property in ${serviceName} proxy`
)
)
},

async getProxyMethod(proxy) {
if (typeof proxy.method === 'string') {
async getProxyMethod(proxy, serviceName) {
if (proxy.method && typeof proxy.method === 'string') {
const method = proxy.method.toLowerCase()

const allowedMethods = ['get', 'post', 'put', 'patch', 'options', 'head', 'delete', 'any']
Expand All @@ -76,7 +84,11 @@ module.exports = {
}
return method
}
return BbPromise.reject(new this.serverless.classes.Error('Invalid service proxy syntax'))
return BbPromise.reject(
new this.serverless.classes.Error(
`Missing or invalid "method" property in ${serviceName} proxy`
)
)
},

async getCors(proxy) {
Expand Down Expand Up @@ -130,6 +142,13 @@ module.exports = {
if (cors.methods.indexOf(proxy.method.toUpperCase()) === NOT_FOUND) {
cors.methods.push(proxy.method.toUpperCase())
}

if (_.has(cors, 'maxAge')) {
if (!_.isInteger(cors.maxAge) || cors.maxAge < 1) {
const errorMessage = 'maxAge should be an integer over 0'
return BbPromise.reject(new this.serverless.classes.Error(errorMessage))
}
}
} else {
cors.methods.push(proxy.method.toUpperCase())
}
Expand Down
Loading