@opra/core 1.0.0-alpha.24 → 1.0.0-alpha.25

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.
@@ -278,104 +278,111 @@ class HttpHandler {
278
278
  */
279
279
  async sendResponse(context, responseValue) {
280
280
  if (context.errors.length)
281
- return this.sendErrorResponse(context, context.errors);
281
+ return this._sendErrorResponse(context);
282
282
  const { response } = context;
283
283
  const { document } = this.adapter;
284
- const responseArgs = this._determineResponseArgs(context, responseValue);
285
- const { operationResponse, statusCode } = responseArgs;
286
- let { contentType, body } = responseArgs;
287
- const operationResultType = document.node.getDataType(common_1.OperationResult);
288
- let operationResultEncoder = this[constants_1.kAssetCache].get(operationResultType, 'encode');
289
- if (!operationResultEncoder) {
290
- operationResultEncoder = operationResultType.generateCodec('encode', { ignoreWriteonlyFields: true });
291
- this[constants_1.kAssetCache].set(operationResultType, 'encode', operationResultEncoder);
292
- }
293
- /** Validate response */
294
- if (operationResponse?.type) {
295
- if (!(body == null && statusCode === common_1.HttpStatusCode.NO_CONTENT)) {
296
- /** Generate encoder */
297
- let encode = this[constants_1.kAssetCache].get(operationResponse, 'encode');
298
- if (!encode) {
299
- encode = operationResponse.type.generateCodec('encode', {
300
- partial: operationResponse.partial,
301
- projection: '*',
302
- ignoreWriteonlyFields: true,
303
- });
304
- if (operationResponse) {
305
- if (operationResponse.isArray)
306
- encode = valgen_1.vg.isArray(encode);
307
- this[constants_1.kAssetCache].set(operationResponse, 'encode', encode);
284
+ try {
285
+ const responseArgs = this._determineResponseArgs(context, responseValue);
286
+ const { operationResponse, statusCode } = responseArgs;
287
+ let { contentType, body } = responseArgs;
288
+ const operationResultType = document.node.getDataType(common_1.OperationResult);
289
+ let operationResultEncoder = this[constants_1.kAssetCache].get(operationResultType, 'encode');
290
+ if (!operationResultEncoder) {
291
+ operationResultEncoder = operationResultType.generateCodec('encode', { ignoreWriteonlyFields: true });
292
+ this[constants_1.kAssetCache].set(operationResultType, 'encode', operationResultEncoder);
293
+ }
294
+ /** Validate response */
295
+ if (operationResponse?.type) {
296
+ if (!(body == null && statusCode === common_1.HttpStatusCode.NO_CONTENT)) {
297
+ /** Generate encoder */
298
+ let encode = this[constants_1.kAssetCache].get(operationResponse, 'encode');
299
+ if (!encode) {
300
+ encode = operationResponse.type.generateCodec('encode', {
301
+ partial: operationResponse.partial,
302
+ projection: '*',
303
+ ignoreWriteonlyFields: true,
304
+ onFail: issue => `Response body validation failed: ` + issue.message,
305
+ });
306
+ if (operationResponse) {
307
+ if (operationResponse.isArray)
308
+ encode = valgen_1.vg.isArray(encode);
309
+ this[constants_1.kAssetCache].set(operationResponse, 'encode', encode);
310
+ }
311
+ }
312
+ /** Encode body */
313
+ if (operationResponse.type.extendsFrom(operationResultType)) {
314
+ if (body instanceof common_1.OperationResult)
315
+ body = encode(body);
316
+ else {
317
+ body.payload = encode(body.payload);
318
+ body = operationResultEncoder(body);
319
+ }
308
320
  }
309
- }
310
- /** Encode body */
311
- if (operationResponse.type.extendsFrom(operationResultType)) {
312
- if (body instanceof common_1.OperationResult)
313
- body = encode(body);
314
321
  else {
315
- body.payload = encode(body.payload);
316
- body = operationResultEncoder(body);
322
+ if (body instanceof common_1.OperationResult &&
323
+ contentType &&
324
+ type_is_1.default.is(contentType, [common_1.MimeTypes.opra_response_json])) {
325
+ body.payload = encode(body.payload);
326
+ body = operationResultEncoder(body);
327
+ }
328
+ else {
329
+ body = encode(body);
330
+ }
317
331
  }
318
- }
319
- else {
320
332
  if (body instanceof common_1.OperationResult &&
321
- contentType &&
322
- type_is_1.default.is(contentType, [common_1.MimeTypes.opra_response_json])) {
323
- body.payload = encode(body.payload);
324
- body = operationResultEncoder(body);
333
+ operationResponse.type &&
334
+ operationResponse.type !== document.node.getDataType(common_1.OperationResult)) {
335
+ body.type = operationResponse.type.name ? operationResponse.type.name : '#embedded';
325
336
  }
326
- else
327
- body = encode(body);
328
- }
329
- if (body instanceof common_1.OperationResult &&
330
- operationResponse.type &&
331
- operationResponse.type !== document.node.getDataType(common_1.OperationResult)) {
332
- body.type = operationResponse.type.name ? operationResponse.type.name : '#embedded';
333
337
  }
334
338
  }
335
- }
336
- else if (body != null) {
337
- if (body instanceof common_1.OperationResult) {
338
- body = operationResultEncoder(body);
339
- contentType = common_1.MimeTypes.opra_response_json;
340
- }
341
- else if (Buffer.isBuffer(body))
342
- contentType = common_1.MimeTypes.binary;
343
- else if (typeof body === 'object') {
344
- contentType = contentType || common_1.MimeTypes.json;
345
- if (typeof body.toJSON === 'function')
346
- body = body.toJSON();
339
+ else if (body != null) {
340
+ if (body instanceof common_1.OperationResult) {
341
+ body = operationResultEncoder(body);
342
+ contentType = common_1.MimeTypes.opra_response_json;
343
+ }
344
+ else if (Buffer.isBuffer(body))
345
+ contentType = common_1.MimeTypes.binary;
346
+ else if (typeof body === 'object') {
347
+ contentType = contentType || common_1.MimeTypes.json;
348
+ if (typeof body.toJSON === 'function')
349
+ body = body.toJSON();
350
+ }
351
+ else {
352
+ contentType = contentType || common_1.MimeTypes.text;
353
+ body = String(body);
354
+ }
347
355
  }
348
- else {
349
- contentType = contentType || common_1.MimeTypes.text;
350
- body = String(body);
356
+ /** Set content-type header value if not set */
357
+ if (contentType && contentType !== responseArgs.contentType)
358
+ response.setHeader('content-type', contentType);
359
+ response.status(statusCode);
360
+ if (body == null) {
361
+ response.end();
362
+ return;
351
363
  }
364
+ let x;
365
+ if (Buffer.isBuffer(body) || (0, common_1.isReadableStream)(body))
366
+ x = body;
367
+ else if ((0, common_1.isBlob)(body))
368
+ x = body.stream();
369
+ else if (typeof body === 'object')
370
+ x = JSON.stringify(body);
371
+ else
372
+ x = String(body);
373
+ response.end(x);
352
374
  }
353
- /** Set content-type header value if not set */
354
- if (contentType && contentType !== responseArgs.contentType)
355
- response.setHeader('content-type', contentType);
356
- response.status(statusCode);
357
- if (body == null) {
358
- response.end();
359
- return;
375
+ catch (error) {
376
+ context.errors.push(error);
377
+ return this._sendErrorResponse(context);
360
378
  }
361
- let x;
362
- if (Buffer.isBuffer(body) || (0, common_1.isReadableStream)(body))
363
- x = body;
364
- else if ((0, common_1.isBlob)(body))
365
- x = body.stream();
366
- else if (typeof body === 'object')
367
- x = JSON.stringify(body);
368
- else
369
- x = String(body);
370
- response.end(x);
371
379
  }
372
- async sendErrorResponse(context, errors) {
373
- const { response } = context;
380
+ async _sendErrorResponse(context) {
381
+ const { response, errors } = context;
374
382
  if (response.headersSent) {
375
383
  response.end();
376
384
  return;
377
385
  }
378
- errors = errors || context.errors;
379
386
  const wrappedErrors = errors.map(wrap_exception_1.wrapException);
380
387
  if (!wrappedErrors.length)
381
388
  wrappedErrors.push(new common_1.InternalServerError());
@@ -477,6 +484,8 @@ class HttpHandler {
477
484
  : operationResponse.contentType);
478
485
  if (typeof ct === 'string')
479
486
  responseArgs.contentType = contentType = ct;
487
+ else if (operationResponse.type)
488
+ responseArgs.contentType = common_1.MimeTypes.opra_response_json;
480
489
  }
481
490
  }
482
491
  }
@@ -95,8 +95,10 @@ class MultipartReader extends events_1.EventEmitter {
95
95
  if (!field)
96
96
  throw new common_1.BadRequestError(`Unknown multipart field (${item.field})`);
97
97
  if (item.kind === 'field') {
98
- const codec = field.generateCodec('decode');
99
- item.value = codec(item.value);
98
+ const decode = field.generateCodec('decode');
99
+ item.value = decode(item.value, {
100
+ onFail: issue => `Multipart field (${item.field}) validation failed: ` + issue.message,
101
+ });
100
102
  }
101
103
  else if (item.kind === 'file') {
102
104
  if (field.contentType) {
@@ -108,7 +110,7 @@ class MultipartReader extends events_1.EventEmitter {
108
110
  }
109
111
  }
110
112
  /** if all items received we check for required items */
111
- if (!item && this.mediaType && this.mediaType.multipartFields?.length > 0) {
113
+ if (this._finished && this.mediaType && this.mediaType.multipartFields?.length > 0) {
112
114
  const fieldsLeft = new Set(this.mediaType.multipartFields);
113
115
  for (const x of this._items) {
114
116
  const field = this.mediaType.findMultipartField(x.field);
@@ -173,14 +175,7 @@ class MultipartReader extends events_1.EventEmitter {
173
175
  this._items.forEach(item => {
174
176
  if (item.kind !== 'file')
175
177
  return;
176
- const file = item.storedPath;
177
- promises.push(new Promise(resolve => {
178
- if (file._writeStream.closed)
179
- return resolve();
180
- file._writeStream.once('close', resolve);
181
- })
182
- .then(() => promises_1.default.unlink(file.filepath))
183
- .then(() => 0));
178
+ promises.push(promises_1.default.unlink(item.storedPath));
184
179
  });
185
180
  return Promise.allSettled(promises);
186
181
  }
@@ -274,104 +274,111 @@ export class HttpHandler {
274
274
  */
275
275
  async sendResponse(context, responseValue) {
276
276
  if (context.errors.length)
277
- return this.sendErrorResponse(context, context.errors);
277
+ return this._sendErrorResponse(context);
278
278
  const { response } = context;
279
279
  const { document } = this.adapter;
280
- const responseArgs = this._determineResponseArgs(context, responseValue);
281
- const { operationResponse, statusCode } = responseArgs;
282
- let { contentType, body } = responseArgs;
283
- const operationResultType = document.node.getDataType(OperationResult);
284
- let operationResultEncoder = this[kAssetCache].get(operationResultType, 'encode');
285
- if (!operationResultEncoder) {
286
- operationResultEncoder = operationResultType.generateCodec('encode', { ignoreWriteonlyFields: true });
287
- this[kAssetCache].set(operationResultType, 'encode', operationResultEncoder);
288
- }
289
- /** Validate response */
290
- if (operationResponse?.type) {
291
- if (!(body == null && statusCode === HttpStatusCode.NO_CONTENT)) {
292
- /** Generate encoder */
293
- let encode = this[kAssetCache].get(operationResponse, 'encode');
294
- if (!encode) {
295
- encode = operationResponse.type.generateCodec('encode', {
296
- partial: operationResponse.partial,
297
- projection: '*',
298
- ignoreWriteonlyFields: true,
299
- });
300
- if (operationResponse) {
301
- if (operationResponse.isArray)
302
- encode = vg.isArray(encode);
303
- this[kAssetCache].set(operationResponse, 'encode', encode);
280
+ try {
281
+ const responseArgs = this._determineResponseArgs(context, responseValue);
282
+ const { operationResponse, statusCode } = responseArgs;
283
+ let { contentType, body } = responseArgs;
284
+ const operationResultType = document.node.getDataType(OperationResult);
285
+ let operationResultEncoder = this[kAssetCache].get(operationResultType, 'encode');
286
+ if (!operationResultEncoder) {
287
+ operationResultEncoder = operationResultType.generateCodec('encode', { ignoreWriteonlyFields: true });
288
+ this[kAssetCache].set(operationResultType, 'encode', operationResultEncoder);
289
+ }
290
+ /** Validate response */
291
+ if (operationResponse?.type) {
292
+ if (!(body == null && statusCode === HttpStatusCode.NO_CONTENT)) {
293
+ /** Generate encoder */
294
+ let encode = this[kAssetCache].get(operationResponse, 'encode');
295
+ if (!encode) {
296
+ encode = operationResponse.type.generateCodec('encode', {
297
+ partial: operationResponse.partial,
298
+ projection: '*',
299
+ ignoreWriteonlyFields: true,
300
+ onFail: issue => `Response body validation failed: ` + issue.message,
301
+ });
302
+ if (operationResponse) {
303
+ if (operationResponse.isArray)
304
+ encode = vg.isArray(encode);
305
+ this[kAssetCache].set(operationResponse, 'encode', encode);
306
+ }
307
+ }
308
+ /** Encode body */
309
+ if (operationResponse.type.extendsFrom(operationResultType)) {
310
+ if (body instanceof OperationResult)
311
+ body = encode(body);
312
+ else {
313
+ body.payload = encode(body.payload);
314
+ body = operationResultEncoder(body);
315
+ }
304
316
  }
305
- }
306
- /** Encode body */
307
- if (operationResponse.type.extendsFrom(operationResultType)) {
308
- if (body instanceof OperationResult)
309
- body = encode(body);
310
317
  else {
311
- body.payload = encode(body.payload);
312
- body = operationResultEncoder(body);
318
+ if (body instanceof OperationResult &&
319
+ contentType &&
320
+ typeIs.is(contentType, [MimeTypes.opra_response_json])) {
321
+ body.payload = encode(body.payload);
322
+ body = operationResultEncoder(body);
323
+ }
324
+ else {
325
+ body = encode(body);
326
+ }
313
327
  }
314
- }
315
- else {
316
328
  if (body instanceof OperationResult &&
317
- contentType &&
318
- typeIs.is(contentType, [MimeTypes.opra_response_json])) {
319
- body.payload = encode(body.payload);
320
- body = operationResultEncoder(body);
329
+ operationResponse.type &&
330
+ operationResponse.type !== document.node.getDataType(OperationResult)) {
331
+ body.type = operationResponse.type.name ? operationResponse.type.name : '#embedded';
321
332
  }
322
- else
323
- body = encode(body);
324
- }
325
- if (body instanceof OperationResult &&
326
- operationResponse.type &&
327
- operationResponse.type !== document.node.getDataType(OperationResult)) {
328
- body.type = operationResponse.type.name ? operationResponse.type.name : '#embedded';
329
333
  }
330
334
  }
331
- }
332
- else if (body != null) {
333
- if (body instanceof OperationResult) {
334
- body = operationResultEncoder(body);
335
- contentType = MimeTypes.opra_response_json;
336
- }
337
- else if (Buffer.isBuffer(body))
338
- contentType = MimeTypes.binary;
339
- else if (typeof body === 'object') {
340
- contentType = contentType || MimeTypes.json;
341
- if (typeof body.toJSON === 'function')
342
- body = body.toJSON();
335
+ else if (body != null) {
336
+ if (body instanceof OperationResult) {
337
+ body = operationResultEncoder(body);
338
+ contentType = MimeTypes.opra_response_json;
339
+ }
340
+ else if (Buffer.isBuffer(body))
341
+ contentType = MimeTypes.binary;
342
+ else if (typeof body === 'object') {
343
+ contentType = contentType || MimeTypes.json;
344
+ if (typeof body.toJSON === 'function')
345
+ body = body.toJSON();
346
+ }
347
+ else {
348
+ contentType = contentType || MimeTypes.text;
349
+ body = String(body);
350
+ }
343
351
  }
344
- else {
345
- contentType = contentType || MimeTypes.text;
346
- body = String(body);
352
+ /** Set content-type header value if not set */
353
+ if (contentType && contentType !== responseArgs.contentType)
354
+ response.setHeader('content-type', contentType);
355
+ response.status(statusCode);
356
+ if (body == null) {
357
+ response.end();
358
+ return;
347
359
  }
360
+ let x;
361
+ if (Buffer.isBuffer(body) || isReadableStream(body))
362
+ x = body;
363
+ else if (isBlob(body))
364
+ x = body.stream();
365
+ else if (typeof body === 'object')
366
+ x = JSON.stringify(body);
367
+ else
368
+ x = String(body);
369
+ response.end(x);
348
370
  }
349
- /** Set content-type header value if not set */
350
- if (contentType && contentType !== responseArgs.contentType)
351
- response.setHeader('content-type', contentType);
352
- response.status(statusCode);
353
- if (body == null) {
354
- response.end();
355
- return;
371
+ catch (error) {
372
+ context.errors.push(error);
373
+ return this._sendErrorResponse(context);
356
374
  }
357
- let x;
358
- if (Buffer.isBuffer(body) || isReadableStream(body))
359
- x = body;
360
- else if (isBlob(body))
361
- x = body.stream();
362
- else if (typeof body === 'object')
363
- x = JSON.stringify(body);
364
- else
365
- x = String(body);
366
- response.end(x);
367
375
  }
368
- async sendErrorResponse(context, errors) {
369
- const { response } = context;
376
+ async _sendErrorResponse(context) {
377
+ const { response, errors } = context;
370
378
  if (response.headersSent) {
371
379
  response.end();
372
380
  return;
373
381
  }
374
- errors = errors || context.errors;
375
382
  const wrappedErrors = errors.map(wrapException);
376
383
  if (!wrappedErrors.length)
377
384
  wrappedErrors.push(new InternalServerError());
@@ -473,6 +480,8 @@ export class HttpHandler {
473
480
  : operationResponse.contentType);
474
481
  if (typeof ct === 'string')
475
482
  responseArgs.contentType = contentType = ct;
483
+ else if (operationResponse.type)
484
+ responseArgs.contentType = MimeTypes.opra_response_json;
476
485
  }
477
486
  }
478
487
  }
@@ -91,8 +91,10 @@ export class MultipartReader extends EventEmitter {
91
91
  if (!field)
92
92
  throw new BadRequestError(`Unknown multipart field (${item.field})`);
93
93
  if (item.kind === 'field') {
94
- const codec = field.generateCodec('decode');
95
- item.value = codec(item.value);
94
+ const decode = field.generateCodec('decode');
95
+ item.value = decode(item.value, {
96
+ onFail: issue => `Multipart field (${item.field}) validation failed: ` + issue.message,
97
+ });
96
98
  }
97
99
  else if (item.kind === 'file') {
98
100
  if (field.contentType) {
@@ -104,7 +106,7 @@ export class MultipartReader extends EventEmitter {
104
106
  }
105
107
  }
106
108
  /** if all items received we check for required items */
107
- if (!item && this.mediaType && this.mediaType.multipartFields?.length > 0) {
109
+ if (this._finished && this.mediaType && this.mediaType.multipartFields?.length > 0) {
108
110
  const fieldsLeft = new Set(this.mediaType.multipartFields);
109
111
  for (const x of this._items) {
110
112
  const field = this.mediaType.findMultipartField(x.field);
@@ -169,14 +171,7 @@ export class MultipartReader extends EventEmitter {
169
171
  this._items.forEach(item => {
170
172
  if (item.kind !== 'file')
171
173
  return;
172
- const file = item.storedPath;
173
- promises.push(new Promise(resolve => {
174
- if (file._writeStream.closed)
175
- return resolve();
176
- file._writeStream.once('close', resolve);
177
- })
178
- .then(() => fsPromise.unlink(file.filepath))
179
- .then(() => 0));
174
+ promises.push(fsPromise.unlink(item.storedPath));
180
175
  });
181
176
  return Promise.allSettled(promises);
182
177
  }
@@ -0,0 +1,21 @@
1
+ {
2
+ "BAD_REQUEST": "Bad request",
3
+ "FAILED_DEPENDENCY": "The request failed due to failure of a previous request",
4
+ "FORBIDDEN": "You are not authorized to perform this action",
5
+ "INTERNAL_SERVER_ERROR": "Internal server error",
6
+ "METHOD_NOT_ALLOWED": "Method not allowed",
7
+ "NOT_ACCEPTABLE": "Not acceptable",
8
+ "NOT_FOUND": "Not found",
9
+ "UNAUTHORIZED": "You have not been authenticated to perform this action",
10
+ "UNPROCESSABLE_ENTITY": "Unprocessable entity",
11
+ "REQUEST_VALIDATION": "Request validation failed",
12
+ "RESPONSE_VALIDATION": "Response validation failed",
13
+ "RESOURCE_NOT_AVAILABLE": "Resource is not available or you dont have access",
14
+ "RESOURCE_CONFLICT": "There is already an other {{resource}} resource with same field values ({{fields}})",
15
+ "OPERATION_FORBIDDEN": "The {{resource}} resource does not accept '{{operation}}' operations",
16
+ "ACTION_NOT_FOUND": "The {{resource}} resource doesn't have an action named '{{action}}'",
17
+ "UNKNOWN_FIELD": "Unknown field '{{field}}'",
18
+ "UNACCEPTED_SORT_FIELD": "Field '{{field}}' is not available for sort operation",
19
+ "UNACCEPTED_FILTER_FIELD": "Field '{{field}}' is not available for filter operation",
20
+ "UNACCEPTED_FILTER_OPERATION": "'{{operation}}' for field '{{field}}' is not available for filter operation"
21
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opra/core",
3
- "version": "1.0.0-alpha.24",
3
+ "version": "1.0.0-alpha.25",
4
4
  "description": "Opra schema package",
5
5
  "author": "Panates",
6
6
  "license": "MIT",
@@ -33,7 +33,7 @@
33
33
  "dependencies": {
34
34
  "@browsery/http-parser": "^0.5.8",
35
35
  "@browsery/type-is": "^1.6.18-r2",
36
- "@opra/common": "^1.0.0-alpha.24",
36
+ "@opra/common": "^1.0.0-alpha.25",
37
37
  "accepts": "^1.3.8",
38
38
  "base64-stream": "^1.0.0",
39
39
  "busboy": "^1.6.0",
@@ -61,7 +61,7 @@ export declare class HttpHandler {
61
61
  * @protected
62
62
  */
63
63
  sendResponse(context: HttpContext, responseValue?: any): Promise<void>;
64
- sendErrorResponse(context: HttpContext, errors: any[]): Promise<void>;
64
+ protected _sendErrorResponse(context: HttpContext): Promise<void>;
65
65
  /**
66
66
  *
67
67
  * @param context
@@ -10,7 +10,7 @@ export declare namespace MultipartReader {
10
10
  interface FieldInfo {
11
11
  kind: 'field';
12
12
  field: string;
13
- value?: string;
13
+ value?: any;
14
14
  mimeType?: string;
15
15
  encoding?: string;
16
16
  }