@stuntman/client 0.1.11 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ruleBuilder.d.ts +4 -4
- package/dist/ruleBuilder.d.ts.map +1 -1
- package/dist/ruleBuilder.js +265 -270
- package/dist/ruleBuilder.js.map +1 -1
- package/package.json +6 -6
- package/src/ruleBuilder.ts +282 -296
package/dist/ruleBuilder.d.ts
CHANGED
|
@@ -48,7 +48,10 @@ declare class RuleBuilder extends RuleBuilderBase {
|
|
|
48
48
|
onRequestToPort(port: string | number | RegExp): RuleBuilderInitialized;
|
|
49
49
|
onAnyRequest(): RuleBuilderInitialized;
|
|
50
50
|
}
|
|
51
|
-
declare class
|
|
51
|
+
declare class RuleBuilderRequestInitialized extends RuleBuilderBase {
|
|
52
|
+
modifyResponse(modifyFunction: Stuntman.ResponseManipulationFn | Stuntman.RemotableFunction<Stuntman.ResponseManipulationFn>, localVariables?: Stuntman.LocalVariables): Stuntman.SerializableRule;
|
|
53
|
+
}
|
|
54
|
+
declare class RuleBuilderInitialized extends RuleBuilderRequestInitialized {
|
|
52
55
|
withHostname(hostname: string | RegExp): this;
|
|
53
56
|
withPathname(pathname: string | RegExp): this;
|
|
54
57
|
withPort(port: number | string | RegExp): this;
|
|
@@ -72,9 +75,6 @@ declare class RuleBuilderInitialized extends RuleBuilderBase {
|
|
|
72
75
|
modifyRequest(modifyFunction: Stuntman.RequestManipulationFn | Stuntman.RemotableFunction<Stuntman.RequestManipulationFn>, localVariables?: Stuntman.LocalVariables): RuleBuilderRequestInitialized;
|
|
73
76
|
modifyResponse(modifyFunction: Stuntman.ResponseManipulationFn | Stuntman.RemotableFunction<Stuntman.ResponseManipulationFn>, localVariables?: Stuntman.LocalVariables): Stuntman.SerializableRule;
|
|
74
77
|
}
|
|
75
|
-
declare class RuleBuilderRequestInitialized extends RuleBuilderBase {
|
|
76
|
-
modifyResponse(modifyFunction: Stuntman.ResponseManipulationFn | Stuntman.RemotableFunction<Stuntman.ResponseManipulationFn>, localVariables?: Stuntman.LocalVariables): Stuntman.SerializableRule;
|
|
77
|
-
}
|
|
78
78
|
export declare const ruleBuilder: () => RuleBuilder;
|
|
79
79
|
export {};
|
|
80
80
|
//# sourceMappingURL=ruleBuilder.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ruleBuilder.d.ts","sourceRoot":"","sources":["../src/ruleBuilder.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAGlD,KAAK,eAAe,GAAG,MAAM,GAAG,MAAM,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,CAAC;AAClF,KAAK,kBAAkB,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;AACpE,KAAK,qBAAqB,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,kBAAkB,CAAA;CAAE,CAAC;AACzE,KAAK,iBAAiB,GAAG;IACrB,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAChC,SAAS,CAAC,EAAE,qBAAqB,EAAE,CAAC;IACpC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,IAAI,CAAC,EAAE,OAAO,GAAG,UAAU,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAChC,CAAC;AAEF,KAAK,qBAAqB,GAAG;IACzB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IAChC,YAAY,CAAC,EAAE,eAAe,EAAE,CAAC;IACjC,OAAO,CAAC,EAAE,eAAe,EAAE,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,EAAE,qBAAqB,EAAE,CAAC;IACnC,OAAO,CAAC,EAAE,iBAAiB,CAAC;CAC/B,CAAC;
|
|
1
|
+
{"version":3,"file":"ruleBuilder.d.ts","sourceRoot":"","sources":["../src/ruleBuilder.ts"],"names":[],"mappings":"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"}
|
package/dist/ruleBuilder.js
CHANGED
|
@@ -4,6 +4,251 @@ exports.ruleBuilder = void 0;
|
|
|
4
4
|
const uuid_1 = require("uuid");
|
|
5
5
|
const shared_1 = require("@stuntman/shared");
|
|
6
6
|
// TODO add fluent match on multipart from data
|
|
7
|
+
function matchFunction(req) {
|
|
8
|
+
var _a, _b, _c, _d, _e;
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
10
|
+
// @ts-ignore
|
|
11
|
+
const localMatchBuilderVariables = (_a = this === null || this === void 0 ? void 0 : this.matchBuilderVariables) !== null && _a !== void 0 ? _a : matchBuilderVariables;
|
|
12
|
+
const ___url = new URL(req.url);
|
|
13
|
+
const ___headers = req.rawHeaders;
|
|
14
|
+
const arrayIndexerRegex = /\[(?<arrayIndex>[0-9]*)\]/i;
|
|
15
|
+
const matchObject = (obj, path, value, parentPath) => {
|
|
16
|
+
var _a, _b, _c, _d;
|
|
17
|
+
if (!obj) {
|
|
18
|
+
return { result: false, description: `${parentPath} is falsey` };
|
|
19
|
+
}
|
|
20
|
+
const [rawKey, ...rest] = path.split('.');
|
|
21
|
+
const key = (rawKey !== null && rawKey !== void 0 ? rawKey : '').replace(arrayIndexerRegex, '');
|
|
22
|
+
const shouldBeArray = rawKey ? arrayIndexerRegex.test(rawKey) : false;
|
|
23
|
+
const arrayIndex = rawKey && (((_b = (_a = arrayIndexerRegex.exec(rawKey)) === null || _a === void 0 ? void 0 : _a.groups) === null || _b === void 0 ? void 0 : _b.arrayIndex) || '').length > 0
|
|
24
|
+
? Number((_d = (_c = arrayIndexerRegex.exec(rawKey)) === null || _c === void 0 ? void 0 : _c.groups) === null || _d === void 0 ? void 0 : _d.arrayIndex)
|
|
25
|
+
: Number.NaN;
|
|
26
|
+
const actualValue = key ? obj[key] : obj;
|
|
27
|
+
const currentPath = `${parentPath ? `${parentPath}.` : ''}${rawKey}`;
|
|
28
|
+
if (value === undefined && actualValue === undefined) {
|
|
29
|
+
return { result: false, description: `${currentPath}=undefined` };
|
|
30
|
+
}
|
|
31
|
+
if (rest.length === 0) {
|
|
32
|
+
if (shouldBeArray &&
|
|
33
|
+
(!Array.isArray(actualValue) || (Number.isInteger(arrayIndex) && actualValue.length <= Number(arrayIndex)))) {
|
|
34
|
+
return { result: false, description: `${currentPath} empty array` };
|
|
35
|
+
}
|
|
36
|
+
if (value === undefined) {
|
|
37
|
+
const result = shouldBeArray
|
|
38
|
+
? !Number.isInteger(arrayIndex) || actualValue.length >= Number(arrayIndex)
|
|
39
|
+
: actualValue !== undefined;
|
|
40
|
+
return { result, description: `${currentPath} === undefined` };
|
|
41
|
+
}
|
|
42
|
+
if (!shouldBeArray) {
|
|
43
|
+
const result = value instanceof RegExp ? value.test(actualValue) : value === actualValue;
|
|
44
|
+
return { result, description: `${currentPath} === "${actualValue}"` };
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (shouldBeArray) {
|
|
48
|
+
if (Number.isInteger(arrayIndex)) {
|
|
49
|
+
return matchObject(actualValue[Number(arrayIndex)], rest.join('.'), value, currentPath);
|
|
50
|
+
}
|
|
51
|
+
const hasArrayMatch = actualValue.some((arrayValue) => matchObject(arrayValue, rest.join('.'), value, currentPath).result);
|
|
52
|
+
return { result: hasArrayMatch, description: `array match ${currentPath}` };
|
|
53
|
+
}
|
|
54
|
+
if (typeof actualValue !== 'object') {
|
|
55
|
+
return { result: false, description: `${currentPath} not an object` };
|
|
56
|
+
}
|
|
57
|
+
return matchObject(actualValue, rest.join('.'), value, currentPath);
|
|
58
|
+
};
|
|
59
|
+
const ___matchesValue = (matcher, value) => {
|
|
60
|
+
if (matcher === undefined) {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
if (typeof matcher !== 'string' && !(matcher instanceof RegExp) && typeof matcher !== 'number') {
|
|
64
|
+
throw new Error('invalid matcher');
|
|
65
|
+
}
|
|
66
|
+
if (typeof matcher === 'string' && matcher !== value) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
if (matcher instanceof RegExp && (typeof value !== 'string' || !matcher.test(value))) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
if (typeof matcher === 'number' && (typeof value !== 'number' || matcher !== value)) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
return true;
|
|
76
|
+
};
|
|
77
|
+
if (!___matchesValue(localMatchBuilderVariables.filter, req.url)) {
|
|
78
|
+
return {
|
|
79
|
+
result: false,
|
|
80
|
+
description: `url ${req.url} doesn't match ${(_b = localMatchBuilderVariables.filter) === null || _b === void 0 ? void 0 : _b.toString()}`,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
if (!___matchesValue(localMatchBuilderVariables.hostname, ___url.hostname)) {
|
|
84
|
+
return {
|
|
85
|
+
result: false,
|
|
86
|
+
description: `hostname ${___url.hostname} doesn't match ${(_c = localMatchBuilderVariables.hostname) === null || _c === void 0 ? void 0 : _c.toString()}`,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
if (!___matchesValue(localMatchBuilderVariables.pathname, ___url.pathname)) {
|
|
90
|
+
return {
|
|
91
|
+
result: false,
|
|
92
|
+
description: `pathname ${___url.pathname} doesn't match ${(_d = localMatchBuilderVariables.pathname) === null || _d === void 0 ? void 0 : _d.toString()}`,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
if (localMatchBuilderVariables.port) {
|
|
96
|
+
const port = ___url.port && ___url.port !== '' ? ___url.port : ___url.protocol === 'https:' ? '443' : '80';
|
|
97
|
+
if (!___matchesValue(localMatchBuilderVariables.port instanceof RegExp
|
|
98
|
+
? localMatchBuilderVariables.port
|
|
99
|
+
: `${localMatchBuilderVariables.port}`, port)) {
|
|
100
|
+
return {
|
|
101
|
+
result: false,
|
|
102
|
+
description: `port ${port} doesn't match ${(_e = localMatchBuilderVariables.port) === null || _e === void 0 ? void 0 : _e.toString()}`,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (localMatchBuilderVariables.searchParams) {
|
|
107
|
+
for (const searchParamMatcher of localMatchBuilderVariables.searchParams) {
|
|
108
|
+
if (typeof searchParamMatcher === 'string') {
|
|
109
|
+
const result = ___url.searchParams.has(searchParamMatcher);
|
|
110
|
+
if (!result) {
|
|
111
|
+
return { result, description: `searchParams.has("${searchParamMatcher}")` };
|
|
112
|
+
}
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (searchParamMatcher instanceof RegExp) {
|
|
116
|
+
const result = Array.from(___url.searchParams.keys()).some((key) => searchParamMatcher.test(key));
|
|
117
|
+
if (!result) {
|
|
118
|
+
return { result, description: `searchParams.keys() matches ${searchParamMatcher.toString()}` };
|
|
119
|
+
}
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
if (!___url.searchParams.has(searchParamMatcher.key)) {
|
|
123
|
+
return { result: false, description: `searchParams.has("${searchParamMatcher.key}")` };
|
|
124
|
+
}
|
|
125
|
+
if (searchParamMatcher.value) {
|
|
126
|
+
const value = ___url.searchParams.get(searchParamMatcher.key);
|
|
127
|
+
if (!___matchesValue(searchParamMatcher.value, value)) {
|
|
128
|
+
return {
|
|
129
|
+
result: false,
|
|
130
|
+
description: `searchParams.get("${searchParamMatcher.key}") = "${searchParamMatcher.value}"`,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (localMatchBuilderVariables.headers) {
|
|
137
|
+
for (const headerMatcher of localMatchBuilderVariables.headers) {
|
|
138
|
+
if (typeof headerMatcher === 'string') {
|
|
139
|
+
const result = ___headers.has(headerMatcher);
|
|
140
|
+
if (result) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
return { result: false, description: `headers.has("${headerMatcher}")` };
|
|
144
|
+
}
|
|
145
|
+
if (headerMatcher instanceof RegExp) {
|
|
146
|
+
const result = ___headers.toHeaderPairs().some(([key]) => headerMatcher.test(key));
|
|
147
|
+
if (result) {
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
return { result: false, description: `headers.keys matches ${headerMatcher.toString()}` };
|
|
151
|
+
}
|
|
152
|
+
if (!___headers.has(headerMatcher.key)) {
|
|
153
|
+
return { result: false, description: `headers.has("${headerMatcher.key}")` };
|
|
154
|
+
}
|
|
155
|
+
if (headerMatcher.value) {
|
|
156
|
+
const value = ___headers.get(headerMatcher.key);
|
|
157
|
+
if (!___matchesValue(headerMatcher.value, value)) {
|
|
158
|
+
return {
|
|
159
|
+
result: false,
|
|
160
|
+
description: `headerMatcher.get("${headerMatcher.key}") = "${headerMatcher.value}"`,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (localMatchBuilderVariables.bodyText === null && !!req.body) {
|
|
167
|
+
return { result: false, description: `empty body` };
|
|
168
|
+
}
|
|
169
|
+
if (localMatchBuilderVariables.bodyText) {
|
|
170
|
+
if (!req.body) {
|
|
171
|
+
return { result: false, description: `empty body` };
|
|
172
|
+
}
|
|
173
|
+
if (localMatchBuilderVariables.bodyText instanceof RegExp) {
|
|
174
|
+
if (!___matchesValue(localMatchBuilderVariables.bodyText, req.body)) {
|
|
175
|
+
return {
|
|
176
|
+
result: false,
|
|
177
|
+
description: `body text doesn't match ${localMatchBuilderVariables.bodyText.toString()}`,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
else if (!req.body.includes(localMatchBuilderVariables.bodyText)) {
|
|
182
|
+
return {
|
|
183
|
+
result: false,
|
|
184
|
+
description: `body text doesn't include "${localMatchBuilderVariables.bodyText}"`,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (localMatchBuilderVariables.bodyJson) {
|
|
189
|
+
let json;
|
|
190
|
+
try {
|
|
191
|
+
json = JSON.parse(req.body);
|
|
192
|
+
}
|
|
193
|
+
catch (kiss) {
|
|
194
|
+
return { result: false, description: `unparseable json` };
|
|
195
|
+
}
|
|
196
|
+
if (!json) {
|
|
197
|
+
return { result: false, description: `empty json object` };
|
|
198
|
+
}
|
|
199
|
+
for (const jsonMatcher of Array.isArray(localMatchBuilderVariables.bodyJson)
|
|
200
|
+
? localMatchBuilderVariables.bodyJson
|
|
201
|
+
: [localMatchBuilderVariables.bodyJson]) {
|
|
202
|
+
const matchObjectResult = matchObject(json, jsonMatcher.key, jsonMatcher.value);
|
|
203
|
+
if (!matchObjectResult.result) {
|
|
204
|
+
return { result: false, description: `$.${jsonMatcher.key} != "${jsonMatcher.value}"` };
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
if (localMatchBuilderVariables.bodyGql) {
|
|
209
|
+
if (!req.gqlBody) {
|
|
210
|
+
return { result: false, description: `not a gql body` };
|
|
211
|
+
}
|
|
212
|
+
if (!___matchesValue(localMatchBuilderVariables.bodyGql.methodName, req.gqlBody.methodName)) {
|
|
213
|
+
return {
|
|
214
|
+
result: false,
|
|
215
|
+
description: `methodName "${localMatchBuilderVariables.bodyGql.methodName}" !== "${req.gqlBody.methodName}"`,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
if (!___matchesValue(localMatchBuilderVariables.bodyGql.operationName, req.gqlBody.operationName)) {
|
|
219
|
+
return {
|
|
220
|
+
result: false,
|
|
221
|
+
description: `operationName "${localMatchBuilderVariables.bodyGql.operationName}" !== "${req.gqlBody.operationName}"`,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
if (!___matchesValue(localMatchBuilderVariables.bodyGql.query, req.gqlBody.query)) {
|
|
225
|
+
return {
|
|
226
|
+
result: false,
|
|
227
|
+
description: `query "${localMatchBuilderVariables.bodyGql.query}" !== "${req.gqlBody.query}"`,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
if (!___matchesValue(localMatchBuilderVariables.bodyGql.type, req.gqlBody.type)) {
|
|
231
|
+
return {
|
|
232
|
+
result: false,
|
|
233
|
+
description: `type "${localMatchBuilderVariables.bodyGql.type}" !== "${req.gqlBody.type}"`,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
if (localMatchBuilderVariables.bodyGql.variables) {
|
|
237
|
+
for (const jsonMatcher of Array.isArray(localMatchBuilderVariables.bodyGql.variables)
|
|
238
|
+
? localMatchBuilderVariables.bodyGql.variables
|
|
239
|
+
: [localMatchBuilderVariables.bodyGql.variables]) {
|
|
240
|
+
const matchObjectResult = matchObject(req.gqlBody.variables, jsonMatcher.key, jsonMatcher.value);
|
|
241
|
+
if (!matchObjectResult.result) {
|
|
242
|
+
return {
|
|
243
|
+
result: false,
|
|
244
|
+
description: `GQL variable ${jsonMatcher.key} != "${jsonMatcher.value}". Detail: ${matchObjectResult.description}`,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return { result: true, description: 'match' };
|
|
251
|
+
}
|
|
7
252
|
class RuleBuilderBaseBase {
|
|
8
253
|
constructor(rule, _matchBuilderVariables) {
|
|
9
254
|
this._matchBuilderVariables = _matchBuilderVariables || {};
|
|
@@ -15,252 +260,7 @@ class RuleBuilderBaseBase {
|
|
|
15
260
|
mockResponse: { status: 200 },
|
|
16
261
|
},
|
|
17
262
|
matches: {
|
|
18
|
-
localFn:
|
|
19
|
-
var _a, _b, _c, _d;
|
|
20
|
-
const ___url = new URL(req.url);
|
|
21
|
-
const ___headers = req.rawHeaders;
|
|
22
|
-
const arrayIndexerRegex = /\[(?<arrayIndex>[0-9]*)\]/i;
|
|
23
|
-
const matchObject = (obj, path, value, parentPath) => {
|
|
24
|
-
var _a, _b, _c, _d;
|
|
25
|
-
if (!obj) {
|
|
26
|
-
return { result: false, description: `${parentPath} is falsey` };
|
|
27
|
-
}
|
|
28
|
-
const [rawKey, ...rest] = path.split('.');
|
|
29
|
-
const key = (rawKey !== null && rawKey !== void 0 ? rawKey : '').replace(arrayIndexerRegex, '');
|
|
30
|
-
const shouldBeArray = rawKey ? arrayIndexerRegex.test(rawKey) : false;
|
|
31
|
-
const arrayIndex = rawKey && (((_b = (_a = arrayIndexerRegex.exec(rawKey)) === null || _a === void 0 ? void 0 : _a.groups) === null || _b === void 0 ? void 0 : _b.arrayIndex) || '').length > 0
|
|
32
|
-
? Number((_d = (_c = arrayIndexerRegex.exec(rawKey)) === null || _c === void 0 ? void 0 : _c.groups) === null || _d === void 0 ? void 0 : _d.arrayIndex)
|
|
33
|
-
: Number.NaN;
|
|
34
|
-
const actualValue = key ? obj[key] : obj;
|
|
35
|
-
const currentPath = `${parentPath ? `${parentPath}.` : ''}${rawKey}`;
|
|
36
|
-
if (value === undefined && actualValue === undefined) {
|
|
37
|
-
return { result: false, description: `${currentPath}=undefined` };
|
|
38
|
-
}
|
|
39
|
-
if (rest.length === 0) {
|
|
40
|
-
if (shouldBeArray &&
|
|
41
|
-
(!Array.isArray(actualValue) ||
|
|
42
|
-
(Number.isInteger(arrayIndex) && actualValue.length <= Number(arrayIndex)))) {
|
|
43
|
-
return { result: false, description: `${currentPath} empty array` };
|
|
44
|
-
}
|
|
45
|
-
if (value === undefined) {
|
|
46
|
-
const result = shouldBeArray
|
|
47
|
-
? !Number.isInteger(arrayIndex) || actualValue.length >= Number(arrayIndex)
|
|
48
|
-
: actualValue !== undefined;
|
|
49
|
-
return { result, description: `${currentPath} === undefined` };
|
|
50
|
-
}
|
|
51
|
-
if (!shouldBeArray) {
|
|
52
|
-
const result = value instanceof RegExp ? value.test(actualValue) : value === actualValue;
|
|
53
|
-
return { result, description: `${currentPath} === "${actualValue}"` };
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
if (shouldBeArray) {
|
|
57
|
-
if (Number.isInteger(arrayIndex)) {
|
|
58
|
-
return matchObject(actualValue[Number(arrayIndex)], rest.join('.'), value, currentPath);
|
|
59
|
-
}
|
|
60
|
-
const hasArrayMatch = actualValue.some((arrayValue) => matchObject(arrayValue, rest.join('.'), value, currentPath).result);
|
|
61
|
-
return { result: hasArrayMatch, description: `array match ${currentPath}` };
|
|
62
|
-
}
|
|
63
|
-
if (typeof actualValue !== 'object') {
|
|
64
|
-
return { result: false, description: `${currentPath} not an object` };
|
|
65
|
-
}
|
|
66
|
-
return matchObject(actualValue, rest.join('.'), value, currentPath);
|
|
67
|
-
};
|
|
68
|
-
const ___matchesValue = (matcher, value) => {
|
|
69
|
-
if (matcher === undefined) {
|
|
70
|
-
return true;
|
|
71
|
-
}
|
|
72
|
-
if (typeof matcher !== 'string' && !(matcher instanceof RegExp) && typeof matcher !== 'number') {
|
|
73
|
-
throw new Error('invalid matcher');
|
|
74
|
-
}
|
|
75
|
-
if (typeof matcher === 'string' && matcher !== value) {
|
|
76
|
-
return false;
|
|
77
|
-
}
|
|
78
|
-
if (matcher instanceof RegExp && (typeof value !== 'string' || !matcher.test(value))) {
|
|
79
|
-
return false;
|
|
80
|
-
}
|
|
81
|
-
if (typeof matcher === 'number' && (typeof value !== 'number' || matcher !== value)) {
|
|
82
|
-
return false;
|
|
83
|
-
}
|
|
84
|
-
return true;
|
|
85
|
-
};
|
|
86
|
-
if (!___matchesValue(matchBuilderVariables.filter, req.url)) {
|
|
87
|
-
return {
|
|
88
|
-
result: false,
|
|
89
|
-
description: `url ${req.url} doesn't match ${(_a = matchBuilderVariables.filter) === null || _a === void 0 ? void 0 : _a.toString()}`,
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
if (!___matchesValue(matchBuilderVariables.hostname, ___url.hostname)) {
|
|
93
|
-
return {
|
|
94
|
-
result: false,
|
|
95
|
-
description: `hostname ${___url.hostname} doesn't match ${(_b = matchBuilderVariables.hostname) === null || _b === void 0 ? void 0 : _b.toString()}`,
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
if (!___matchesValue(matchBuilderVariables.pathname, ___url.pathname)) {
|
|
99
|
-
return {
|
|
100
|
-
result: false,
|
|
101
|
-
description: `pathname ${___url.pathname} doesn't match ${(_c = matchBuilderVariables.pathname) === null || _c === void 0 ? void 0 : _c.toString()}`,
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
if (matchBuilderVariables.port) {
|
|
105
|
-
const port = ___url.port && ___url.port !== '' ? ___url.port : ___url.protocol === 'https:' ? '443' : '80';
|
|
106
|
-
if (!___matchesValue(matchBuilderVariables.port instanceof RegExp
|
|
107
|
-
? matchBuilderVariables.port
|
|
108
|
-
: `${matchBuilderVariables.port}`, port)) {
|
|
109
|
-
return {
|
|
110
|
-
result: false,
|
|
111
|
-
description: `port ${port} doesn't match ${(_d = matchBuilderVariables.port) === null || _d === void 0 ? void 0 : _d.toString()}`,
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
if (matchBuilderVariables.searchParams) {
|
|
116
|
-
for (const searchParamMatcher of matchBuilderVariables.searchParams) {
|
|
117
|
-
if (typeof searchParamMatcher === 'string') {
|
|
118
|
-
const result = ___url.searchParams.has(searchParamMatcher);
|
|
119
|
-
return { result, description: `searchParams.has("${searchParamMatcher}")` };
|
|
120
|
-
}
|
|
121
|
-
if (searchParamMatcher instanceof RegExp) {
|
|
122
|
-
const result = Array.from(___url.searchParams.keys()).some((key) => searchParamMatcher.test(key));
|
|
123
|
-
return { result, description: `searchParams.keys() matches ${searchParamMatcher.toString()}` };
|
|
124
|
-
}
|
|
125
|
-
if (!___url.searchParams.has(searchParamMatcher.key)) {
|
|
126
|
-
return { result: false, description: `searchParams.has("${searchParamMatcher.key}")` };
|
|
127
|
-
}
|
|
128
|
-
if (searchParamMatcher.value) {
|
|
129
|
-
const value = ___url.searchParams.get(searchParamMatcher.key);
|
|
130
|
-
if (value === null) {
|
|
131
|
-
return {
|
|
132
|
-
result: false,
|
|
133
|
-
description: `searchParams.get("${searchParamMatcher.key}") === null`,
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
if (!___matchesValue(searchParamMatcher.value, value)) {
|
|
137
|
-
return {
|
|
138
|
-
result: false,
|
|
139
|
-
description: `searchParams.get("${searchParamMatcher.key}") = "${searchParamMatcher.value}"`,
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
if (matchBuilderVariables.headers) {
|
|
146
|
-
for (const headerMatcher of matchBuilderVariables.headers) {
|
|
147
|
-
if (typeof headerMatcher === 'string') {
|
|
148
|
-
const result = ___headers.has(headerMatcher);
|
|
149
|
-
if (result) {
|
|
150
|
-
continue;
|
|
151
|
-
}
|
|
152
|
-
return { result: false, description: `headers.has("${headerMatcher}")` };
|
|
153
|
-
}
|
|
154
|
-
if (headerMatcher instanceof RegExp) {
|
|
155
|
-
const result = ___headers.toHeaderPairs().some(([key]) => headerMatcher.test(key));
|
|
156
|
-
if (result) {
|
|
157
|
-
continue;
|
|
158
|
-
}
|
|
159
|
-
return { result: false, description: `headers.keys matches ${headerMatcher.toString()}` };
|
|
160
|
-
}
|
|
161
|
-
if (!___headers.has(headerMatcher.key)) {
|
|
162
|
-
return { result: false, description: `headers.has("${headerMatcher.key}")` };
|
|
163
|
-
}
|
|
164
|
-
if (headerMatcher.value) {
|
|
165
|
-
const value = ___headers.get(headerMatcher.key);
|
|
166
|
-
if (value === null) {
|
|
167
|
-
return { result: false, description: `headers.get("${headerMatcher.key}") === null` };
|
|
168
|
-
}
|
|
169
|
-
if (!___matchesValue(headerMatcher.value, value)) {
|
|
170
|
-
return {
|
|
171
|
-
result: false,
|
|
172
|
-
description: `headerMatcher.get("${headerMatcher.key}") = "${headerMatcher.value}"`,
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
if (matchBuilderVariables.bodyText === null && !!req.body) {
|
|
179
|
-
return { result: false, description: `empty body` };
|
|
180
|
-
}
|
|
181
|
-
if (matchBuilderVariables.bodyText) {
|
|
182
|
-
if (!req.body) {
|
|
183
|
-
return { result: false, description: `empty body` };
|
|
184
|
-
}
|
|
185
|
-
if (matchBuilderVariables.bodyText instanceof RegExp) {
|
|
186
|
-
if (!___matchesValue(matchBuilderVariables.bodyText, req.body)) {
|
|
187
|
-
return {
|
|
188
|
-
result: false,
|
|
189
|
-
description: `body text doesn't match ${matchBuilderVariables.bodyText.toString()}`,
|
|
190
|
-
};
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
else if (!req.body.includes(matchBuilderVariables.bodyText)) {
|
|
194
|
-
return {
|
|
195
|
-
result: false,
|
|
196
|
-
description: `body text doesn't include "${matchBuilderVariables.bodyText}"`,
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
if (matchBuilderVariables.bodyJson) {
|
|
201
|
-
let json;
|
|
202
|
-
try {
|
|
203
|
-
json = JSON.parse(req.body);
|
|
204
|
-
}
|
|
205
|
-
catch (kiss) {
|
|
206
|
-
return { result: false, description: `unparseable json` };
|
|
207
|
-
}
|
|
208
|
-
if (!json) {
|
|
209
|
-
return { result: false, description: `empty json object` };
|
|
210
|
-
}
|
|
211
|
-
for (const jsonMatcher of Array.isArray(matchBuilderVariables.bodyJson)
|
|
212
|
-
? matchBuilderVariables.bodyJson
|
|
213
|
-
: [matchBuilderVariables.bodyJson]) {
|
|
214
|
-
const matchObjectResult = matchObject(json, jsonMatcher.key, jsonMatcher.value);
|
|
215
|
-
if (!matchObjectResult.result) {
|
|
216
|
-
return { result: false, description: `$.${jsonMatcher.key} != "${jsonMatcher.value}"` };
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
if (matchBuilderVariables.bodyGql) {
|
|
221
|
-
if (!req.gqlBody) {
|
|
222
|
-
return { result: false, description: `not a gql body` };
|
|
223
|
-
}
|
|
224
|
-
if (!___matchesValue(matchBuilderVariables.bodyGql.methodName, req.gqlBody.methodName)) {
|
|
225
|
-
return {
|
|
226
|
-
result: false,
|
|
227
|
-
description: `methodName "${matchBuilderVariables.bodyGql.methodName}" !== "${req.gqlBody.methodName}"`,
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
if (!___matchesValue(matchBuilderVariables.bodyGql.operationName, req.gqlBody.operationName)) {
|
|
231
|
-
return {
|
|
232
|
-
result: false,
|
|
233
|
-
description: `operationName "${matchBuilderVariables.bodyGql.operationName}" !== "${req.gqlBody.operationName}"`,
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
if (!___matchesValue(matchBuilderVariables.bodyGql.query, req.gqlBody.query)) {
|
|
237
|
-
return {
|
|
238
|
-
result: false,
|
|
239
|
-
description: `query "${matchBuilderVariables.bodyGql.query}" !== "${req.gqlBody.query}"`,
|
|
240
|
-
};
|
|
241
|
-
}
|
|
242
|
-
if (!___matchesValue(matchBuilderVariables.bodyGql.type, req.gqlBody.type)) {
|
|
243
|
-
return {
|
|
244
|
-
result: false,
|
|
245
|
-
description: `type "${matchBuilderVariables.bodyGql.type}" !== "${req.gqlBody.type}"`,
|
|
246
|
-
};
|
|
247
|
-
}
|
|
248
|
-
if (matchBuilderVariables.bodyGql.variables) {
|
|
249
|
-
for (const jsonMatcher of Array.isArray(matchBuilderVariables.bodyGql.variables)
|
|
250
|
-
? matchBuilderVariables.bodyGql.variables
|
|
251
|
-
: [matchBuilderVariables.bodyGql.variables]) {
|
|
252
|
-
const matchObjectResult = matchObject(req.gqlBody.variables, jsonMatcher.key, jsonMatcher.value);
|
|
253
|
-
if (!matchObjectResult.result) {
|
|
254
|
-
return {
|
|
255
|
-
result: false,
|
|
256
|
-
description: `GQL variable ${jsonMatcher.key} != "${jsonMatcher.value}". Detail: ${matchObjectResult.description}`,
|
|
257
|
-
};
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
return { result: true, description: 'match' };
|
|
263
|
-
},
|
|
263
|
+
localFn: matchFunction,
|
|
264
264
|
localVariables: { matchBuilderVariables: this._matchBuilderVariables },
|
|
265
265
|
},
|
|
266
266
|
};
|
|
@@ -342,7 +342,23 @@ class RuleBuilder extends RuleBuilderBase {
|
|
|
342
342
|
return new RuleBuilderInitialized(this.rule, this._matchBuilderVariables);
|
|
343
343
|
}
|
|
344
344
|
}
|
|
345
|
-
class
|
|
345
|
+
class RuleBuilderRequestInitialized extends RuleBuilderBase {
|
|
346
|
+
modifyResponse(modifyFunction, localVariables) {
|
|
347
|
+
if (!this.rule.actions) {
|
|
348
|
+
throw new Error('rule.actions not defined - builder implementation error');
|
|
349
|
+
}
|
|
350
|
+
if (typeof modifyFunction === 'function') {
|
|
351
|
+
this.rule.actions.modifyResponse = { localFn: modifyFunction, localVariables: localVariables !== null && localVariables !== void 0 ? localVariables : {} };
|
|
352
|
+
return this.rule;
|
|
353
|
+
}
|
|
354
|
+
if (localVariables) {
|
|
355
|
+
throw new Error('invalid call - localVariables cannot be used together with Response or RemotableFunction');
|
|
356
|
+
}
|
|
357
|
+
this.rule.actions.modifyResponse = modifyFunction;
|
|
358
|
+
return this.rule;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
class RuleBuilderInitialized extends RuleBuilderRequestInitialized {
|
|
346
362
|
withHostname(hostname) {
|
|
347
363
|
if (this._matchBuilderVariables.hostname) {
|
|
348
364
|
throw new Error('hostname already set');
|
|
@@ -502,32 +518,11 @@ class RuleBuilderInitialized extends RuleBuilderBase {
|
|
|
502
518
|
this.rule.actions = { modifyRequest: modifyFunction };
|
|
503
519
|
return new RuleBuilderRequestInitialized(this.rule, this._matchBuilderVariables);
|
|
504
520
|
}
|
|
505
|
-
modifyResponse(modifyFunction, localVariables) {
|
|
506
|
-
if (typeof modifyFunction === 'function') {
|
|
507
|
-
this.rule.actions = { modifyResponse: { localFn: modifyFunction, localVariables: localVariables !== null && localVariables !== void 0 ? localVariables : {} } };
|
|
508
|
-
return this.rule;
|
|
509
|
-
}
|
|
510
|
-
if (localVariables) {
|
|
511
|
-
throw new Error('invalid call - localVariables cannot be used together with Response or RemotableFunction');
|
|
512
|
-
}
|
|
513
|
-
this.rule.actions = { modifyResponse: modifyFunction };
|
|
514
|
-
return this.rule;
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
class RuleBuilderRequestInitialized extends RuleBuilderBase {
|
|
518
521
|
modifyResponse(modifyFunction, localVariables) {
|
|
519
522
|
if (!this.rule.actions) {
|
|
520
|
-
|
|
521
|
-
}
|
|
522
|
-
if (typeof modifyFunction === 'function') {
|
|
523
|
-
this.rule.actions = { modifyResponse: { localFn: modifyFunction, localVariables: localVariables !== null && localVariables !== void 0 ? localVariables : {} } };
|
|
524
|
-
return this.rule;
|
|
523
|
+
this.rule.actions = { proxyPass: true };
|
|
525
524
|
}
|
|
526
|
-
|
|
527
|
-
throw new Error('invalid call - localVariables cannot be used together with Response or RemotableFunction');
|
|
528
|
-
}
|
|
529
|
-
this.rule.actions.modifyResponse = modifyFunction;
|
|
530
|
-
return this.rule;
|
|
525
|
+
return super.modifyResponse(modifyFunction, localVariables);
|
|
531
526
|
}
|
|
532
527
|
}
|
|
533
528
|
const ruleBuilder = () => new RuleBuilder();
|