Skip to content

Commit

Permalink
Merge branch 'release/1.4.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
diana.ionita committed May 27, 2019
2 parents 4cccb7f + d3a2456 commit a04b588
Show file tree
Hide file tree
Showing 7 changed files with 195 additions and 7 deletions.
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,26 @@ functions:
- name: request.path.pawId
- name: request.header.Accept-Language
```
### Configuring a shared api gateway
No modifications will be applied to the root caching configuration of the api gateway,
Cache time to live, invalidation settings and data encryption are applied to all functions, unless specifically overridden.
```yml
plugins:
- serverless-api-gateway-caching

custom:
# Enable or disable caching globally
apiGatewayCaching:
enabled: true
apiGatewayIsShared: true
clusterSize: '0.5' # defaults to '0.5'
ttlInSeconds: 300 # defaults to the maximum allowed: 3600
dataEncrypted: true # defaults to false
perKeyInvalidation:
requireAuthorization: true # default is true
handleUnauthorizedRequests: Ignore # default is "IgnoreWithWarning"

```
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "serverless-api-gateway-caching",
"version": "1.3.8",
"version": "1.4.0",
"description": "A plugin for the serverless framework which helps with configuring caching for API Gateway endpoints.",
"main": "src/apiGatewayCachingPlugin.js",
"scripts": {
Expand Down
1 change: 1 addition & 0 deletions src/ApiGatewayCachingSettings.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ class ApiGatewayCachingSettings {
return;
}
this.cachingEnabled = serverless.service.custom.apiGatewayCaching.enabled;
this.apiGatewayIsShared = serverless.service.custom.apiGatewayCaching.apiGatewayIsShared;

if (options) {
this.stage = options.stage || serverless.service.provider.stage;
Expand Down
18 changes: 16 additions & 2 deletions src/stageCache.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,37 @@ const escapeJsonPointer = path => {
}

const createPatchForStage = (settings) => {

if (settings.apiGatewayIsShared) {
return [];
}

let patch = [{
op: 'replace',
path: '/cacheClusterEnabled',
value: `${settings.cachingEnabled}`
}]
}];

if (settings.cachingEnabled) {
patch.push({
op: 'replace',
path: '/cacheClusterSize',
value: `${settings.cacheClusterSize}`
});

patch.push({
op: 'replace',
path: '/*/*/caching/dataEncrypted',
value: `${settings.dataEncrypted}`
value: `${settings.dataEncrypted}`
});

patch.push({
op: 'replace',
path: '/*/*/caching/ttlInSeconds',
value: `${settings.cacheTtlInSeconds}`
});
}

return patch;
}

Expand Down
3 changes: 2 additions & 1 deletion test/model/Serverless.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,10 @@ class Serverless {
return this;
}

withApiGatewayCachingConfig(cachingEnabled, clusterSize, ttlInSeconds, perKeyInvalidation, dataEncrypted) {
withApiGatewayCachingConfig(cachingEnabled, clusterSize, ttlInSeconds, perKeyInvalidation, dataEncrypted, apiGatewayIsShared) {
this.service.custom.apiGatewayCaching = {
enabled: cachingEnabled,
apiGatewayIsShared: apiGatewayIsShared,
clusterSize,
ttlInSeconds,
perKeyInvalidation,
Expand Down
153 changes: 151 additions & 2 deletions test/updating-stage-cache-settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,147 @@ describe('Updating stage cache settings', () => {
});
});

describe('When api gateway caching is true but api gateway is shared', () => {
let restApiId;

describe('and there are no endpoints for which to enable caching', () => {
before(async () => {
serverless = given.a_serverless_instance()
.withApiGatewayCachingConfig(undefined, undefined, undefined, undefined, undefined, true)
.forStage('somestage');
settings = new ApiGatewayCachingSettings(serverless);

restApiId = await given.a_rest_api_id_for_deployment(serverless, settings);

await when_updating_stage_cache_settings(settings, serverless);

requestsToAws = serverless.getRequestsToAws();
});

it('should not make calls to the AWS SDK', () => {
expect(requestsToAws).to.be.empty;
});
});

describe('and there are some endpoints with caching enabled', () => {
before(async () => {
let endpointWithoutCaching = given.a_serverless_function('get-my-cat')
.withHttpEndpoint('get', '/personal/cat', { enabled: false })
.withHttpEndpoint('get', '/personal/cat/{catId}', { enabled: false });
let endpointWithCaching = given.a_serverless_function('get-cat-by-paw-id')
.withHttpEndpoint('get', '/cat/{pawId}', { enabled: true, ttlInSeconds: 45, dataEncrypted: true })
.withHttpEndpoint('delete', '/cat/{pawId}', { enabled: true, ttlInSeconds: 45 });
serverless = given.a_serverless_instance()
.withApiGatewayCachingConfig(true, '0.5', 60, undefined, false, true)
.withFunction(endpointWithCaching)
.withFunction(endpointWithoutCaching)
.forStage('somestage');

settings = new ApiGatewayCachingSettings(serverless);

restApiId = await given.a_rest_api_id_for_deployment(serverless, settings);

await when_updating_stage_cache_settings(settings, serverless);

requestsToAws = serverless.getRequestsToAws();
});

describe('the request sent to AWS SDK to update stage', () => {
const noOperationAreExpectedForPath = (path) => () => {
const foundItems = apiGatewayRequest.properties.patchOperations.filter((item => item.path === path))
expect(foundItems.length).to.equal(0);
}

before(() => {
apiGatewayRequest = requestsToAws.find(r => r.awsService == apiGatewayService && r.method == updateStageMethod);
});

it('should contain the Rest Api Id', () => {
expect(apiGatewayRequest.properties.restApiId).to.equal(restApiId);
});

it('should contain the stage name', () => {
expect(apiGatewayRequest.properties.stageName).to.equal('somestage');
});

it('should leave caching untouched', noOperationAreExpectedForPath ('/cacheClusterEnabled'));

it('should leave the cache cluster size untouched', noOperationAreExpectedForPath ('/cacheClusterSize'));

describe('for the endpoint with caching enabled', () => {
it('should enable caching', () => {
expect(apiGatewayRequest.properties.patchOperations).to.deep.include({
op: 'replace',
path: '/~1cat~1{pawId}/GET/caching/enabled',
value: 'true'
});
expect(apiGatewayRequest.properties.patchOperations).to.deep.include({
op: 'replace',
path: '/~1cat~1{pawId}/DELETE/caching/enabled',
value: 'true'
});
});

it('should set the correct cache time to live', () => {
expect(apiGatewayRequest.properties.patchOperations).to.deep.include({
op: 'replace',
path: '/~1cat~1{pawId}/GET/caching/ttlInSeconds',
value: '45'
});
expect(apiGatewayRequest.properties.patchOperations).to.deep.include({
op: 'replace',
path: '/~1cat~1{pawId}/DELETE/caching/ttlInSeconds',
value: '45'
});
});

it('should configure data encryption where enabled', () => {
expect(apiGatewayRequest.properties.patchOperations).to.deep.include({
op: 'replace',
path: '/~1cat~1{pawId}/GET/caching/dataEncrypted',
value: 'true'
});
expect(apiGatewayRequest.properties.patchOperations).to.deep.include({
op: 'replace',
path: '/~1cat~1{pawId}/DELETE/caching/dataEncrypted',
value: 'false'
});
});
});

describe('for each endpoint with caching disabled', () => {
it('should disable caching', () => {
expect(apiGatewayRequest.properties.patchOperations).to.deep.include({
op: 'replace',
path: '/~1personal~1cat/GET/caching/enabled',
value: 'false'
});
expect(apiGatewayRequest.properties.patchOperations).to.deep.include({
op: 'replace',
path: '/~1personal~1cat~1{catId}/GET/caching/enabled',
value: 'false'
});
});

it('should not set the cache time to live', () => {
let ttlOperation = apiGatewayRequest.properties.patchOperations
.find(o => o.path == '/~personal~1cat/GET/caching/ttlInSeconds' ||
o.path == '/~personal~1cat~1{catId}/GET/caching/ttlInSeconds');
expect(ttlOperation).to.not.exist;
});

it('should not configure data encryption', () => {
let dataEncryptionOperation = apiGatewayRequest.properties.patchOperations
.find(o => o.path == '/~personal~1cat/GET/caching/dataEncryption' ||
o.path == '/~personal~1cat~1{catId}/GET/caching/dataEncryption');
expect(dataEncryptionOperation).to.not.exist;
});
});
});
});

})

describe('When api gateway caching is enabled', () => {
let restApiId;

Expand Down Expand Up @@ -107,8 +248,8 @@ describe('Updating stage cache settings', () => {
expect(apiGatewayRequest.properties.stageName).to.equal('somestage');
});

it('should specify exactly three patch operations', () => {
expect(apiGatewayRequest.properties.patchOperations).to.have.lengthOf(3);
it('should specify exactly four patch operations', () => {
expect(apiGatewayRequest.properties.patchOperations).to.have.lengthOf(4);
})

it('should enable caching', () => {
Expand All @@ -135,6 +276,14 @@ describe('Updating stage cache settings', () => {
});
});

it('should set the cache ttlInSeconds', () => {
expect(apiGatewayRequest.properties.patchOperations).to.deep.include({
op: 'replace',
path: '/*/*/caching/ttlInSeconds',
value: '60'
});
});

it('should log a warning message that no endpoints are being cached', () => {
expect(serverless._logMessages).to
.include('[serverless-api-gateway-caching] [WARNING] API Gateway caching is enabled but none of the endpoints have caching enabled');
Expand Down

0 comments on commit a04b588

Please sign in to comment.