Skip to content

Commit 511e73b

Browse files
author
Diana Ionita
committed
Merge branch 'release/1.9.0'
2 parents e7a8adc + 4c6a390 commit 511e73b

14 files changed

+323
-49
lines changed

README.md

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ functions:
5353
- name: request.header.Accept-Language
5454
```
5555
56+
## Only supports REST API
57+
58+
This plugin only supports REST API, because HTTP API does not support API Gateway Caching at the time of this writing. See [docs](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-vs-rest.html).
59+
5660
## Time-to-live, encryption, cache invalidation settings
5761
5862
You can use the `apiGatewayCaching` section ("global settings") to quickly configure cache time-to-live, data encryption and per-key cache invalidation for all endpoints. The settings are inherited by each endpoint for which caching is enabled.
@@ -147,8 +151,8 @@ functions:
147151

148152
When the endpoint is hit, API Gateway will create cache entries based on the `pawId` path parameter and the `catName` query string parameter. For instance:
149153
- `GET /cats/4` will create a cache entry for `pawId=4` and `catName` as `undefined`.
150-
- `GET /cats/34?catName=Toby` will create a cache entry for `pawId=34` and `catName=Toby`.
151-
- `GET /cats/72?catName=Dixon&furColour=white` will create a cache entry for `pawId=72` and `catName=Dixon`, but will ignore the `furColour` query string parameter. That means that a subsequent request to `GET /cats/72?catName=Dixon&furColour=black` will return the cached response for `pawId=72` and `catName=Dixon`.
154+
- `GET /cats/34?catName=Dixon` will create a cache entry for `pawId=34` and `catName=Dixon`.
155+
- `GET /cats/72?catName=Tsunami&furColour=white` will create a cache entry for `pawId=72` and `catName=Tsunami`, but will ignore the `furColour` query string parameter. That means that a subsequent request to `GET /cats/72?catName=Tsunami&furColour=black` will return the cached response for `pawId=72` and `catName=Tsunami`.
152156

153157
### Cache key parameters from the path, query string and header
154158
When an endpoint varies its responses based on values found in the `path`, `query string` or `header`, you can specify all the parameter names as cache key parameters:
@@ -387,6 +391,53 @@ custom:
387391
dataEncrypted: true # if not set, inherited from global settings
388392
```
389393

394+
## Configuring caching when the endpoint bypasses lambda and talks to a service like DynamoDb
395+
396+
This example uses the `serverless-apigateway-service-proxy` plugin which creates the path `/dynamodb?id=cat_id`.
397+
Caching can be configured using the `additionalEndpoints` feature. The method and path must match the ones defined as a service proxy. It also supports cache key parameters.
398+
399+
```yml
400+
plugins:
401+
- serverless-api-gateway-caching
402+
- serverless-apigateway-service-proxy
403+
404+
custom:
405+
apiGatewayCaching:
406+
enabled: true
407+
additionalEndpoints:
408+
- method: GET
409+
path: /dynamodb
410+
caching:
411+
enabled: true
412+
ttlInSeconds: 120
413+
cacheKeyParameters:
414+
- name: request.querystring.id
415+
416+
apiGatewayServiceProxies:
417+
- dynamodb:
418+
path: /dynamodb
419+
method: get
420+
tableName: { Ref: 'MyDynamoCatsTable' }
421+
hashKey:
422+
queryStringParam: id # use query string parameter
423+
attributeType: S
424+
action: GetItem
425+
cors: true
426+
427+
resources:
428+
Resources:
429+
MyDynamoCatsTable:
430+
Type: AWS::DynamoDB::Table
431+
Properties:
432+
TableName: my-dynamo-cats
433+
AttributeDefinitions:
434+
- AttributeName: id
435+
AttributeType: S
436+
KeySchema:
437+
- AttributeName: id
438+
KeyType: HASH
439+
```
440+
390441
## More Examples
391442

392443
A function with several endpoints:

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "serverless-api-gateway-caching",
3-
"version": "1.8.1",
3+
"version": "1.9.0",
44
"description": "A plugin for the serverless framework which helps with configuring caching for API Gateway endpoints.",
55
"main": "src/apiGatewayCachingPlugin.js",
66
"scripts": {

src/ApiGatewayCachingSettings.js

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ class ApiGatewayEndpointCachingSettings {
8888
if (basePath.endsWith('/')) {
8989
basePath = basePath.slice(0, -1);
9090
}
91+
this.pathWithoutGlobalBasePath = this.path;
9192
this.path = basePath.concat(this.path);
9293
}
9394

@@ -113,11 +114,24 @@ class ApiGatewayAdditionalEndpointCachingSettings {
113114
constructor(method, path, caching, globalSettings) {
114115
this.method = method;
115116
this.path = path;
116-
this.cachingEnabled = globalSettings.cachingEnabled ? get(caching, 'enabled', false) : false;
117-
if (caching) {
118-
this.cacheTtlInSeconds = caching.ttlInSeconds >= 0 ? caching.ttlInSeconds : globalSettings.cacheTtlInSeconds;
117+
118+
this.gatewayResourceName = getApiGatewayResourceNameFor(this.path, this.method);
119+
120+
if (!caching) {
121+
this.cachingEnabled = false;
122+
return;
123+
}
124+
const cachingConfig = caching;
125+
this.cachingEnabled = globalSettings.cachingEnabled ? cachingConfig.enabled : false;
126+
this.dataEncrypted = cachingConfig.dataEncrypted || globalSettings.dataEncrypted;
127+
this.cacheTtlInSeconds = caching.ttlInSeconds >= 0 ? caching.ttlInSeconds : globalSettings.cacheTtlInSeconds;
128+
this.cacheKeyParameters = cachingConfig.cacheKeyParameters;
129+
130+
if (!cachingConfig.perKeyInvalidation) {
131+
this.perKeyInvalidation = globalSettings.perKeyInvalidation;
132+
} else {
133+
this.perKeyInvalidation = new PerKeyInvalidationSettings(cachingConfig);
119134
}
120-
this.dataEncrypted = get(caching, 'dataEncrypted', globalSettings.dataEncrypted);
121135
}
122136
}
123137

@@ -147,13 +161,6 @@ class ApiGatewayCachingSettings {
147161
this.cacheTtlInSeconds = cachingSettings.ttlInSeconds >= 0 ? cachingSettings.ttlInSeconds : DEFAULT_TTL;
148162
this.dataEncrypted = cachingSettings.dataEncrypted || DEFAULT_DATA_ENCRYPTED;
149163

150-
const additionalEndpoints = cachingSettings.additionalEndpoints || [];
151-
for (let additionalEndpoint of additionalEndpoints) {
152-
const { method, path, caching } = additionalEndpoint;
153-
154-
this.additionalEndpointSettings.push(new ApiGatewayAdditionalEndpointCachingSettings(method, path, caching, this))
155-
}
156-
157164
this.perKeyInvalidation = new PerKeyInvalidationSettings(cachingSettings);
158165

159166
for (let functionName in serverless.service.functions) {
@@ -164,6 +171,12 @@ class ApiGatewayCachingSettings {
164171
}
165172
}
166173
}
174+
175+
const additionalEndpoints = cachingSettings.additionalEndpoints || [];
176+
for (let additionalEndpoint of additionalEndpoints) {
177+
const { method, path, caching } = additionalEndpoint;
178+
this.additionalEndpointSettings.push(new ApiGatewayAdditionalEndpointCachingSettings(method, path, caching, this))
179+
}
167180
}
168181
}
169182

src/apiGatewayCachingPlugin.js

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use strict';
22

33
const ApiGatewayCachingSettings = require('./ApiGatewayCachingSettings');
4-
const pathParametersCache = require('./pathParametersCache');
4+
const cacheKeyParameters = require('./cacheKeyParameters');
55
const updateStageCacheSettings = require('./stageCache');
66
const { restApiExists, outputRestApiIdTo } = require('./restApiId');
77

@@ -37,7 +37,7 @@ class ApiGatewayCachingPlugin {
3737
return;
3838
}
3939

40-
return pathParametersCache.addPathParametersCacheConfig(this.settings, this.serverless);
40+
return cacheKeyParameters.addCacheKeyParametersConfig(this.settings, this.serverless);
4141
}
4242

4343
async updateStage() {
@@ -141,6 +141,26 @@ class ApiGatewayCachingPlugin {
141141
enabled: { type: 'boolean' },
142142
ttlInSeconds: { type: 'number' },
143143
dataEncrypted: { type: 'boolean' },
144+
perKeyInvalidation: {
145+
type: 'object',
146+
properties: {
147+
requireAuthorization: { type: 'boolean' },
148+
handleUnauthorizedRequests: {
149+
type: 'string',
150+
enum: ['Ignore', 'IgnoreWithWarning', 'Fail']
151+
}
152+
}
153+
},
154+
cacheKeyParameters: {
155+
type: 'array',
156+
items: {
157+
type: 'object',
158+
properties: {
159+
name: { type: 'string' },
160+
value: { type: 'string' }
161+
}
162+
}
163+
}
144164
}
145165
}
146166
}
Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@ const getResourcesByName = (name, serverless) => {
77
}
88
}
99

10-
const addPathParametersCacheConfig = (settings, serverless) => {
11-
for (let endpointSettings of settings.endpointSettings) {
10+
const applyCacheKeyParameterSettings = (settings, serverless) => {
11+
for (let endpointSettings of settings) {
1212
if (!endpointSettings.cacheKeyParameters) {
1313
continue;
1414
}
1515
const method = getResourcesByName(endpointSettings.gatewayResourceName, serverless);
1616
if (!method) {
1717
serverless.cli.log(`[serverless-api-gateway-caching] The method ${endpointSettings.gatewayResourceName} couldn't be found in the
18-
compiled CloudFormation template. Caching settings will not be updated for this endpoint.`);
18+
compiled CloudFormation template. Caching settings will not be updated for this endpoint.`);
1919
continue;
2020
}
2121
if (!method.Properties.Integration.CacheKeyParameters) {
@@ -58,7 +58,11 @@ const addPathParametersCacheConfig = (settings, serverless) => {
5858
method.Properties.Integration.CacheNamespace = `${endpointSettings.gatewayResourceName}CacheNS`;
5959
}
6060
}
61+
const addCacheKeyParametersConfig = (settings, serverless) => {
62+
applyCacheKeyParameterSettings(settings.endpointSettings, serverless);
63+
applyCacheKeyParameterSettings(settings.additionalEndpointSettings, serverless);
64+
}
6165

6266
module.exports = {
63-
addPathParametersCacheConfig: addPathParametersCacheConfig
67+
addCacheKeyParametersConfig: addCacheKeyParametersConfig
6468
}

src/stageCache.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,13 @@ const httpEventOf = (lambda, endpointSettings) => {
106106
}
107107
});
108108

109-
return httpEvents.filter(e => (e.path === endpointSettings.path) || (`/${e.path}` === endpointSettings.path))
109+
const event = httpEvents.filter(e =>
110+
(e.path === endpointSettings.path) ||
111+
(`/${e.path}` === endpointSettings.path) ||
112+
(e.path == endpointSettings.pathWithoutGlobalBasePath) ||
113+
(`/${e.path}` == endpointSettings.pathWithoutGlobalBasePath))
110114
.filter(e => e.method.toUpperCase() == endpointSettings.method.toUpperCase());
115+
return event;
111116
}
112117

113118
const createPatchForEndpoint = (endpointSettings, serverless) => {
@@ -121,7 +126,7 @@ const createPatchForEndpoint = (endpointSettings, serverless) => {
121126
serverless.cli.log(`[serverless-api-gateway-caching] Lambda ${endpointSettings.functionName} has not defined any HTTP events.`);
122127
return;
123128
}
124-
let { path, method } = httpEvents[0];
129+
const { path, method } = endpointSettings;
125130

126131
let patch = [];
127132
if (method.toUpperCase() == 'ANY') {
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
const given = require('../test/steps/given');
2+
const ApiGatewayCachingSettings = require('../src/ApiGatewayCachingSettings');
3+
const cacheKeyParams = require('../src/cacheKeyParameters');
4+
const expect = require('chai').expect;
5+
6+
describe('Configuring path parameters for additional endpoints defined as CloudFormation', () => {
7+
let serverless;
8+
let serviceName = 'cat-api', stage = 'dev';
9+
10+
describe('when there are no additional endpoints', () => {
11+
before(() => {
12+
serverless = given.a_serverless_instance(serviceName)
13+
.withApiGatewayCachingConfig()
14+
.forStage(stage);
15+
});
16+
17+
it('should do nothing to the serverless instance', () => {
18+
let stringified = JSON.stringify(serverless);
19+
when_configuring_cache_key_parameters(serverless);
20+
let stringifiedAfter = JSON.stringify(serverless);
21+
expect(stringified).to.equal(stringifiedAfter);
22+
});
23+
});
24+
25+
describe('when one of the additional endpoints has cache key parameters', () => {
26+
let cacheKeyParameters, apiGatewayMethod;
27+
before(() => {
28+
cacheKeyParameters = [
29+
{ name: 'request.path.pawId' },
30+
{ name: 'request.header.Accept-Language' }]
31+
const additionalEndpointWithCaching = given.an_additional_endpoint({
32+
method: 'GET', path: '/items',
33+
caching: {
34+
enabled: true, ttlInSeconds: 120,
35+
cacheKeyParameters
36+
}
37+
})
38+
const additionalEndpointWithoutCaching = given.an_additional_endpoint({
39+
method: 'POST', path: '/blue-items',
40+
caching: { enabled: true }
41+
});
42+
43+
serverless = given.a_serverless_instance(serviceName)
44+
.withApiGatewayCachingConfig()
45+
.withAdditionalEndpoints([additionalEndpointWithCaching, additionalEndpointWithoutCaching])
46+
.forStage('somestage');
47+
48+
when_configuring_cache_key_parameters(serverless);
49+
50+
apiGatewayMethod = serverless.getMethodResourceForAdditionalEndpoint(additionalEndpointWithCaching);
51+
});
52+
53+
it('should configure the method\'s request parameters', () => {
54+
for (let parameter of cacheKeyParameters) {
55+
expect(apiGatewayMethod.Properties.RequestParameters)
56+
.to.deep.include({
57+
[`method.${parameter.name}`]: {}
58+
});
59+
}
60+
});
61+
62+
it('should not set integration request parameters', () => {
63+
for (let parameter of cacheKeyParameters) {
64+
expect(apiGatewayMethod.Properties.Integration.RequestParameters)
65+
.to.not.include({
66+
[`integration.${parameter.name}`]: `method.${parameter.name}`
67+
});
68+
}
69+
});
70+
71+
it('should set the method\'s integration cache key parameters', () => {
72+
for (let parameter of cacheKeyParameters) {
73+
expect(apiGatewayMethod.Properties.Integration.CacheKeyParameters)
74+
.to.include(`method.${parameter.name}`);
75+
}
76+
});
77+
78+
it('should set a cache namespace', () => {
79+
expect(apiGatewayMethod.Properties.Integration.CacheNamespace).to.exist;
80+
});
81+
});
82+
});
83+
84+
const when_configuring_cache_key_parameters = (serverless) => {
85+
let cacheSettings = new ApiGatewayCachingSettings(serverless);
86+
return cacheKeyParams.addCacheKeyParametersConfig(cacheSettings, serverless);
87+
}

0 commit comments

Comments
 (0)