@api-client/core 0.5.10 → 0.5.11
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/build/browser.d.ts +2 -0
- package/build/browser.js +2 -0
- package/build/browser.js.map +1 -1
- package/build/index.d.ts +2 -0
- package/build/index.js +2 -0
- package/build/index.js.map +1 -1
- package/build/src/lib/calculators/DataCalculator.d.ts +3 -3
- package/build/src/lib/calculators/DataCalculator.js +3 -3
- package/build/src/lib/calculators/DataCalculator.js.map +1 -1
- package/build/src/lib/events/Utils.d.ts +1 -0
- package/build/src/lib/events/Utils.js +6 -0
- package/build/src/lib/events/Utils.js.map +1 -0
- package/build/src/lib/parsers/UriTemplate.d.ts +94 -0
- package/build/src/lib/parsers/UriTemplate.js +419 -0
- package/build/src/lib/parsers/UriTemplate.js.map +1 -0
- package/build/src/lib/parsers/UrlEncoder.d.ts +5 -0
- package/build/src/lib/parsers/UrlEncoder.js +49 -0
- package/build/src/lib/parsers/UrlEncoder.js.map +1 -1
- package/build/src/models/HttpProject.d.ts +4 -1
- package/build/src/models/HttpProject.js +9 -6
- package/build/src/models/HttpProject.js.map +1 -1
- package/build/src/models/ProjectParent.d.ts +9 -0
- package/build/src/models/ProjectParent.js +25 -0
- package/build/src/models/ProjectParent.js.map +1 -1
- package/build/src/models/Property.d.ts +8 -0
- package/build/src/models/Property.js +17 -0
- package/build/src/models/Property.js.map +1 -1
- package/build/src/models/Server.d.ts +14 -1
- package/build/src/models/Server.js +31 -1
- package/build/src/models/Server.js.map +1 -1
- package/build/src/runtime/actions/runnable/DeleteCookieRunnable.d.ts +1 -1
- package/build/src/runtime/actions/runnable/SetCookieRunnable.d.ts +1 -1
- package/build/src/runtime/actions/runnable/SetVariableRunnable.d.ts +1 -1
- package/build/src/runtime/http-engine/CoreEngine.d.ts +1 -1
- package/build/src/runtime/store/FilesSdk.d.ts +8 -0
- package/build/src/runtime/store/FilesSdk.js +15 -0
- package/build/src/runtime/store/FilesSdk.js.map +1 -1
- package/package.json +1 -1
- package/src/lib/calculators/DataCalculator.ts +3 -3
- package/src/lib/events/Utils.ts +5 -0
- package/src/lib/parsers/UriTemplate.ts +494 -0
- package/src/lib/parsers/UrlEncoder.ts +51 -0
- package/src/models/HttpProject.ts +10 -7
- package/src/models/ProjectParent.ts +27 -0
- package/src/models/Property.ts +18 -0
- package/src/models/Server.ts +32 -1
- package/src/runtime/actions/runnable/DeleteCookieRunnable.ts +1 -1
- package/src/runtime/actions/runnable/SetCookieRunnable.ts +1 -1
- package/src/runtime/actions/runnable/SetVariableRunnable.ts +1 -1
- package/src/runtime/http-engine/CoreEngine.ts +1 -1
- package/src/runtime/store/FilesSdk.ts +16 -0
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
import { UrlEncoder } from './UrlEncoder.js';
|
|
2
|
+
|
|
3
|
+
const cache = new Map<string, UriTemplate>();
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* pattern to identify expressions [operator, variable-list] in template
|
|
7
|
+
*/
|
|
8
|
+
const EXPRESSION_PATTERN = /\{([^a-zA-Z0-9%_]?)([^}]+)(\}|$)/g;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* pattern to identify variables [name, explode, maxlength] in variable-list
|
|
12
|
+
*/
|
|
13
|
+
const VARIABLE_PATTERN = /^([^*:.](?:\.?[^*:.])*)((\*)|:(\d+))?$/;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* pattern to verify variable name integrity
|
|
17
|
+
*/
|
|
18
|
+
const VARIABLE_NAME_PATTERN = /[^a-zA-Z0-9%_.]/;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* pattern to verify literal integrity
|
|
22
|
+
*/
|
|
23
|
+
const LITERAL_PATTERN = /[<>{}"`^| \\]/;
|
|
24
|
+
|
|
25
|
+
enum DataType {
|
|
26
|
+
// undefined/null
|
|
27
|
+
nil,
|
|
28
|
+
// string
|
|
29
|
+
string,
|
|
30
|
+
// object
|
|
31
|
+
object,
|
|
32
|
+
// array
|
|
33
|
+
array,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface IData {
|
|
37
|
+
/**
|
|
38
|
+
* type of data 0: undefined/null, 1: string, 2: object, 3: array
|
|
39
|
+
*/
|
|
40
|
+
type: DataType;
|
|
41
|
+
/**
|
|
42
|
+
* original values (except undefined/null)
|
|
43
|
+
*/
|
|
44
|
+
val: (string | undefined)[][];
|
|
45
|
+
/**
|
|
46
|
+
* cache for encoded values (only for non-maxlength expansion)
|
|
47
|
+
*/
|
|
48
|
+
strictEncode: (string | undefined)[][];
|
|
49
|
+
encodeReserved: (string | undefined)[][]
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
class Data {
|
|
53
|
+
cache: Record<string, IData>;
|
|
54
|
+
constructor(public data: Record<string, any>) {
|
|
55
|
+
this.cache = {};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
get(key: string): IData {
|
|
59
|
+
const { data } = this;
|
|
60
|
+
const d: IData = {
|
|
61
|
+
type: DataType.nil,
|
|
62
|
+
val: [],
|
|
63
|
+
strictEncode: [],
|
|
64
|
+
encodeReserved: [],
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
if (this.cache[key] !== undefined) {
|
|
68
|
+
// we've already processed this key
|
|
69
|
+
return this.cache[key];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
this.cache[key] = d;
|
|
73
|
+
|
|
74
|
+
let value: any;
|
|
75
|
+
if (typeof data === 'function') {
|
|
76
|
+
value = data(key);
|
|
77
|
+
} else if (typeof data[key] === 'function') {
|
|
78
|
+
value = data[key](key);
|
|
79
|
+
} else {
|
|
80
|
+
value = data[key];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// generalize input into [ [name1, value1], [name2, value2], … ]
|
|
84
|
+
// so expansion has to deal with a single data structure only
|
|
85
|
+
if (value === undefined || value === null) {
|
|
86
|
+
// undefined and null values are to be ignored completely
|
|
87
|
+
return d;
|
|
88
|
+
} else if (Array.isArray(value)) {
|
|
89
|
+
value.forEach((v) => {
|
|
90
|
+
if (v !== undefined && v !== null) {
|
|
91
|
+
// arrays don't have names
|
|
92
|
+
d.val.push([undefined, v])
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
if (d.val.length) {
|
|
96
|
+
d.type = DataType.array;
|
|
97
|
+
}
|
|
98
|
+
} else if (String(Object.prototype.toString.call(value)) === '[object Object]') {
|
|
99
|
+
Object.keys(value).forEach((k) => {
|
|
100
|
+
const v = value[k];
|
|
101
|
+
if (v !== undefined && v !== null) {
|
|
102
|
+
d.val.push([k, v])
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
if (d.val.length) {
|
|
106
|
+
d.type = DataType.object;
|
|
107
|
+
}
|
|
108
|
+
} else {
|
|
109
|
+
d.type = DataType.string;
|
|
110
|
+
d.val.push([undefined, String(value)]);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return d;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
interface IOperator {
|
|
118
|
+
prefix: string,
|
|
119
|
+
separator: string;
|
|
120
|
+
named: boolean;
|
|
121
|
+
empty_name_separator: boolean;
|
|
122
|
+
encode: 'strictEncode' | 'encodeReserved';
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
interface IVariable {
|
|
126
|
+
name: string;
|
|
127
|
+
explode: boolean;
|
|
128
|
+
maxlength?: number;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
interface IPart {
|
|
132
|
+
expression: string;
|
|
133
|
+
operator: string;
|
|
134
|
+
variables: IVariable[];
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export interface IUriTemplateOptions {
|
|
138
|
+
/**
|
|
139
|
+
* Throws when a variable is not found to replaces a value in the template.
|
|
140
|
+
*/
|
|
141
|
+
strict?: boolean;
|
|
142
|
+
/**
|
|
143
|
+
* When set it ignores replacing the value in the template when the variable is missing.
|
|
144
|
+
*/
|
|
145
|
+
ignoreMissing?: boolean;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const operators: Record<string, IOperator> = {
|
|
149
|
+
// Simple string expansion
|
|
150
|
+
'': {
|
|
151
|
+
prefix: '',
|
|
152
|
+
separator: ',',
|
|
153
|
+
named: false,
|
|
154
|
+
empty_name_separator: false,
|
|
155
|
+
encode: 'strictEncode'
|
|
156
|
+
},
|
|
157
|
+
// Reserved character strings
|
|
158
|
+
'+': {
|
|
159
|
+
prefix: '',
|
|
160
|
+
separator: ',',
|
|
161
|
+
named: false,
|
|
162
|
+
empty_name_separator: false,
|
|
163
|
+
encode: 'encodeReserved'
|
|
164
|
+
},
|
|
165
|
+
// Fragment identifiers prefixed by '#'
|
|
166
|
+
'#': {
|
|
167
|
+
prefix: '#',
|
|
168
|
+
separator: ',',
|
|
169
|
+
named: false,
|
|
170
|
+
empty_name_separator: false,
|
|
171
|
+
encode: 'encodeReserved'
|
|
172
|
+
},
|
|
173
|
+
// Name labels or extensions prefixed by '.'
|
|
174
|
+
'.': {
|
|
175
|
+
prefix: '.',
|
|
176
|
+
separator: '.',
|
|
177
|
+
named: false,
|
|
178
|
+
empty_name_separator: false,
|
|
179
|
+
encode: 'strictEncode'
|
|
180
|
+
},
|
|
181
|
+
// Path segments prefixed by '/'
|
|
182
|
+
'/': {
|
|
183
|
+
prefix: '/',
|
|
184
|
+
separator: '/',
|
|
185
|
+
named: false,
|
|
186
|
+
empty_name_separator: false,
|
|
187
|
+
encode: 'strictEncode'
|
|
188
|
+
},
|
|
189
|
+
// Path parameter name or name=value pairs prefixed by ';'
|
|
190
|
+
';': {
|
|
191
|
+
prefix: ';',
|
|
192
|
+
separator: ';',
|
|
193
|
+
named: true,
|
|
194
|
+
empty_name_separator: false,
|
|
195
|
+
encode: 'strictEncode'
|
|
196
|
+
},
|
|
197
|
+
// Query component beginning with '?' and consisting
|
|
198
|
+
// of name=value pairs separated by '&'; an
|
|
199
|
+
'?': {
|
|
200
|
+
prefix: '?',
|
|
201
|
+
separator: '&',
|
|
202
|
+
named: true,
|
|
203
|
+
empty_name_separator: true,
|
|
204
|
+
encode: 'strictEncode'
|
|
205
|
+
},
|
|
206
|
+
// Continuation of query-style &name=value pairs
|
|
207
|
+
// within a literal query component.
|
|
208
|
+
'&': {
|
|
209
|
+
prefix: '&',
|
|
210
|
+
separator: '&',
|
|
211
|
+
named: true,
|
|
212
|
+
empty_name_separator: true,
|
|
213
|
+
encode: 'strictEncode'
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// The operator characters equals ("="), comma (","), exclamation ("!"),
|
|
217
|
+
// at sign ("@"), and pipe ("|") are reserved for future extensions.
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Processor for URI templates: http://tools.ietf.org/html/rfc6570
|
|
222
|
+
*/
|
|
223
|
+
export class UriTemplate {
|
|
224
|
+
parts?: (string | IPart)[];
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Allows to ache and reuse cached instances.
|
|
228
|
+
*
|
|
229
|
+
* @param uri The URI string.
|
|
230
|
+
* @returns
|
|
231
|
+
*/
|
|
232
|
+
static fromUri(uri: string): UriTemplate {
|
|
233
|
+
let cached = cache.get(uri);
|
|
234
|
+
if (!cached) {
|
|
235
|
+
cached = new UriTemplate(uri);
|
|
236
|
+
cache.set(uri, cached);
|
|
237
|
+
}
|
|
238
|
+
return cached;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* @param expression The URI string.
|
|
243
|
+
*/
|
|
244
|
+
constructor(protected expression: string) { }
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Expands the template with the given map values.
|
|
248
|
+
*
|
|
249
|
+
* @param map The map with values
|
|
250
|
+
* @param opts Processing options
|
|
251
|
+
* @returns The expanded URI.
|
|
252
|
+
*/
|
|
253
|
+
expand(map: Record<string, any>, opts: IUriTemplateOptions = {}): string {
|
|
254
|
+
let result = '';
|
|
255
|
+
if (!this.parts || !this.parts.length) {
|
|
256
|
+
this.parse();
|
|
257
|
+
}
|
|
258
|
+
const data = new Data(map);
|
|
259
|
+
|
|
260
|
+
for (const part of this.parts!) {
|
|
261
|
+
const item = typeof part === 'string' ? part : UriTemplate.expand(part, data, opts);
|
|
262
|
+
result += item;
|
|
263
|
+
}
|
|
264
|
+
return result;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Parses the template into action tokens.
|
|
269
|
+
*/
|
|
270
|
+
parse(): void {
|
|
271
|
+
const { expression } = this;
|
|
272
|
+
const parts: (string | IPart)[] = [];
|
|
273
|
+
let pos = 0;
|
|
274
|
+
|
|
275
|
+
function checkLiteral(literal: string): string {
|
|
276
|
+
if (literal.match(LITERAL_PATTERN)) {
|
|
277
|
+
throw new Error(`Invalid Literal "${literal}"`);
|
|
278
|
+
}
|
|
279
|
+
return literal;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
EXPRESSION_PATTERN.lastIndex = 0;
|
|
283
|
+
|
|
284
|
+
// eslint-disable-next-line no-constant-condition
|
|
285
|
+
while (true) {
|
|
286
|
+
const eMatch = EXPRESSION_PATTERN.exec(expression);
|
|
287
|
+
if (eMatch === null) {
|
|
288
|
+
// push trailing literal
|
|
289
|
+
parts.push(checkLiteral(expression.substring(pos)));
|
|
290
|
+
break;
|
|
291
|
+
} else {
|
|
292
|
+
// push leading literal
|
|
293
|
+
parts.push(checkLiteral(expression.substring(pos, eMatch.index)));
|
|
294
|
+
pos = eMatch.index + eMatch[0].length;
|
|
295
|
+
}
|
|
296
|
+
if (!operators[eMatch[1]]) {
|
|
297
|
+
throw new Error(`Unknown Operator "${eMatch[1]}" in "${eMatch[0]}"`);
|
|
298
|
+
} else if (!eMatch[3]) {
|
|
299
|
+
throw new Error(`Unclosed Expression "${eMatch[0]}"`);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// parse variable-list
|
|
303
|
+
const varParts = eMatch[2].split(',');
|
|
304
|
+
const vars: IVariable[] = [];
|
|
305
|
+
|
|
306
|
+
for (var i = 0, l = varParts.length; i < l; i++) {
|
|
307
|
+
const vMatch = varParts[i].match(VARIABLE_PATTERN);
|
|
308
|
+
if (vMatch === null) {
|
|
309
|
+
throw new Error(`Invalid Variable "${varParts[i]}" in "${eMatch[0]}"`);
|
|
310
|
+
} else if (vMatch[1].match(VARIABLE_NAME_PATTERN)) {
|
|
311
|
+
throw new Error(`Invalid Variable Name "${vMatch[1]}" in ""${eMatch[0]}"`);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
vars[i] = {
|
|
315
|
+
name: vMatch[1],
|
|
316
|
+
explode: !!vMatch[3],
|
|
317
|
+
maxlength: vMatch[4] && parseInt(vMatch[4], 10) || undefined,
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (!vars.length) {
|
|
322
|
+
throw new Error(`Expression Missing Variable(s) "${eMatch[0]}"`);
|
|
323
|
+
}
|
|
324
|
+
parts.push({
|
|
325
|
+
expression: eMatch[0],
|
|
326
|
+
operator: eMatch[1],
|
|
327
|
+
variables: vars,
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (!parts.length) {
|
|
332
|
+
// template doesn't contain any expressions
|
|
333
|
+
// so it is a simple literal string
|
|
334
|
+
// this probably should fire a warning or something?
|
|
335
|
+
parts.push(checkLiteral(expression));
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
this.parts = parts;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
protected static expand(expression: IPart, data: Data, opts: IUriTemplateOptions = {}): string {
|
|
342
|
+
const options = operators[expression.operator];
|
|
343
|
+
const type = options.named ? 'Named' : 'Unnamed';
|
|
344
|
+
const { variables } = expression;
|
|
345
|
+
const buffer = [];
|
|
346
|
+
|
|
347
|
+
for (const variable of variables) {
|
|
348
|
+
const d = data.get(variable.name);
|
|
349
|
+
if (d.type === DataType.nil && opts && opts.strict) {
|
|
350
|
+
throw new Error(`Missing expansion value for variable "${variable.name}"`);
|
|
351
|
+
}
|
|
352
|
+
if (d.type === DataType.nil && opts.ignoreMissing) {
|
|
353
|
+
buffer.push(expression.expression)
|
|
354
|
+
continue
|
|
355
|
+
}
|
|
356
|
+
if (!d.val.length) {
|
|
357
|
+
if (d.type !== DataType.nil) {
|
|
358
|
+
// empty variables (empty string) still lead to a separator being appended!
|
|
359
|
+
buffer.push('');
|
|
360
|
+
}
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
if (d.type > DataType.string && variable.maxlength) {
|
|
364
|
+
throw new Error(`Invalid expression: Prefix modifier not applicable to variable "${variable.name}"`);
|
|
365
|
+
}
|
|
366
|
+
buffer.push(UriTemplate[`expand${type}`](
|
|
367
|
+
d,
|
|
368
|
+
options,
|
|
369
|
+
variable.explode,
|
|
370
|
+
variable.explode && options.separator || ',',
|
|
371
|
+
variable.maxlength,
|
|
372
|
+
variable.name
|
|
373
|
+
));
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (buffer.length) {
|
|
377
|
+
return options.prefix + buffer.join(options.separator);
|
|
378
|
+
}
|
|
379
|
+
// prefix is not prepended for empty expressions
|
|
380
|
+
return '';
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Expands a named variable.
|
|
385
|
+
*/
|
|
386
|
+
protected static expandNamed(d: IData, options: IOperator, explode: boolean, separator: string, length?: number, name?: string): string {
|
|
387
|
+
let result = '';
|
|
388
|
+
const { encode, empty_name_separator } = options;
|
|
389
|
+
const _encode = !d[encode].length;
|
|
390
|
+
let _name = d.type === DataType.object ? '' : UrlEncoder[encode](name!);
|
|
391
|
+
|
|
392
|
+
d.val.forEach((item, index) => {
|
|
393
|
+
let _value;
|
|
394
|
+
if (length) {
|
|
395
|
+
// maxlength must be determined before encoding can happen
|
|
396
|
+
_value = UrlEncoder[encode](item[1]!.substring(0, length));
|
|
397
|
+
if (d.type === DataType.object) {
|
|
398
|
+
// apply maxlength to keys of objects as well
|
|
399
|
+
_name = UrlEncoder[encode](item[0]!.substring(0, length));
|
|
400
|
+
}
|
|
401
|
+
} else if (_encode) {
|
|
402
|
+
// encode value
|
|
403
|
+
_value = UrlEncoder[encode](item[1]!);
|
|
404
|
+
if (d.type === DataType.object) {
|
|
405
|
+
// encode name and cache encoded value
|
|
406
|
+
_name = UrlEncoder[encode](item[0]!);
|
|
407
|
+
d[encode].push([_name, _value]);
|
|
408
|
+
} else {
|
|
409
|
+
// cache encoded value
|
|
410
|
+
d[encode].push([undefined, _value]);
|
|
411
|
+
}
|
|
412
|
+
} else {
|
|
413
|
+
// values are already encoded and can be pulled from cache
|
|
414
|
+
_value = d[encode][index][1];
|
|
415
|
+
if (d.type === DataType.object) {
|
|
416
|
+
_name = d[encode][index][0]!;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
if (result) {
|
|
421
|
+
result += separator;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (!explode) {
|
|
425
|
+
if (!index) {
|
|
426
|
+
result += UrlEncoder[encode](name!) + (empty_name_separator || _value ? '=' : '');
|
|
427
|
+
}
|
|
428
|
+
if (d.type === DataType.object) {
|
|
429
|
+
result += `${_name},`;
|
|
430
|
+
}
|
|
431
|
+
result += _value;
|
|
432
|
+
} else {
|
|
433
|
+
result += _name + (empty_name_separator || _value ? '=' : '') + _value;
|
|
434
|
+
}
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
return result;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Expands an unnamed variable.
|
|
442
|
+
*/
|
|
443
|
+
protected static expandUnnamed(d: IData, options: IOperator, explode: boolean, separator: string, length?: number): string {
|
|
444
|
+
let result = '';
|
|
445
|
+
const { encode, empty_name_separator } = options;
|
|
446
|
+
const _encode = !d[encode].length;
|
|
447
|
+
|
|
448
|
+
d.val.forEach((item, index) => {
|
|
449
|
+
let _value: string;
|
|
450
|
+
if (length) {
|
|
451
|
+
// maxlength must be determined before encoding can happen
|
|
452
|
+
_value = UrlEncoder[encode](item[1]!.substring(0, length));
|
|
453
|
+
} else if (_encode) {
|
|
454
|
+
// encode and cache value
|
|
455
|
+
_value = UrlEncoder[encode](item[1]!);
|
|
456
|
+
d[encode].push([
|
|
457
|
+
d.type === DataType.object ? UrlEncoder[encode](item[0]!) : undefined,
|
|
458
|
+
_value
|
|
459
|
+
]);
|
|
460
|
+
} else {
|
|
461
|
+
// value already encoded, pull from cache
|
|
462
|
+
_value = d[encode][index][1]!;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
if (result) {
|
|
466
|
+
// unless we're the first value, prepend the separator
|
|
467
|
+
result += separator;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
if (d.type === DataType.object) {
|
|
471
|
+
let _name;
|
|
472
|
+
if (length) {
|
|
473
|
+
// maxlength also applies to keys of objects
|
|
474
|
+
_name = UrlEncoder[encode](item[0]!.substring(0, length));
|
|
475
|
+
} else {
|
|
476
|
+
// at this point the name must already be encoded
|
|
477
|
+
_name = d[encode][index][0];
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
result += _name;
|
|
481
|
+
if (explode) {
|
|
482
|
+
// explode-modifier separates name and value by "="
|
|
483
|
+
result += (empty_name_separator || _value ? '=' : '');
|
|
484
|
+
} else {
|
|
485
|
+
// no explode-modifier separates name and value by ","
|
|
486
|
+
result += ',';
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
result += _value;
|
|
491
|
+
});
|
|
492
|
+
return result;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
@@ -71,4 +71,55 @@ export class UrlEncoder {
|
|
|
71
71
|
}
|
|
72
72
|
return decodeURIComponent(result);
|
|
73
73
|
}
|
|
74
|
+
|
|
75
|
+
static strictEncode(str: string): string {
|
|
76
|
+
if (!str) {
|
|
77
|
+
return str;
|
|
78
|
+
}
|
|
79
|
+
const escaped = {
|
|
80
|
+
'!': '%21',
|
|
81
|
+
"'": '%27',
|
|
82
|
+
'(': '%28',
|
|
83
|
+
')': '%29',
|
|
84
|
+
};
|
|
85
|
+
return encodeURIComponent(str).replace(/\*/g, '%2A')
|
|
86
|
+
// @ts-ignore
|
|
87
|
+
.replace(/[!'()*]/g, (c) => escaped[c] );
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* For URI templates encodes the URL string without encoding the reserved characters.
|
|
92
|
+
*/
|
|
93
|
+
static encodeReserved(str: string): string {
|
|
94
|
+
if (!str) {
|
|
95
|
+
return str;
|
|
96
|
+
}
|
|
97
|
+
const expression = /%(21|23|24|26|27|28|29|2A|2B|2C|2F|3A|3B|3D|3F|40|5B|5D)/ig;
|
|
98
|
+
const map = {
|
|
99
|
+
// gen-delims
|
|
100
|
+
'%3A': ':',
|
|
101
|
+
'%2F': '/',
|
|
102
|
+
'%3F': '?',
|
|
103
|
+
'%23': '#',
|
|
104
|
+
'%5B': '[',
|
|
105
|
+
'%5D': ']',
|
|
106
|
+
'%40': '@',
|
|
107
|
+
// sub-delims
|
|
108
|
+
'%21': '!',
|
|
109
|
+
'%24': '$',
|
|
110
|
+
'%26': '&',
|
|
111
|
+
'%27': '\'',
|
|
112
|
+
'%28': '(',
|
|
113
|
+
'%29': ')',
|
|
114
|
+
'%2A': '*',
|
|
115
|
+
'%2B': '+',
|
|
116
|
+
'%2C': ',',
|
|
117
|
+
'%3B': ';',
|
|
118
|
+
'%3D': '='
|
|
119
|
+
};
|
|
120
|
+
let result = UrlEncoder.strictEncode(str);
|
|
121
|
+
// @ts-ignore
|
|
122
|
+
result = result.replace(expression, (c) => map[c]);
|
|
123
|
+
return result;
|
|
124
|
+
}
|
|
74
125
|
}
|
|
@@ -1107,6 +1107,16 @@ export class HttpProject extends ProjectParent {
|
|
|
1107
1107
|
return result.reverse();
|
|
1108
1108
|
}
|
|
1109
1109
|
|
|
1110
|
+
/**
|
|
1111
|
+
* @returns Returns the effective environments. If the project has been initialized with an environment then it is returned. Otherwise other environments.
|
|
1112
|
+
*/
|
|
1113
|
+
getEnvironments(): Environment[] {
|
|
1114
|
+
if (Array.isArray(this.initEnvironments)) {
|
|
1115
|
+
return this.initEnvironments;
|
|
1116
|
+
}
|
|
1117
|
+
return super.getEnvironments();
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1110
1120
|
/**
|
|
1111
1121
|
* Makes a copy of this project.
|
|
1112
1122
|
*/
|
|
@@ -1257,13 +1267,6 @@ export class HttpProject extends ProjectParent {
|
|
|
1257
1267
|
return this.definitions.schemas;
|
|
1258
1268
|
}
|
|
1259
1269
|
|
|
1260
|
-
getEnvironments(): Environment[] {
|
|
1261
|
-
if (Array.isArray(this.initEnvironments)) {
|
|
1262
|
-
return this.initEnvironments;
|
|
1263
|
-
}
|
|
1264
|
-
return super.getEnvironments();
|
|
1265
|
-
}
|
|
1266
|
-
|
|
1267
1270
|
/**
|
|
1268
1271
|
* Iterates over requests in the project.
|
|
1269
1272
|
*/
|
|
@@ -110,4 +110,31 @@ export abstract class ProjectParent implements ProjectDefinitionProperty {
|
|
|
110
110
|
});
|
|
111
111
|
return result;
|
|
112
112
|
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* @param key The environment key to read.
|
|
116
|
+
*/
|
|
117
|
+
getEnvironment(key: string): Environment | undefined {
|
|
118
|
+
const { environments } = this;
|
|
119
|
+
if (!environments.length) {
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
const has = environments.includes(key);
|
|
123
|
+
if (!has) {
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
const project = this.getProject();
|
|
127
|
+
if (!project.definitions.environments) {
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
130
|
+
return project.definitions.environments.find(i => i.key === key);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* This is a link to the `getEnvironments()`. The difference is that on the
|
|
135
|
+
* project level it won't return environments defined with the class initialization.
|
|
136
|
+
*/
|
|
137
|
+
listEnvironments(): Environment[] {
|
|
138
|
+
return this.getEnvironments();
|
|
139
|
+
}
|
|
113
140
|
}
|
package/src/models/Property.ts
CHANGED
|
@@ -420,4 +420,22 @@ export class Property {
|
|
|
420
420
|
}
|
|
421
421
|
return result;
|
|
422
422
|
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Maps the list of properties to a single map.
|
|
426
|
+
* It overrides previously defined properties on the map in case of multiple variables with the same name.
|
|
427
|
+
*
|
|
428
|
+
* @param properties The list of properties to map.
|
|
429
|
+
* @returns A map where keys are property names and values are the property values.
|
|
430
|
+
*/
|
|
431
|
+
static toMap(properties: (IProperty | Property)[]): Record<string, any> {
|
|
432
|
+
const result: Record<string, any> = {};
|
|
433
|
+
properties.forEach(p => {
|
|
434
|
+
if (p.enabled === false) {
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
result[p.name] = p.value;
|
|
438
|
+
});
|
|
439
|
+
return result;
|
|
440
|
+
}
|
|
423
441
|
}
|
package/src/models/Server.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { IProperty, Property } from './Property.js';
|
|
2
|
+
import { UriTemplate } from '../lib/parsers/UriTemplate.js';
|
|
3
|
+
|
|
1
4
|
export const Kind = 'Core#Server';
|
|
2
5
|
|
|
3
6
|
export interface IServer {
|
|
@@ -129,8 +132,10 @@ export class Server {
|
|
|
129
132
|
|
|
130
133
|
/**
|
|
131
134
|
* Constructs the final URI from the server configuration.
|
|
135
|
+
*
|
|
136
|
+
* @param variables When set it evaluates the generated URI against these variables
|
|
132
137
|
*/
|
|
133
|
-
readUri(): string {
|
|
138
|
+
readUri(variables?: (IProperty | Property)[]): string {
|
|
134
139
|
const { uri, protocol, basePath } = this;
|
|
135
140
|
let result = '';
|
|
136
141
|
if (!uri) {
|
|
@@ -148,6 +153,32 @@ export class Server {
|
|
|
148
153
|
}
|
|
149
154
|
result += basePath.startsWith('/') ? basePath : `/${basePath}`
|
|
150
155
|
}
|
|
156
|
+
if (variables) {
|
|
157
|
+
return this.evaluateUri(result, variables);
|
|
158
|
+
}
|
|
159
|
+
return result;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Evaluates the URI against the variables.
|
|
164
|
+
*
|
|
165
|
+
* Note, this doesn't throw errors. When error occurs it returns the input string.
|
|
166
|
+
*
|
|
167
|
+
* @param uri The URI to process
|
|
168
|
+
* @param variables The list of variables to use
|
|
169
|
+
* @returns Expanded URI.
|
|
170
|
+
*/
|
|
171
|
+
protected evaluateUri(uri: string, variables: (IProperty | Property)[]): string {
|
|
172
|
+
if (!variables || !variables.length) {
|
|
173
|
+
return uri;
|
|
174
|
+
}
|
|
175
|
+
let result = uri;
|
|
176
|
+
try {
|
|
177
|
+
const map = Property.toMap(variables);
|
|
178
|
+
result = new UriTemplate(uri).expand(map, { ignoreMissing: true });
|
|
179
|
+
} catch (e) {
|
|
180
|
+
//
|
|
181
|
+
}
|
|
151
182
|
return result;
|
|
152
183
|
}
|
|
153
184
|
}
|
|
@@ -2,7 +2,7 @@ import { IHttpRequest } from 'src/models/HttpRequest.js';
|
|
|
2
2
|
import { ActionRunnable } from './ActionRunnable.js';
|
|
3
3
|
import { IDeleteCookieAction } from '../../../models/actions/runnable/DeleteCookieAction.js';
|
|
4
4
|
import { Events } from '../../../events/Events.js';
|
|
5
|
-
import { IRequestLog } from '
|
|
5
|
+
import { IRequestLog } from '../../../models/RequestLog.js';
|
|
6
6
|
|
|
7
7
|
export class DeleteCookieRunnable extends ActionRunnable {
|
|
8
8
|
async response(log: IRequestLog): Promise<void> {
|
|
@@ -4,7 +4,7 @@ import { ActionRunnable } from './ActionRunnable.js';
|
|
|
4
4
|
import { ISetCookieAction } from '../../../models/actions/runnable/SetCookieAction.js';
|
|
5
5
|
import { Events } from '../../../events/Events.js';
|
|
6
6
|
import { RequestDataExtractor } from '../../../data/RequestDataExtractor.js';
|
|
7
|
-
import { IRequestLog } from '
|
|
7
|
+
import { IRequestLog } from '../../../models/RequestLog.js';
|
|
8
8
|
|
|
9
9
|
export class SetCookieRunnable extends ActionRunnable {
|
|
10
10
|
async request(request: IHttpRequest): Promise<void> {
|