Skip to content

Commit 8f87147

Browse files
author
diana.ionita
committed
Merge branch 'release/1.1.0'
2 parents fbc6cb0 + 62cc8a2 commit 8f87147

12 files changed

+499
-56
lines changed

.circleci/config.yml

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@ references:
44
container_config: &container_config
55
docker:
66
- image: circleci/node:8.10
7-
environment:
8-
MOCHA_FILE: ./test-results/unit/test-results.xml
9-
107
poke_npmrc: &poke_npmrc
118
run: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc
129

@@ -25,12 +22,12 @@ references:
2522
create_test_results_dir: &create_test_results_dir
2623
run:
2724
command: |
28-
mkdir ./test-results
29-
mkdir ./test-results/unit
25+
mkdir test-results
26+
mkdir test-results/mocha
3027
3128
store_test_results: &store_test_results
3229
store_test_results:
33-
path: ./test-results
30+
path: test-results
3431

3532
jobs:
3633
dev:
@@ -41,7 +38,11 @@ jobs:
4138
- *restore_npm_cache
4239
- run: npm install
4340
- *save_npm_cache
44-
- run: npm run test -- --reporter mocha-junit-reporter
41+
- *create_test_results_dir
42+
- run:
43+
environment:
44+
MOCHA_FILE: ./test-results/mocha/results.xml
45+
command: npm run test -- --reporter mocha-junit-reporter
4546
- *store_test_results
4647

4748
live:

README.md

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,20 @@
66
A plugin for the serverless framework which helps with configuring caching for API Gateway endpoints.
77

88
## Good to know
9-
If you enable caching globally, it does NOT automatically enable caching for your endpoints - you have to be explicit about which endpoints should have caching enabled.
9+
* If you enable caching globally, it does NOT automatically enable caching for your endpoints - you have to be explicit about which endpoints should have caching enabled.
1010
However, disabling caching globally disables it across endpoints.
11+
* If you don't specify `ttlInSeconds` and `perKeyInvalidation` for an endpoint which has caching enabled, these settings are inherited from global settings.
12+
* For HTTP method `ANY`, caching will be enabled only for the `GET` method and disabled for the other methods.
13+
14+
## Per-key cache invalidation
15+
If you don't configure per-key cache invalidation authorization, by default it is *required*.
16+
You can configure how to handle unauthorized requests to invalidate a cache key using the options:
17+
* `Ignore` - ignores the request to invalidate the cache key.
18+
* `IgnoreWithWarning` - ignores the request to invalidate and adds a `warning` header in the response.
19+
* `Fail` - fails the request to invalidate the cache key with a 403 response status code.
20+
21+
## Currently not supported:
22+
* lambda functions with many HTTP events.
1123

1224
## Example
1325

@@ -21,6 +33,9 @@ custom:
2133
enabled: true
2234
clusterSize: '0.5' # defaults to '0.5'
2335
ttlInSeconds: 300 # defaults to the maximum allowed: 3600
36+
perKeyInvalidation:
37+
requireAuthorization: true # default is true
38+
handleUnauthorizedRequests: IgnoreWithWarning # default is "IgnoreWithWarning"
2439

2540
functions:
2641
# Responses are not cached
@@ -44,15 +59,10 @@ functions:
4459
caching:
4560
enabled: true
4661
ttlInSeconds: 3600
62+
perKeyInvalidation:
63+
requireAuthorization: true
64+
handleUnauthorizedRequests: Ignore
4765
cacheKeyParameters:
4866
- name: request.path.pawId
49-
required: true
5067
- name: request.header.Accept-Language
51-
required: false
5268
```
53-
54-
## Limitations
55-
* For HTTP method `ANY`, caching will be enabled only for the `GET` method and disabled for the other methods.
56-
57-
## Currently not supported:
58-
* lambda functions with many http events

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: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
{
22
"name": "serverless-api-gateway-caching",
3-
"version": "1.0.0",
3+
"version": "1.1.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": {
7-
"test": "mocha --recursive test/**/*.js -t 5000"
7+
"test": "mocha --recursive 'test/**/*.js' -t 5000"
88
},
99
"keywords": [
1010
"serverless",

src/ApiGatewayCachingSettings.js

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,46 @@
11
const isEmpty = require('lodash.isempty');
22
const get = require('lodash.get');
3+
const { Ignore, IgnoreWithWarning, Fail } = require('./UnauthorizedCacheControlHeaderStrategy');
4+
5+
const DEFAULT_CACHE_CLUSTER_SIZE = '0.5';
6+
const DEFAULT_TTL = 3600;
7+
const DEFAULT_UNAUTHORIZED_INVALIDATION_REQUEST_STRATEGY = IgnoreWithWarning;
8+
9+
const mapUnauthorizedRequestStrategy = strategy => {
10+
if (!strategy) {
11+
return DEFAULT_UNAUTHORIZED_INVALIDATION_REQUEST_STRATEGY;
12+
}
13+
switch (strategy.toLowerCase()) {
14+
case 'ignore': return Ignore;
15+
case 'ignorewithwarning': return IgnoreWithWarning;
16+
case 'fail': return Fail;
17+
default: return DEFAULT_UNAUTHORIZED_INVALIDATION_REQUEST_STRATEGY;
18+
}
19+
}
20+
21+
const isApiGatewayEndpoint = functionSettings => {
22+
if (isEmpty(functionSettings.events)) {
23+
return false;
24+
}
25+
return functionSettings.events.filter(e => e.http != null).length > 0;
26+
}
27+
28+
class PerKeyInvalidationSettings {
29+
constructor(cachingSettings) {
30+
let { perKeyInvalidation } = cachingSettings;
31+
if (!perKeyInvalidation) {
32+
this.requireAuthorization = true;
33+
this.handleUnauthorizedRequests = DEFAULT_UNAUTHORIZED_INVALIDATION_REQUEST_STRATEGY;
34+
}
35+
else {
36+
this.requireAuthorization = perKeyInvalidation.requireAuthorization
37+
if (perKeyInvalidation.requireAuthorization) {
38+
this.handleUnauthorizedRequests =
39+
mapUnauthorizedRequestStrategy(perKeyInvalidation.handleUnauthorizedRequests);
40+
}
41+
}
42+
}
43+
}
344

445
class ApiGatewayEndpointCachingSettings {
546
constructor(functionName, functionSettings, globalSettings) {
@@ -14,13 +55,17 @@ class ApiGatewayEndpointCachingSettings {
1455
this.cachingEnabled = globalSettings.cachingEnabled ? cachingConfig.enabled : false;
1556
this.cacheTtlInSeconds = cachingConfig.ttlInSeconds || globalSettings.cacheTtlInSeconds;
1657
this.cacheKeyParameters = cachingConfig.cacheKeyParameters;
58+
59+
if (!cachingConfig.perKeyInvalidation) {
60+
this.perKeyInvalidation = globalSettings.perKeyInvalidation;
61+
} else {
62+
this.perKeyInvalidation = new PerKeyInvalidationSettings(cachingConfig);
63+
}
1764
}
1865
}
1966

2067
class ApiGatewayCachingSettings {
2168
constructor(serverless, options) {
22-
const DEFAULT_CACHE_CLUSTER_SIZE = '0.5';
23-
const DEFAULT_TTL = 3600;
2469
if (!get(serverless, 'service.custom.apiGatewayCaching')) {
2570
return;
2671
}
@@ -39,19 +84,15 @@ class ApiGatewayCachingSettings {
3984
this.cacheClusterSize = serverless.service.custom.apiGatewayCaching.clusterSize || DEFAULT_CACHE_CLUSTER_SIZE;
4085
this.cacheTtlInSeconds = serverless.service.custom.apiGatewayCaching.ttlInSeconds || DEFAULT_TTL;
4186

87+
this.perKeyInvalidation = new PerKeyInvalidationSettings(serverless.service.custom.apiGatewayCaching);
88+
4289
for (let functionName in serverless.service.functions) {
4390
let functionSettings = serverless.service.functions[functionName];
44-
if (this.isApiGatewayEndpoint(functionSettings)) {
91+
if (isApiGatewayEndpoint(functionSettings)) {
4592
this.endpointSettings.push(new ApiGatewayEndpointCachingSettings(functionName, functionSettings, this))
4693
}
4794
}
4895
}
49-
50-
isApiGatewayEndpoint(functionSettings) {
51-
if (isEmpty(functionSettings.events)) {
52-
return false;
53-
}
54-
return functionSettings.events.filter(e => e.http != null).length > 0;
55-
}
5696
}
97+
5798
module.exports = ApiGatewayCachingSettings
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module.exports = {
2+
Fail: 'FAIL_WITH_403',
3+
IgnoreWithWarning: 'SUCCEED_WITH_RESPONSE_HEADER',
4+
Ignore: 'SUCCEED_WITHOUT_RESPONSE_HEADER'
5+
}

src/pathParametersCache.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ const addPathParametersCacheConfig = (settings, serverless) => {
4848
}
4949

5050
for (let cacheKeyParameter of endpointSettings.cacheKeyParameters) {
51-
method.resource.Properties.RequestParameters[`method.${cacheKeyParameter.name}`] = cacheKeyParameter.required;
51+
method.resource.Properties.RequestParameters[`method.${cacheKeyParameter.name}`] = true;
5252
method.resource.Properties.Integration.RequestParameters[`integration.${cacheKeyParameter.name}`] = `method.${cacheKeyParameter.name}`;
5353
method.resource.Properties.Integration.CacheKeyParameters.push(`method.${cacheKeyParameter.name}`);
5454
}

src/stageCache.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,20 @@ const patchForMethod = (path, method, endpointSettings) => {
6060
value: `${endpointSettings.cacheTtlInSeconds}`
6161
})
6262
}
63+
if (endpointSettings.perKeyInvalidation) {
64+
patch.push({
65+
op: 'replace',
66+
path: `/${patchPath}/caching/requireAuthorizationForCacheControl`,
67+
value: `${endpointSettings.perKeyInvalidation.requireAuthorization}`
68+
});
69+
if (endpointSettings.perKeyInvalidation.requireAuthorization) {
70+
patch.push({
71+
op: 'replace',
72+
path: `/${patchPath}/caching/unauthorizedCacheControlHeaderStrategy`,
73+
value: `${endpointSettings.perKeyInvalidation.handleUnauthorizedRequests}`
74+
});
75+
}
76+
}
6377
return patch;
6478
}
6579

@@ -113,7 +127,7 @@ const updateStageCacheSettings = async (settings, serverless) => {
113127
});
114128

115129
let patchOps = createPatchForStage(settings);
116-
130+
117131
let endpointsWithCachingEnabled = settings.endpointSettings.filter(e => e.cachingEnabled);
118132
if (settings.cachingEnabled && isEmpty(endpointsWithCachingEnabled)) {
119133
serverless.cli.log(`[serverless-api-gateway-caching] [WARNING] API Gateway caching is enabled but none of the endpoints have caching enabled`);

test/configuring-path-parameters.js

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,7 @@ describe('Configuring path parameter caching', () => {
5252
.withHttpEndpoint('get', '/cats');
5353

5454
functionWithCachingName = 'get-cat-by-paw-id';
55-
cacheKeyParameters = [
56-
{ name: 'request.path.pawId', required: true },
57-
{ name: 'request.header.Accept-Language', required: false }];
55+
cacheKeyParameters = [{ name: 'request.path.pawId' }, { name: 'request.header.Accept-Language' }];
5856
let functionWithCaching = given.a_serverless_function(functionWithCachingName)
5957
.withHttpEndpoint('get', '/cat/{pawId}', { enabled: true, cacheKeyParameters });
6058

@@ -73,13 +71,13 @@ describe('Configuring path parameter caching', () => {
7371
method = serverless.getMethodResourceForFunction(functionWithCachingName);
7472
});
7573

76-
it('should set whether request parameters are required', () => {
77-
for (let parameter of cacheKeyParameters) {
78-
expect(method.Properties.RequestParameters)
79-
.to.deep.include({
80-
[`method.${parameter.name}`]: parameter.required
81-
});
82-
}
74+
it('should set that request parameters are part of the cache key', () => {
75+
for (let parameter of cacheKeyParameters) {
76+
expect(method.Properties.RequestParameters)
77+
.to.deep.include({
78+
[`method.${parameter.name}`]: true
79+
});
80+
}
8381
});
8482

8583
it('should set integration request parameters', () => {

0 commit comments

Comments
 (0)