UNPKG

4.28 kBJavaScriptView Raw
1const hash = require('object-hash');
2
3const typemap = {
4 REQUEST: 'request_condition',
5 RESPONSE: 'response_condition',
6 CACHE: 'cache_condition',
7};
8
9/** Helper class with high-level operations for condition-management. */
10class Headers {
11 constructor(fastly) {
12 this._fastly = fastly;
13 }
14
15 /**
16 * Creates functions for multi-step creation of missing and deletion of
17 * superflous conditional headers.
18 *
19 * @param {number} version - Service config version.
20 * @param {string} type - Condition type, can be `REQUEST`, `RESPONSE`, or `CACHE`.
21 * @param {string} commentprefix - The prefix to be used for comments.
22 * @param {string} nameprefix - - The prefix to be used for names.
23 * @param {string} action - What do do with the header, can be `set`, `append`, `delete`.
24 * @param {string} header - The name of the header to set.
25 * @param {string} sub - Name of the subroutine where the header
26 * should be applied, can be `request`, `fetch`, `cache`, or `response`.
27 * @returns {Function[]} A pair of a create and cleanup function.
28 */
29 update(version, type, commentprefix, nameprefix, action, header, sub) {
30 const makeheaders = (headers, namedcondition) => headers.map(({ condition, expression }) => {
31 const hashable = {
32 type: sub,
33 action,
34 dst: header,
35 src: expression,
36 };
37
38 // set `request_condition` etc. to the *name* that corresponds to the condition statement
39 hashable[typemap[type]] = namedcondition[condition].name;
40
41 const name = `${nameprefix}-${hash(hashable)}`;
42
43 return {
44 name,
45 priority: '10',
46 ...hashable,
47 };
48 });
49
50 const [
51 createconditions,
52 cleanupconditions] = this._fastly.conditions.multistepupdate(
53 version,
54 type,
55 commentprefix,
56 nameprefix,
57 );
58
59 // each header is a tuple like this:
60 // `{ condition: 'req.url ~ "foo/(.*)/bar"', expression: '"bar/" + re.group.1 + "/foo"'}`
61 // this function extracts the condition bit.
62 const conditions = (headers) => headers.map(({ condition }) => condition);
63
64 // this function takes care of the creation of new headers by looking
65 // at existing headers and finding ones that are missing from the service
66 const createheaders = async (...headers) => {
67 const jobs = [];
68 // get the map of condition statements to condition names
69 const namedconditions = await createconditions(...conditions(headers));
70
71 const existing = (await this._fastly.readHeaders(version)).data;
72 // keep a list of known names in the remote service
73 const existingnames = new Set(existing.map(({ name }) => name));
74
75 const headernames = makeheaders(headers, namedconditions);
76 const headernameset = new Set(headernames.map(({ name }) => name));
77
78 const headerstobecreated = headernames
79 // only consider conditions that don't exist in the remote service
80 .filter(({ name }) => !existingnames.has(name))
81 // schedule each condition that does not yet exist on Fastly
82 // but was passed as an argument to be created
83 .map((h) => this._fastly.createHeader(version, h));
84
85 // all headers need to be created
86 jobs.push(headerstobecreated);
87
88 const todelete = existing
89 // only look at headers that start with 'our' prefix
90 .filter(({ name }) => name.startsWith(`${nameprefix}-`))
91 // only keep those that are *not* in the list of generated names
92 .filter(({ name }) => !headernameset.has(name))
93 // only consider those for deletetion where type, action, and dst do match
94 .filter((h) => h.type === sub)
95 .filter((h) => h.action === action)
96 .filter((h) => h.dst === header);
97
98 // schedule each remaining header that exists on Fastly, but wasn't passed as
99 // an argument for deletion
100 jobs.push(todelete.map(({ name }) => this._fastly.deleteHeader(version, name)));
101
102 jobs.push(todelete);
103 // finally, clean up the conditions we no longer need
104 jobs.push(cleanupconditions(...conditions(headers)));
105
106 // return a promise that waits for the creation of all headers
107 return Promise.all(jobs);
108 };
109
110 return createheaders;
111 }
112}
113
114module.exports = Headers;