@opra/http 1.21.0 → 1.22.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.
Files changed (70) hide show
  1. package/{esm/express-adapter.js → express-adapter.js} +2 -1
  2. package/{esm/http-adapter.js → http-adapter.js} +5 -1
  3. package/{esm/http-context.js → http-context.js} +10 -1
  4. package/{esm/http-handler.js → http-handler.js} +3 -0
  5. package/{esm/impl → impl}/http-incoming.host.js +1 -0
  6. package/{esm/impl → impl}/multipart-reader.js +10 -5
  7. package/{esm/impl → impl}/node-incoming-message.host.js +15 -4
  8. package/{esm/impl → impl}/node-outgoing-message.host.js +7 -2
  9. package/package.json +14 -31
  10. package/{esm/utils → utils}/body-reader.js +10 -2
  11. package/cjs/express-adapter.js +0 -134
  12. package/cjs/http-adapter.js +0 -25
  13. package/cjs/http-context.js +0 -114
  14. package/cjs/http-handler.js +0 -668
  15. package/cjs/impl/http-incoming.host.js +0 -112
  16. package/cjs/impl/http-outgoing.host.js +0 -207
  17. package/cjs/impl/multipart-reader.js +0 -209
  18. package/cjs/impl/node-incoming-message.host.js +0 -115
  19. package/cjs/impl/node-outgoing-message.host.js +0 -201
  20. package/cjs/index.js +0 -27
  21. package/cjs/interfaces/http-incoming.interface.js +0 -25
  22. package/cjs/interfaces/http-outgoing.interface.js +0 -22
  23. package/cjs/interfaces/node-incoming-message.interface.js +0 -65
  24. package/cjs/interfaces/node-outgoing-message.interface.js +0 -15
  25. package/cjs/package.json +0 -3
  26. package/cjs/type-guards.js +0 -27
  27. package/cjs/utils/body-reader.js +0 -227
  28. package/cjs/utils/common.js +0 -67
  29. package/cjs/utils/concat-readable.js +0 -19
  30. package/cjs/utils/convert-to-headers.js +0 -64
  31. package/cjs/utils/convert-to-raw-headers.js +0 -25
  32. package/cjs/utils/match-known-fields.js +0 -49
  33. package/cjs/utils/wrap-exception.js +0 -33
  34. package/esm/package.json +0 -3
  35. package/types/index.d.cts +0 -22
  36. /package/{types/express-adapter.d.ts → express-adapter.d.ts} +0 -0
  37. /package/{types/http-adapter.d.ts → http-adapter.d.ts} +0 -0
  38. /package/{types/http-context.d.ts → http-context.d.ts} +0 -0
  39. /package/{types/http-handler.d.ts → http-handler.d.ts} +0 -0
  40. /package/{types/impl → impl}/http-incoming.host.d.ts +0 -0
  41. /package/{types/impl → impl}/http-outgoing.host.d.ts +0 -0
  42. /package/{esm/impl → impl}/http-outgoing.host.js +0 -0
  43. /package/{types/impl → impl}/multipart-reader.d.ts +0 -0
  44. /package/{types/impl → impl}/node-incoming-message.host.d.ts +0 -0
  45. /package/{types/impl → impl}/node-outgoing-message.host.d.ts +0 -0
  46. /package/{types/index.d.ts → index.d.ts} +0 -0
  47. /package/{esm/index.js → index.js} +0 -0
  48. /package/{types/interfaces → interfaces}/http-incoming.interface.d.ts +0 -0
  49. /package/{esm/interfaces → interfaces}/http-incoming.interface.js +0 -0
  50. /package/{types/interfaces → interfaces}/http-outgoing.interface.d.ts +0 -0
  51. /package/{esm/interfaces → interfaces}/http-outgoing.interface.js +0 -0
  52. /package/{types/interfaces → interfaces}/node-incoming-message.interface.d.ts +0 -0
  53. /package/{esm/interfaces → interfaces}/node-incoming-message.interface.js +0 -0
  54. /package/{types/interfaces → interfaces}/node-outgoing-message.interface.d.ts +0 -0
  55. /package/{esm/interfaces → interfaces}/node-outgoing-message.interface.js +0 -0
  56. /package/{types/type-guards.d.ts → type-guards.d.ts} +0 -0
  57. /package/{esm/type-guards.js → type-guards.js} +0 -0
  58. /package/{types/utils → utils}/body-reader.d.ts +0 -0
  59. /package/{types/utils → utils}/common.d.ts +0 -0
  60. /package/{esm/utils → utils}/common.js +0 -0
  61. /package/{types/utils → utils}/concat-readable.d.ts +0 -0
  62. /package/{esm/utils → utils}/concat-readable.js +0 -0
  63. /package/{types/utils → utils}/convert-to-headers.d.ts +0 -0
  64. /package/{esm/utils → utils}/convert-to-headers.js +0 -0
  65. /package/{types/utils → utils}/convert-to-raw-headers.d.ts +0 -0
  66. /package/{esm/utils → utils}/convert-to-raw-headers.js +0 -0
  67. /package/{types/utils → utils}/match-known-fields.d.ts +0 -0
  68. /package/{esm/utils → utils}/match-known-fields.js +0 -0
  69. /package/{types/utils → utils}/wrap-exception.d.ts +0 -0
  70. /package/{esm/utils → utils}/wrap-exception.js +0 -0
@@ -1,668 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.HttpHandler = void 0;
4
- const tslib_1 = require("tslib");
5
- const process = tslib_1.__importStar(require("node:process"));
6
- const type_is_1 = tslib_1.__importDefault(require("@browsery/type-is"));
7
- const common_1 = require("@opra/common");
8
- const core_1 = require("@opra/core");
9
- const content_type_1 = require("content-type");
10
- const fast_tokenizer_1 = require("fast-tokenizer");
11
- const super_fast_md5_1 = require("super-fast-md5");
12
- const ts_gems_1 = require("ts-gems");
13
- const valgen_1 = require("valgen");
14
- const wrap_exception_js_1 = require("./utils/wrap-exception.js");
15
- /**
16
- * @class HttpHandler
17
- */
18
- class HttpHandler {
19
- constructor(adapter) {
20
- this.adapter = adapter;
21
- this[core_1.kAssetCache] = adapter[core_1.kAssetCache];
22
- }
23
- /**
24
- * Main http request handler
25
- * @param context
26
- * @protected
27
- */
28
- async handleRequest(context) {
29
- const { response } = context;
30
- try {
31
- response.setHeader(common_1.HttpHeaderCodes.X_Opra_Version, common_1.OpraSchema.SpecVersion);
32
- // Expose headers if cors enabled
33
- if (response.getHeader(common_1.HttpHeaderCodes.Access_Control_Allow_Origin)) {
34
- // Expose X-Opra-* headers
35
- response.appendHeader(common_1.HttpHeaderCodes.Access_Control_Expose_Headers, Object.values(common_1.HttpHeaderCodes).filter(k => k.toLowerCase().startsWith('x-opra-')));
36
- }
37
- // Parse request
38
- try {
39
- await this.parseRequest(context);
40
- }
41
- catch (e) {
42
- if (e instanceof common_1.OpraException)
43
- throw e;
44
- if (e instanceof valgen_1.ValidationError) {
45
- throw new common_1.BadRequestError({
46
- message: 'Request validation failed',
47
- code: 'REQUEST_VALIDATION',
48
- details: e.issues,
49
- }, e);
50
- }
51
- throw new common_1.BadRequestError(e);
52
- }
53
- await this.adapter.emitAsync('request', context);
54
- // Call interceptors than execute request
55
- if (this.adapter.interceptors) {
56
- const interceptors = this.adapter.interceptors;
57
- let i = 0;
58
- const next = async () => {
59
- const interceptor = interceptors[i++];
60
- if (typeof interceptor === 'function')
61
- await interceptor(context, next);
62
- else if (typeof interceptor?.intercept === 'function')
63
- await interceptor.intercept(context, next);
64
- await this._executeRequest(context);
65
- };
66
- await next();
67
- }
68
- else
69
- await this._executeRequest(context);
70
- }
71
- catch (error) {
72
- let e = error;
73
- if (e instanceof valgen_1.ValidationError) {
74
- e = new common_1.InternalServerError({
75
- message: 'Response validation failed',
76
- code: 'RESPONSE_VALIDATION',
77
- details: e.issues,
78
- }, e);
79
- }
80
- else
81
- e = (0, wrap_exception_js_1.wrapException)(e);
82
- if (this.onError)
83
- await this.onError(context, error);
84
- context.errors.push(e);
85
- await this.sendResponse(context);
86
- }
87
- finally {
88
- await context.emitAsync('finish', context);
89
- }
90
- }
91
- /**
92
- *
93
- * @param context
94
- */
95
- async parseRequest(context) {
96
- await this._parseParameters(context);
97
- await this._parseContentType(context);
98
- if (context.__oprDef?.requestBody?.immediateFetch)
99
- await context.getBody();
100
- /** Set default status code as the first status code between 200 and 299 */
101
- if (context.__oprDef) {
102
- for (const r of context.__oprDef.responses) {
103
- const st = r.statusCode.find(sc => sc.start <= 299 && sc.end >= 200);
104
- if (st) {
105
- context.response.status(st.start);
106
- break;
107
- }
108
- }
109
- }
110
- }
111
- /**
112
- *
113
- * @param context
114
- * @protected
115
- */
116
- async _parseParameters(context) {
117
- const { __oprDef, request } = context;
118
- if (!__oprDef)
119
- return;
120
- let key = '';
121
- try {
122
- const onFail = (issue) => {
123
- issue.location = key;
124
- return issue;
125
- };
126
- /** prepare decoders */
127
- const getDecoder = (prm) => {
128
- let decode = this[core_1.kAssetCache].get(prm, 'decode');
129
- if (!decode) {
130
- decode = prm.generateCodec('decode', {
131
- scope: this.adapter.scope,
132
- ignoreReadonlyFields: true,
133
- });
134
- this[core_1.kAssetCache].set(prm, 'decode', decode);
135
- }
136
- return decode;
137
- };
138
- const paramsLeft = new Set([
139
- ...__oprDef.parameters,
140
- ...__oprDef.owner.parameters,
141
- ]);
142
- /** parse cookie parameters */
143
- if (request.cookies) {
144
- for (key of Object.keys(request.cookies)) {
145
- const oprPrm = __oprDef.findParameter(key, 'cookie');
146
- const cntPrm = __oprDef.owner.findParameter(key, 'cookie');
147
- const prm = oprPrm || cntPrm;
148
- if (!prm)
149
- continue;
150
- if (oprPrm)
151
- paramsLeft.delete(oprPrm);
152
- if (cntPrm)
153
- paramsLeft.delete(cntPrm);
154
- const decode = getDecoder(prm);
155
- const v = decode(request.cookies[key], {
156
- coerce: true,
157
- label: key,
158
- onFail,
159
- });
160
- const prmName = typeof prm.name === 'string' ? prm.name : key;
161
- if (v !== undefined)
162
- context.cookies[prmName] = v;
163
- }
164
- }
165
- /** parse headers */
166
- if (request.headers) {
167
- for (key of Object.keys(request.headers)) {
168
- const oprPrm = __oprDef.findParameter(key, 'header');
169
- const cntPrm = __oprDef.owner.findParameter(key, 'header');
170
- const prm = oprPrm || cntPrm;
171
- if (!prm)
172
- continue;
173
- if (oprPrm)
174
- paramsLeft.delete(oprPrm);
175
- if (cntPrm)
176
- paramsLeft.delete(cntPrm);
177
- const decode = getDecoder(prm);
178
- const v = decode(request.headers[key], {
179
- coerce: true,
180
- label: key,
181
- onFail,
182
- });
183
- const prmName = typeof prm.name === 'string' ? prm.name : key;
184
- if (v !== undefined)
185
- context.headers[prmName] = v;
186
- }
187
- }
188
- /** parse path parameters */
189
- if (request.params) {
190
- for (key of Object.keys(request.params)) {
191
- const oprPrm = __oprDef.findParameter(key, 'path');
192
- const cntPrm = __oprDef.owner.findParameter(key, 'path');
193
- const prm = oprPrm || cntPrm;
194
- if (!prm)
195
- continue;
196
- if (oprPrm)
197
- paramsLeft.delete(oprPrm);
198
- if (cntPrm)
199
- paramsLeft.delete(cntPrm);
200
- const decode = getDecoder(prm);
201
- const v = decode(request.params[key], {
202
- coerce: true,
203
- label: key,
204
- onFail,
205
- });
206
- if (v !== undefined)
207
- context.pathParams[key] = v;
208
- }
209
- }
210
- /** parse query parameters */
211
- const url = new URL(request.originalUrl || request.url || '/', 'http://tempuri.org');
212
- const { searchParams } = url;
213
- for (key of searchParams.keys()) {
214
- const oprPrm = __oprDef.findParameter(key, 'query');
215
- const cntPrm = __oprDef.owner.findParameter(key, 'query');
216
- const prm = oprPrm || cntPrm;
217
- if (!prm)
218
- continue;
219
- if (oprPrm)
220
- paramsLeft.delete(oprPrm);
221
- if (cntPrm)
222
- paramsLeft.delete(cntPrm);
223
- const decode = getDecoder(prm);
224
- let values = searchParams?.getAll(key);
225
- const prmName = typeof prm.name === 'string' ? prm.name : key;
226
- if (values?.length && prm.isArray) {
227
- values = values
228
- .map(v => (0, fast_tokenizer_1.splitString)(v, {
229
- delimiters: prm.arraySeparator || ',',
230
- quotes: true,
231
- }))
232
- .flat();
233
- values = values.map(v => decode(v, { coerce: true, label: key, onFail }));
234
- if (prm.parser)
235
- values = prm.parser(values);
236
- if (values.length)
237
- context.queryParams[prmName] = values;
238
- }
239
- else {
240
- let v = decode(values[0], { coerce: true, label: key, onFail });
241
- if (prm.parser)
242
- v = prm.parser(v);
243
- if (values.length)
244
- context.queryParams[prmName] = v;
245
- }
246
- }
247
- for (const prm of paramsLeft) {
248
- key = String(prm.name);
249
- // Throw error for required parameters
250
- if (prm.required) {
251
- const decode = getDecoder(prm);
252
- decode(undefined, { coerce: true, label: String(prm.name), onFail });
253
- }
254
- }
255
- }
256
- catch (e) {
257
- if (e instanceof valgen_1.ValidationError) {
258
- throw new common_1.BadRequestError({
259
- message: `Invalid parameter (${key}) value. ` + e.message,
260
- code: 'REQUEST_VALIDATION',
261
- details: e.issues,
262
- }, e);
263
- }
264
- throw e;
265
- }
266
- }
267
- /**
268
- *
269
- * @param context
270
- * @protected
271
- */
272
- async _parseContentType(context) {
273
- const { request, __oprDef } = context;
274
- if (!__oprDef)
275
- return;
276
- if (__oprDef.requestBody?.content.length) {
277
- let mediaType;
278
- let contentType = request.header('content-type');
279
- if (contentType) {
280
- contentType = (0, content_type_1.parse)(contentType).type;
281
- mediaType = __oprDef.requestBody.content.find(mc => mc.contentType &&
282
- type_is_1.default.is(contentType, Array.isArray(mc.contentType) ? mc.contentType : [mc.contentType]));
283
- }
284
- if (!mediaType) {
285
- const contentTypes = __oprDef.requestBody.content
286
- .map(mc => mc.contentType)
287
- .flat();
288
- throw new common_1.BadRequestError(`Request body should be one of required content types (${contentTypes.join(', ')})`);
289
- }
290
- (0, ts_gems_1.asMutable)(context).mediaType = mediaType;
291
- }
292
- }
293
- /**
294
- *
295
- * @param context
296
- * @protected
297
- */
298
- async _executeRequest(context) {
299
- if (!context.__handler)
300
- throw new common_1.MethodNotAllowedError();
301
- const responseValue = await context.__handler.call(context.__controller, context);
302
- const { response } = context;
303
- if (!response.writableEnded) {
304
- await this.sendResponse(context, responseValue).finally(() => {
305
- if (!response.writableEnded)
306
- response.end();
307
- });
308
- }
309
- }
310
- /**
311
- *
312
- * @param context
313
- * @param responseValue
314
- * @protected
315
- */
316
- async sendResponse(context, responseValue) {
317
- if (context.errors.length)
318
- return this._sendErrorResponse(context);
319
- const { response } = context;
320
- const { document } = this.adapter;
321
- try {
322
- const responseArgs = this._determineResponseArgs(context, responseValue);
323
- const { operationResponse, statusCode } = responseArgs;
324
- let { contentType, body } = responseArgs;
325
- const operationResultType = document.node.getDataType(common_1.OperationResult);
326
- let operationResultEncoder = this[core_1.kAssetCache].get(operationResultType, 'encode');
327
- if (!operationResultEncoder) {
328
- operationResultEncoder = operationResultType.generateCodec('encode', {
329
- scope: this.adapter.scope,
330
- ignoreWriteonlyFields: true,
331
- });
332
- this[core_1.kAssetCache].set(operationResultType, 'encode', operationResultEncoder);
333
- }
334
- /** Validate response */
335
- if (operationResponse?.type) {
336
- if (!(body == null &&
337
- statusCode === common_1.HttpStatusCode.NO_CONTENT)) {
338
- /** Generate encoder */
339
- const projection = responseArgs.projection || '*';
340
- const assetKey = (0, super_fast_md5_1.md5)(String(projection));
341
- let encode = this[core_1.kAssetCache].get(operationResponse, 'encode:' + assetKey);
342
- if (!encode) {
343
- encode = operationResponse.type.generateCodec('encode', {
344
- scope: this.adapter.scope,
345
- partial: operationResponse.partial,
346
- projection,
347
- ignoreWriteonlyFields: true,
348
- onFail: issue => `Response body validation failed: ` + issue.message,
349
- });
350
- if (operationResponse) {
351
- if (operationResponse.isArray)
352
- encode = valgen_1.vg.isArray(encode);
353
- this[core_1.kAssetCache].set(operationResponse, 'encode:' + assetKey, encode);
354
- }
355
- }
356
- /** Encode body */
357
- if (operationResponse.type.extendsFrom(operationResultType)) {
358
- if (body instanceof common_1.OperationResult)
359
- body = encode(body);
360
- else {
361
- body.payload = encode(body.payload);
362
- body = operationResultEncoder(body);
363
- }
364
- }
365
- else {
366
- if (body instanceof common_1.OperationResult &&
367
- contentType &&
368
- type_is_1.default.is(contentType, [common_1.MimeTypes.opra_response_json])) {
369
- body.payload = encode(body.payload);
370
- body = operationResultEncoder(body);
371
- }
372
- else {
373
- body = encode(body);
374
- }
375
- }
376
- if (body instanceof common_1.OperationResult &&
377
- operationResponse.type &&
378
- operationResponse.type !==
379
- document.node.getDataType(common_1.OperationResult)) {
380
- body.type = operationResponse.type.name
381
- ? operationResponse.type.name
382
- : '#embedded';
383
- }
384
- }
385
- }
386
- else if (body != null) {
387
- if (body instanceof common_1.OperationResult) {
388
- body = operationResultEncoder(body);
389
- contentType = common_1.MimeTypes.opra_response_json;
390
- }
391
- else if (Buffer.isBuffer(body))
392
- contentType = common_1.MimeTypes.binary;
393
- else if (typeof body === 'object') {
394
- contentType = contentType || common_1.MimeTypes.json;
395
- if (typeof body.toJSON === 'function')
396
- body = body.toJSON();
397
- }
398
- else {
399
- contentType = contentType || common_1.MimeTypes.text;
400
- body = String(body);
401
- }
402
- }
403
- /** Set content-type header value if not set */
404
- if (contentType && contentType !== responseArgs.contentType)
405
- response.setHeader('content-type', contentType);
406
- response.status(statusCode);
407
- if (body == null) {
408
- response.end();
409
- return;
410
- }
411
- let x;
412
- if (Buffer.isBuffer(body) || (0, common_1.isReadableStream)(body))
413
- x = body;
414
- else if ((0, common_1.isBlob)(body))
415
- x = body.stream();
416
- else if (typeof body === 'object')
417
- x = JSON.stringify(body);
418
- else
419
- x = String(body);
420
- response.end(x);
421
- }
422
- catch (error) {
423
- context.errors.push(error);
424
- return this._sendErrorResponse(context);
425
- }
426
- }
427
- async _sendErrorResponse(context) {
428
- let errors = (context.errors = this._wrapExceptions(context.errors));
429
- try {
430
- if (context.listenerCount('error')) {
431
- await context.emitAsync('error', errors[0], context);
432
- errors = context.errors = this._wrapExceptions(context.errors);
433
- }
434
- if (this.adapter.listenerCount('error')) {
435
- await this.adapter.emitAsync('error', errors[0], context);
436
- errors = context.errors = this._wrapExceptions(errors);
437
- }
438
- if (this.adapter.logger?.error) {
439
- const logger = this.adapter.logger;
440
- errors.forEach(e => {
441
- if (e.status >= 500 && e.status < 600)
442
- logger.error(e);
443
- });
444
- }
445
- }
446
- catch (e) {
447
- context.errors = this._wrapExceptions([e, ...context.errors]);
448
- }
449
- const { response } = context;
450
- if (response.headersSent) {
451
- response.end();
452
- return;
453
- }
454
- let status = response.statusCode || 0;
455
- if (!status || status < Number(common_1.HttpStatusCode.BAD_REQUEST)) {
456
- status = errors[0].status;
457
- if (status < Number(common_1.HttpStatusCode.BAD_REQUEST))
458
- status = common_1.HttpStatusCode.INTERNAL_SERVER_ERROR;
459
- }
460
- response.statusCode = status;
461
- const { document } = this.adapter;
462
- const dt = document.node.getComplexType('OperationResult');
463
- let encode = this[core_1.kAssetCache].get(dt, 'encode');
464
- if (!encode) {
465
- encode = dt.generateCodec('encode', {
466
- scope: this.adapter.scope,
467
- ignoreWriteonlyFields: true,
468
- });
469
- this[core_1.kAssetCache].set(dt, 'encode', encode);
470
- }
471
- // const { i18n } = this.adapter;
472
- const bodyObject = new common_1.OperationResult({
473
- errors: errors.map(x => {
474
- const o = x.toJSON();
475
- if (!(process.env.NODE_ENV === 'dev' ||
476
- process.env.NODE_ENV === 'development'))
477
- delete o.stack;
478
- return o; // i18n.deep(o);
479
- }),
480
- });
481
- const body = encode(bodyObject);
482
- response.setHeader(common_1.HttpHeaderCodes.Content_Type, common_1.MimeTypes.opra_response_json + '; charset=utf-8');
483
- response.setHeader(common_1.HttpHeaderCodes.Cache_Control, 'no-cache');
484
- response.setHeader(common_1.HttpHeaderCodes.Pragma, 'no-cache');
485
- response.setHeader(common_1.HttpHeaderCodes.Expires, '-1');
486
- response.setHeader(common_1.HttpHeaderCodes.X_Opra_Version, common_1.OpraSchema.SpecVersion);
487
- response.send((0, common_1.safeJsonStringify)(body));
488
- response.end();
489
- }
490
- async sendDocumentSchema(context) {
491
- const { request, response } = context;
492
- const { document } = this.adapter;
493
- response.setHeader('content-type', common_1.MimeTypes.json);
494
- const url = new URL(request.originalUrl || request.url || '/', 'http://tempuri.org');
495
- const { searchParams } = url;
496
- const documentId = searchParams.get('id');
497
- const doc = documentId ? document.findDocument(documentId) : document;
498
- if (!doc) {
499
- context.errors.push(new common_1.BadRequestError({
500
- message: `Document with given id [${documentId}] does not exists`,
501
- }));
502
- return this.sendResponse(context);
503
- }
504
- /** Check if response cache exists */
505
- let responseBody = this[core_1.kAssetCache].get(doc, `$schema`);
506
- /** Create response if response cache does not exists */
507
- if (!responseBody) {
508
- const schema = doc.export({
509
- scope: this.adapter.scope,
510
- });
511
- responseBody = JSON.stringify(schema);
512
- this[core_1.kAssetCache].set(doc, `$schema`, responseBody);
513
- }
514
- response.end(responseBody);
515
- }
516
- /**
517
- *
518
- * @param context
519
- * @param body
520
- * @protected
521
- */
522
- _determineResponseArgs(context, body) {
523
- const { response, __oprDef } = context;
524
- const hasBody = body != null;
525
- const statusCode = !hasBody && response.statusCode === common_1.HttpStatusCode.OK
526
- ? common_1.HttpStatusCode.NO_CONTENT
527
- : response.statusCode;
528
- /** Parse content-type header */
529
- const parsedContentType = hasBody && response.hasHeader('content-type')
530
- ? (0, content_type_1.parse)(response)
531
- : undefined;
532
- let contentType = parsedContentType?.type;
533
- /** Estimate content type if not defined */
534
- if (hasBody && !contentType) {
535
- if (body instanceof common_1.OperationResult)
536
- contentType = common_1.MimeTypes.opra_response_json;
537
- else if (Buffer.isBuffer(body))
538
- contentType = common_1.MimeTypes.binary;
539
- }
540
- let operationResponse;
541
- const cacheKey = `HttpOperationResponse:${statusCode}${contentType ? ':' + contentType : ''}`;
542
- let responseArgs = this[core_1.kAssetCache].get(response, cacheKey);
543
- if (!responseArgs) {
544
- responseArgs = { statusCode, contentType };
545
- if (__oprDef?.responses.length) {
546
- /** Filter available HttpOperationResponse instances according to status code. */
547
- const filteredResponses = __oprDef.responses.filter(r => r.statusCode.find(sc => sc.start <= statusCode && sc.end >= statusCode));
548
- /** Throw InternalServerError if controller returns non-configured status code */
549
- if (!filteredResponses.length && statusCode < 400) {
550
- throw new common_1.InternalServerError(`No responses defined for status code ${statusCode} in operation "${__oprDef.name}"`);
551
- }
552
- /** We search for content-type in filtered HttpOperationResponse array */
553
- if (filteredResponses.length) {
554
- /** If no response returned, and content-type has not been set (No response wants to be returned by operation) */
555
- if (!hasBody) {
556
- /** Find HttpOperationResponse with no content-type */
557
- operationResponse = filteredResponses.find(r => !r.contentType);
558
- }
559
- if (!operationResponse) {
560
- /** Find HttpOperationResponse according to content-type */
561
- if (contentType) {
562
- // Find HttpEndpointResponse instance according to content-type header
563
- operationResponse = filteredResponses.find(r => type_is_1.default.is(contentType, (0, valgen_1.toArray)(r.contentType)));
564
- if (!operationResponse) {
565
- throw new common_1.InternalServerError(`Operation didn't configured to return "${contentType}" content`);
566
- }
567
- }
568
- else {
569
- /** Select first HttpOperationResponse if content-type header has not been set */
570
- operationResponse = filteredResponses[0];
571
- if (operationResponse.contentType) {
572
- const ct = type_is_1.default.normalize(Array.isArray(operationResponse.contentType)
573
- ? operationResponse.contentType[0]
574
- : operationResponse.contentType);
575
- if (typeof ct === 'string')
576
- responseArgs.contentType = contentType = ct;
577
- else if (operationResponse.type)
578
- responseArgs.contentType = common_1.MimeTypes.opra_response_json;
579
- }
580
- }
581
- }
582
- responseArgs.operationResponse = operationResponse;
583
- if (!operationResponse.statusCode.find(sc => sc.start <= statusCode && sc.end >= statusCode)) {
584
- responseArgs.statusCode = operationResponse.statusCode[0].start;
585
- }
586
- }
587
- }
588
- if (!hasBody)
589
- delete responseArgs.contentType;
590
- if (__oprDef?.composition?.startsWith('Entity.')) {
591
- if (context.queryParams.projection)
592
- responseArgs.projection = context.queryParams.projection;
593
- }
594
- this[core_1.kAssetCache].set(response, cacheKey, { ...responseArgs });
595
- }
596
- /** Fix response value according to composition */
597
- const composition = operationResponse?.owner.composition;
598
- if (composition && body != null) {
599
- switch (composition) {
600
- case 'Entity.Create':
601
- case 'Entity.Get':
602
- case 'Entity.FindMany':
603
- case 'Entity.Update': {
604
- if (!(body instanceof common_1.OperationResult)) {
605
- body = new common_1.OperationResult({
606
- payload: body,
607
- });
608
- }
609
- if ((composition === 'Entity.Create' ||
610
- composition === 'Entity.Update') &&
611
- composition &&
612
- body.affected == null) {
613
- body.affected = 1;
614
- }
615
- break;
616
- }
617
- case 'Entity.Delete':
618
- case 'Entity.DeleteMany':
619
- case 'Entity.UpdateMany': {
620
- if (!(body instanceof common_1.OperationResult)) {
621
- body = new common_1.OperationResult({
622
- affected: body,
623
- });
624
- }
625
- body.affected =
626
- typeof body.affected === 'number'
627
- ? body.affected
628
- : typeof body.affected === 'boolean'
629
- ? body.affected
630
- ? 1
631
- : 0
632
- : undefined;
633
- break;
634
- }
635
- default:
636
- break;
637
- }
638
- }
639
- if (responseArgs.contentType &&
640
- responseArgs.contentType !== parsedContentType?.type) {
641
- response.setHeader('content-type', responseArgs.contentType);
642
- }
643
- if (responseArgs.contentType &&
644
- body != null &&
645
- !(body instanceof common_1.OperationResult) &&
646
- type_is_1.default.is(responseArgs.contentType, [common_1.MimeTypes.opra_response_json])) {
647
- body = new common_1.OperationResult({ payload: body });
648
- }
649
- if (hasBody)
650
- responseArgs.body = body;
651
- return responseArgs;
652
- }
653
- _wrapExceptions(exceptions) {
654
- const wrappedErrors = exceptions.map(wrap_exception_js_1.wrapException);
655
- if (!wrappedErrors.length)
656
- wrappedErrors.push(new common_1.InternalServerError());
657
- // Sort errors from fatal to info
658
- wrappedErrors.sort((a, b) => {
659
- const i = common_1.IssueSeverity.Keys.indexOf(a.severity) -
660
- common_1.IssueSeverity.Keys.indexOf(b.severity);
661
- if (i === 0)
662
- return b.status - a.status;
663
- return i;
664
- });
665
- return wrappedErrors;
666
- }
667
- }
668
- exports.HttpHandler = HttpHandler;