@opra/core 0.5.0 → 0.6.0

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 (56) hide show
  1. package/cjs/adapter/adapter.js +103 -122
  2. package/cjs/adapter/classes/execution-context.host.js +17 -0
  3. package/cjs/adapter/classes/express-request-wrapper.host.js +37 -0
  4. package/cjs/adapter/classes/express-response-wrapper.host.js +56 -0
  5. package/cjs/adapter/classes/http-execution-context.host.js +31 -0
  6. package/cjs/adapter/{metadata-resource.js → classes/metadata.resource.js} +3 -3
  7. package/cjs/adapter/express-adapter.js +6 -104
  8. package/cjs/adapter/http-adapter.js +270 -68
  9. package/cjs/adapter/request-contexts/batch-request-context.js +12 -0
  10. package/cjs/adapter/{query-context.js → request-contexts/request-context.js} +8 -12
  11. package/cjs/adapter/request-contexts/single-request-context.js +15 -0
  12. package/cjs/index.js +2 -1
  13. package/cjs/interfaces/i18n-options.interface.js +2 -0
  14. package/cjs/services/json-collection-service.js +36 -39
  15. package/cjs/services/json-singleton-service.js +9 -10
  16. package/cjs/utils/create-i18n.js +2 -2
  17. package/esm/adapter/adapter.d.ts +13 -44
  18. package/esm/adapter/adapter.js +89 -108
  19. package/esm/adapter/classes/execution-context.host.d.ts +10 -0
  20. package/esm/adapter/classes/execution-context.host.js +13 -0
  21. package/esm/adapter/classes/express-request-wrapper.host.d.ts +19 -0
  22. package/esm/adapter/classes/express-request-wrapper.host.js +33 -0
  23. package/esm/adapter/classes/express-response-wrapper.host.d.ts +22 -0
  24. package/esm/adapter/classes/express-response-wrapper.host.js +52 -0
  25. package/esm/adapter/classes/http-execution-context.host.d.ts +13 -0
  26. package/esm/adapter/classes/http-execution-context.host.js +27 -0
  27. package/esm/adapter/classes/metadata.resource.d.ts +8 -0
  28. package/esm/adapter/{metadata-resource.js → classes/metadata.resource.js} +2 -2
  29. package/esm/adapter/express-adapter.d.ts +1 -1
  30. package/esm/adapter/express-adapter.js +5 -103
  31. package/esm/adapter/http-adapter.d.ts +23 -12
  32. package/esm/adapter/http-adapter.js +248 -46
  33. package/esm/adapter/request-contexts/batch-request-context.d.ts +7 -0
  34. package/esm/adapter/request-contexts/batch-request-context.js +8 -0
  35. package/esm/adapter/request-contexts/request-context.d.ts +22 -0
  36. package/esm/adapter/{query-context.js → request-contexts/request-context.js} +6 -10
  37. package/esm/adapter/request-contexts/single-request-context.d.ts +10 -0
  38. package/esm/adapter/request-contexts/single-request-context.js +11 -0
  39. package/esm/index.d.ts +2 -1
  40. package/esm/index.js +2 -1
  41. package/esm/interfaces/execution-context.interface.d.ts +19 -11
  42. package/esm/interfaces/i18n-options.interface.d.ts +28 -0
  43. package/esm/interfaces/i18n-options.interface.js +1 -0
  44. package/esm/interfaces/resource.interface.d.ts +1 -1
  45. package/esm/services/json-collection-service.d.ts +3 -4
  46. package/esm/services/json-collection-service.js +17 -20
  47. package/esm/services/json-singleton-service.d.ts +3 -3
  48. package/esm/services/json-singleton-service.js +8 -8
  49. package/esm/utils/create-i18n.d.ts +3 -3
  50. package/esm/utils/create-i18n.js +1 -1
  51. package/package.json +7 -8
  52. package/cjs/utils/path-to-tree.js +0 -28
  53. package/esm/adapter/metadata-resource.d.ts +0 -8
  54. package/esm/adapter/query-context.d.ts +0 -24
  55. package/esm/utils/path-to-tree.d.ts +0 -4
  56. package/esm/utils/path-to-tree.js +0 -24
@@ -2,13 +2,10 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.JsonCollectionService = void 0;
4
4
  const tslib_1 = require("tslib");
5
- const lodash_1 = tslib_1.__importDefault(require("lodash"));
5
+ const lodash_1 = require("lodash");
6
6
  const putil_merge_1 = tslib_1.__importDefault(require("putil-merge"));
7
7
  const core_1 = require("@nano-sql/core");
8
- const exception_1 = require("@opra/exception");
9
- const schema_1 = require("@opra/schema");
10
- const url_1 = require("@opra/url");
11
- const path_to_tree_js_1 = require("../utils/path-to-tree.js");
8
+ const common_1 = require("@opra/common");
12
9
  let dbId = 1;
13
10
  const indexingTypes = ['int', 'float', 'number', 'date', 'string'];
14
11
  class JsonCollectionService {
@@ -101,7 +98,7 @@ class JsonCollectionService {
101
98
  }
102
99
  async create(data, options) {
103
100
  if (!data[this.primaryKey])
104
- throw new exception_1.BadRequestError({
101
+ throw new common_1.BadRequestError({
105
102
  message: 'You must provide primary key value'
106
103
  });
107
104
  await this._init();
@@ -111,7 +108,7 @@ class JsonCollectionService {
111
108
  .where([this.primaryKey, '=', keyValue])
112
109
  .exec();
113
110
  if (rows.length)
114
- throw new exception_1.ResourceConflictError(this.resourceName, this.primaryKey);
111
+ throw new common_1.ResourceConflictError(this.resourceName, this.primaryKey);
115
112
  await (0, core_1.nSQL)(this.resourceName).query('upsert', data)
116
113
  .exec();
117
114
  return await this.get(keyValue, options);
@@ -245,16 +242,16 @@ class JsonCollectionService {
245
242
  }
246
243
  }
247
244
  _prepare(query) {
248
- if (query.resource instanceof schema_1.CollectionResourceInfo) {
245
+ if (query.resource instanceof common_1.CollectionResourceInfo) {
249
246
  if (query.dataType !== this.dataType)
250
247
  throw new TypeError(`Query data type (${query.dataType.name}) ` +
251
248
  `differs from JsonCollectionService data type (${this.dataType.name})`);
252
249
  }
253
250
  switch (query.method) {
254
251
  case 'count': {
255
- const options = lodash_1.default.omitBy({
252
+ const options = (0, lodash_1.omitBy)({
256
253
  filter: this._convertFilter(query.filter)
257
- }, lodash_1.default.isNil);
254
+ }, lodash_1.isNil);
258
255
  return {
259
256
  method: query.method,
260
257
  options,
@@ -262,11 +259,11 @@ class JsonCollectionService {
262
259
  };
263
260
  }
264
261
  case 'create': {
265
- const options = lodash_1.default.omitBy({
262
+ const options = (0, lodash_1.omitBy)({
266
263
  pick: query.pick,
267
264
  omit: query.omit,
268
265
  include: query.include
269
- }, lodash_1.default.isNil);
266
+ }, lodash_1.isNil);
270
267
  const { data } = query;
271
268
  return {
272
269
  method: query.method,
@@ -277,11 +274,11 @@ class JsonCollectionService {
277
274
  }
278
275
  case 'get': {
279
276
  if (query.kind === 'CollectionGetQuery') {
280
- const options = lodash_1.default.omitBy({
277
+ const options = (0, lodash_1.omitBy)({
281
278
  pick: query.pick,
282
279
  omit: query.omit,
283
280
  include: query.include
284
- }, lodash_1.default.isNil);
281
+ }, lodash_1.isNil);
285
282
  const keyValue = query.keyValue;
286
283
  return {
287
284
  method: query.method,
@@ -297,10 +294,10 @@ class JsonCollectionService {
297
294
  }
298
295
  case 'search': {
299
296
  if (query.distinct)
300
- throw new exception_1.MethodNotAllowedError({
297
+ throw new common_1.MethodNotAllowedError({
301
298
  message: '$distinct parameter is not supported by JsonDataService'
302
299
  });
303
- const options = lodash_1.default.omitBy({
300
+ const options = (0, lodash_1.omitBy)({
304
301
  pick: query.pick,
305
302
  omit: query.omit,
306
303
  include: query.include,
@@ -310,7 +307,7 @@ class JsonCollectionService {
310
307
  limit: query.limit,
311
308
  offset: query.skip,
312
309
  count: query.count,
313
- }, lodash_1.default.isNil);
310
+ }, lodash_1.isNil);
314
311
  return {
315
312
  method: query.method,
316
313
  options,
@@ -318,11 +315,11 @@ class JsonCollectionService {
318
315
  };
319
316
  }
320
317
  case 'update': {
321
- const options = lodash_1.default.omitBy({
318
+ const options = (0, lodash_1.omitBy)({
322
319
  pick: query.pick,
323
320
  omit: query.omit,
324
321
  include: query.include
325
- }, lodash_1.default.isNil);
322
+ }, lodash_1.isNil);
326
323
  const { data } = query;
327
324
  const keyValue = query.keyValue;
328
325
  return {
@@ -334,9 +331,9 @@ class JsonCollectionService {
334
331
  };
335
332
  }
336
333
  case 'updateMany': {
337
- const options = lodash_1.default.omitBy({
334
+ const options = (0, lodash_1.omitBy)({
338
335
  filter: this._convertFilter(query.filter)
339
- }, lodash_1.default.isNil);
336
+ }, lodash_1.isNil);
340
337
  const { data } = query;
341
338
  return {
342
339
  method: query.method,
@@ -355,9 +352,9 @@ class JsonCollectionService {
355
352
  };
356
353
  }
357
354
  case 'deleteMany': {
358
- const options = lodash_1.default.omitBy({
355
+ const options = (0, lodash_1.omitBy)({
359
356
  filter: this._convertFilter(query.filter)
360
- }, lodash_1.default.isNil);
357
+ }, lodash_1.isNil);
361
358
  return {
362
359
  method: query.method,
363
360
  options,
@@ -379,7 +376,7 @@ class JsonCollectionService {
379
376
  if ((((!pick && !f.exclusive) || pick?.[kl])) || include?.[kl]) {
380
377
  const fieldType = document.getDataType(f.type);
381
378
  const subPath = (path ? path + '.' : '') + f.name;
382
- if (fieldType instanceof schema_1.ComplexType) {
379
+ if (fieldType instanceof common_1.ComplexType) {
383
380
  processDataType(fieldType, subPath, typeof pick?.[kl] === 'object' ? pick?.[kl] : undefined, typeof omit?.[kl] === 'object' ? omit?.[kl] : undefined, typeof include?.[kl] === 'object' ? include?.[kl] : undefined);
384
381
  continue;
385
382
  }
@@ -387,16 +384,16 @@ class JsonCollectionService {
387
384
  }
388
385
  }
389
386
  };
390
- processDataType(this.dataType, '', (args.pick ? (0, path_to_tree_js_1.pathToTree)(args.pick, true) : undefined), (args.omit ? (0, path_to_tree_js_1.pathToTree)(args.omit, true) : undefined), (args.include ? (0, path_to_tree_js_1.pathToTree)(args.include, true) : undefined));
387
+ processDataType(this.dataType, '', (args.pick ? (0, common_1.pathToTree)(args.pick, true) : undefined), (args.omit ? (0, common_1.pathToTree)(args.omit, true) : undefined), (args.include ? (0, common_1.pathToTree)(args.include, true) : undefined));
391
388
  return result;
392
389
  }
393
390
  _convertFilter(str) {
394
391
  const ast = typeof str === 'string'
395
- ? (0, url_1.$parse)(str)
392
+ ? (0, common_1.parseFilter)(str)
396
393
  : str;
397
- if (!ast || !(ast instanceof url_1.Expression))
394
+ if (!ast || !(ast instanceof common_1.Expression))
398
395
  return ast;
399
- if (ast instanceof url_1.ComparisonExpression) {
396
+ if (ast instanceof common_1.ComparisonExpression) {
400
397
  const left = this._convertFilter(ast.left);
401
398
  const right = this._convertFilter(ast.right);
402
399
  switch (ast.op) {
@@ -424,21 +421,21 @@ class JsonCollectionService {
424
421
  throw new Error(`ComparisonExpression operator (${ast.op}) not implemented yet`);
425
422
  }
426
423
  }
427
- if (ast instanceof url_1.QualifiedIdentifier) {
424
+ if (ast instanceof common_1.QualifiedIdentifier) {
428
425
  return ast.value;
429
426
  }
430
- if (ast instanceof url_1.NumberLiteral ||
431
- ast instanceof url_1.StringLiteral ||
432
- ast instanceof url_1.BooleanLiteral ||
433
- ast instanceof url_1.NullLiteral ||
434
- ast instanceof url_1.DateLiteral ||
435
- ast instanceof url_1.TimeLiteral) {
427
+ if (ast instanceof common_1.NumberLiteral ||
428
+ ast instanceof common_1.StringLiteral ||
429
+ ast instanceof common_1.BooleanLiteral ||
430
+ ast instanceof common_1.NullLiteral ||
431
+ ast instanceof common_1.DateLiteral ||
432
+ ast instanceof common_1.TimeLiteral) {
436
433
  return ast.value;
437
434
  }
438
- if (ast instanceof url_1.ArrayExpression) {
435
+ if (ast instanceof common_1.ArrayExpression) {
439
436
  return ast.items.map(item => this._convertFilter(item));
440
437
  }
441
- if (ast instanceof url_1.LogicalExpression) {
438
+ if (ast instanceof common_1.LogicalExpression) {
442
439
  return ast.items.map(item => this._convertFilter(item))
443
440
  .reduce((a, v) => {
444
441
  if (a.length)
@@ -447,10 +444,10 @@ class JsonCollectionService {
447
444
  return a;
448
445
  }, []);
449
446
  }
450
- if (ast instanceof url_1.ArrayExpression) {
447
+ if (ast instanceof common_1.ArrayExpression) {
451
448
  return ast.items.map(item => this._convertFilter(item));
452
449
  }
453
- if (ast instanceof url_1.ParenthesesExpression) {
450
+ if (ast instanceof common_1.ParenthesesExpression) {
454
451
  return this._convertFilter(ast.expression);
455
452
  }
456
453
  throw new Error(`${ast.kind} is not implemented yet`);
@@ -1,9 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.JsonSingletonService = void 0;
4
- const tslib_1 = require("tslib");
5
- const lodash_1 = tslib_1.__importDefault(require("lodash"));
6
- const schema_1 = require("@opra/schema");
4
+ const lodash_1 = require("lodash");
5
+ const common_1 = require("@opra/common");
7
6
  class JsonSingletonService {
8
7
  dataType;
9
8
  _data;
@@ -24,18 +23,18 @@ class JsonSingletonService {
24
23
  return this._data;
25
24
  }
26
25
  _prepare(query) {
27
- if (query.resource instanceof schema_1.SingletonResourceInfo) {
26
+ if (query.resource instanceof common_1.SingletonResourceInfo) {
28
27
  if (query.dataType !== this.dataType)
29
28
  throw new TypeError(`Query data type (${query.dataType.name}) ` +
30
29
  `differs from JsonCollectionService data type (${this.dataType.name})`);
31
30
  }
32
31
  switch (query.method) {
33
32
  case 'create': {
34
- const options = lodash_1.default.omitBy({
33
+ const options = (0, lodash_1.omitBy)({
35
34
  pick: query.pick,
36
35
  omit: query.omit,
37
36
  include: query.include
38
- }, lodash_1.default.isNil);
37
+ }, lodash_1.isNil);
39
38
  const { data } = query;
40
39
  return {
41
40
  method: query.method,
@@ -46,11 +45,11 @@ class JsonSingletonService {
46
45
  }
47
46
  case 'get': {
48
47
  if (query.kind === 'CollectionGetQuery') {
49
- const options = lodash_1.default.omitBy({
48
+ const options = (0, lodash_1.omitBy)({
50
49
  pick: query.pick,
51
50
  omit: query.omit,
52
51
  include: query.include
53
- }, lodash_1.default.isNil);
52
+ }, lodash_1.isNil);
54
53
  const keyValue = query.keyValue;
55
54
  return {
56
55
  method: query.method,
@@ -65,11 +64,11 @@ class JsonSingletonService {
65
64
  break;
66
65
  }
67
66
  case 'update': {
68
- const options = lodash_1.default.omitBy({
67
+ const options = (0, lodash_1.omitBy)({
69
68
  pick: query.pick,
70
69
  omit: query.omit,
71
70
  include: query.include
72
- }, lodash_1.default.isNil);
71
+ }, lodash_1.isNil);
73
72
  const { data } = query;
74
73
  const keyValue = query.keyValue;
75
74
  return {
@@ -3,14 +3,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createI18n = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const path_1 = tslib_1.__importDefault(require("path"));
6
- const i18n_1 = require("@opra/i18n");
6
+ const common_1 = require("@opra/common");
7
7
  const get_caller_file_util_js_1 = require("./get-caller-file.util.js");
8
8
  async function createI18n(options) {
9
9
  const opts = {
10
10
  ...options,
11
11
  };
12
12
  delete opts.resourceDirs;
13
- const instance = i18n_1.I18n.createInstance(opts);
13
+ const instance = common_1.I18n.createInstance(opts);
14
14
  await instance.init();
15
15
  await instance.loadResourceDir(path_1.default.resolve((0, get_caller_file_util_js_1.getCallerFile)(), '../../../i18n'));
16
16
  if (options?.resourceDirs)
@@ -1,62 +1,31 @@
1
- import { ResponsiveMap } from '@opra/common';
2
- import { OpraException } from '@opra/exception';
3
- import { FallbackLng, I18n, LanguageResource } from '@opra/i18n';
4
- import { CollectionGetQuery, CollectionResourceInfo, ComplexType, DataType, FieldGetQuery, OpraDocument, ResourceInfo, SingletonGetQuery, SingletonResourceInfo } from '@opra/schema';
1
+ import { Task } from 'power-tasks';
2
+ import { CollectionGetQuery, CollectionResourceInfo, ComplexType, DataType, FieldGetQuery, I18n, OpraDocument, OpraException, ResourceInfo, ResponsiveMap, SingletonGetQuery, SingletonResourceInfo } from '@opra/common';
5
3
  import { IExecutionContext } from '../interfaces/execution-context.interface.js';
6
- import { QueryContext } from './query-context.js';
4
+ import { I18nOptions } from '../interfaces/i18n-options.interface.js';
5
+ import { RequestContext } from './request-contexts/request-context.js';
6
+ import { SingleRequestContext } from './request-contexts/single-request-context.js';
7
7
  export declare namespace OpraAdapter {
8
- type UserContextResolver = (args: {
9
- executionContext: IExecutionContext;
10
- isBatch: boolean;
11
- }) => object | Promise<object>;
8
+ type UserContextResolver = (executionContext: IExecutionContext) => object | Promise<object>;
12
9
  interface Options {
13
10
  i18n?: I18n | I18nOptions | (() => Promise<I18n>);
14
11
  userContext?: UserContextResolver;
15
12
  }
16
- interface I18nOptions {
17
- /**
18
- * Language to use
19
- * @default undefined
20
- */
21
- lng?: string;
22
- /**
23
- * Language to use if translations in user language are not available.
24
- * @default 'dev'
25
- */
26
- fallbackLng?: false | FallbackLng;
27
- /**
28
- * Default namespace used if not passed to translation function
29
- * @default 'translation'
30
- */
31
- defaultNS?: string;
32
- /**
33
- * Resources to initialize with
34
- * @default undefined
35
- */
36
- resources?: LanguageResource;
37
- /**
38
- * Resource directories to initialize with (if not using loading or not appending using addResourceBundle)
39
- * @default undefined
40
- */
41
- resourceDirs?: string[];
42
- }
43
13
  }
44
14
  export declare abstract class OpraAdapter<TExecutionContext extends IExecutionContext> {
45
15
  readonly document: OpraDocument;
46
- protected i18n: I18n;
47
16
  protected userContextResolver?: OpraAdapter.UserContextResolver;
48
17
  protected _internalResources: ResponsiveMap<string, ResourceInfo>;
18
+ i18n: I18n;
49
19
  constructor(document: OpraDocument);
50
- protected abstract prepareRequests(executionContext: TExecutionContext): QueryContext[];
51
- protected abstract sendResponse(executionContext: TExecutionContext, queryContexts: QueryContext[]): Promise<void>;
20
+ protected abstract parse(executionContext: TExecutionContext): Promise<RequestContext>;
21
+ protected abstract sendResponse(executionContext: TExecutionContext, requestContext: RequestContext): Promise<void>;
52
22
  protected abstract sendError(executionContext: TExecutionContext, error: OpraException): Promise<void>;
53
- protected abstract isBatch(executionContext: TExecutionContext): boolean;
54
23
  protected handler(executionContext: TExecutionContext): Promise<void>;
55
- protected _resourcePrepare(resource: ResourceInfo, context: QueryContext): Promise<void>;
56
- protected _resourceExecute(document: OpraDocument, resource: ResourceInfo, context: QueryContext): Promise<void>;
24
+ protected _requestContextToTask(executionContext: TExecutionContext, requestContext: RequestContext): Task;
57
25
  protected _init(options?: OpraAdapter.Options): Promise<void>;
58
- protected _collectionResourceExecute(document: OpraDocument, resource: CollectionResourceInfo, context: QueryContext): Promise<any>;
59
- protected _singletonResourceExecute(document: OpraDocument, resource: SingletonResourceInfo, context: QueryContext): Promise<any>;
26
+ protected _executeResourceQuery(document: OpraDocument, resource: ResourceInfo, context: SingleRequestContext): Promise<void>;
27
+ protected _executeCollectionResource(document: OpraDocument, resource: CollectionResourceInfo, context: SingleRequestContext): Promise<any>;
28
+ protected _executeSingletonResource(document: OpraDocument, resource: SingletonResourceInfo, context: SingleRequestContext): Promise<any>;
60
29
  protected _pathWalkThrough(query: CollectionGetQuery | SingletonGetQuery | FieldGetQuery, dataType: ComplexType, value: any, parentPath: string): Promise<{
61
30
  value?: any;
62
31
  dataType?: DataType;
@@ -1,81 +1,29 @@
1
+ import { Task } from 'power-tasks';
1
2
  import { AsyncEventEmitter } from 'strict-typed-events';
2
- import { HttpHeaders, ResponsiveMap } from '@opra/common';
3
- import { FailedDependencyError, ForbiddenError, ResourceNotFoundError, wrapException } from '@opra/exception';
4
- import { I18n, translate } from '@opra/i18n';
5
- import { CollectionCountQuery, CollectionResourceInfo, ComplexType, SingletonResourceInfo } from '@opra/schema';
3
+ import { CollectionCountQuery, CollectionResourceInfo, CollectionSearchQuery, ComplexType, FailedDependencyError, ForbiddenError, HttpHeaders, I18n, InternalServerError, ResourceNotFoundError, ResponsiveMap, SingletonResourceInfo, translate, wrapException } from '@opra/common';
6
4
  import { createI18n } from '../utils/create-i18n.js';
7
- import { MetadataResource } from './metadata-resource.js';
5
+ import { MetadataResource } from './classes/metadata.resource.js';
6
+ import { BatchRequestContext } from './request-contexts/batch-request-context.js';
7
+ import { SingleRequestContext } from './request-contexts/single-request-context.js';
8
+ const noOp = () => void 0;
8
9
  export class OpraAdapter {
9
10
  document;
10
- i18n;
11
11
  userContextResolver;
12
- // protected _metadataResource: SingletonResourceInfo;
13
12
  _internalResources = new ResponsiveMap();
13
+ i18n;
14
14
  constructor(document) {
15
15
  this.document = document;
16
16
  }
17
17
  async handler(executionContext) {
18
- let queryContexts;
19
- let userContext;
18
+ let requestContext;
20
19
  let failed = false;
21
20
  try {
22
- queryContexts = this.prepareRequests(executionContext);
23
- let stop = false;
24
- // Read requests can be executed simultaneously, write request should be executed one by one
25
- let promises;
26
- let exclusive = false;
27
- for (const context of queryContexts) {
28
- exclusive = exclusive || context.query.operation !== 'read';
29
- // Wait previous read requests before executing update request
30
- if (exclusive && promises) {
31
- await Promise.allSettled(promises);
32
- promises = undefined;
33
- }
34
- // If previous request in bucket had an error and executed an update
35
- // we do not execute next requests
36
- if (stop) {
37
- context.errors.push(new FailedDependencyError());
38
- continue;
39
- }
40
- try {
41
- const promise = (async () => {
42
- // if (context.query.method === 'metadata') {
43
- // await this._getSchemaExecute(context); //todo
44
- // return;
45
- // }
46
- const resource = context.query.resource;
47
- await this._resourcePrepare(resource, context);
48
- if (this.userContextResolver && !userContext)
49
- userContext = this.userContextResolver({
50
- executionContext,
51
- isBatch: this.isBatch(executionContext)
52
- });
53
- context.userContext = userContext;
54
- await this._resourceExecute(this.document, resource, context);
55
- })().catch(e => {
56
- context.errors.push(e);
57
- });
58
- if (exclusive)
59
- await promise;
60
- else {
61
- promises = promises || [];
62
- promises.push(promise);
63
- }
64
- // todo execute sub property queries
65
- }
66
- catch (e) {
67
- context.errors.unshift(e);
68
- }
69
- if (context.errors.length) {
70
- // noinspection SuspiciousTypeOfGuard
71
- context.errors = context.errors.map(e => wrapException(e));
72
- if (exclusive)
73
- stop = stop || !!context.errors.find(e => !(e.issue.severity === 'warning' || e.issue.severity === 'info'));
74
- }
75
- }
76
- if (promises)
77
- await Promise.allSettled(promises);
78
- await this.sendResponse(executionContext, queryContexts);
21
+ if (this.userContextResolver)
22
+ executionContext.userContext = this.userContextResolver(executionContext);
23
+ requestContext = await this.parse(executionContext);
24
+ const task = this._requestContextToTask(executionContext, requestContext);
25
+ await task.toPromise().catch(noOp);
26
+ await this.sendResponse(executionContext, requestContext);
79
27
  }
80
28
  catch (e) {
81
29
  failed = true;
@@ -86,47 +34,49 @@ export class OpraAdapter {
86
34
  if (executionContext instanceof AsyncEventEmitter) {
87
35
  await executionContext
88
36
  .emitAsyncSerial('finish', {
89
- userContext,
37
+ context: executionContext,
90
38
  failed
91
39
  }).catch();
92
40
  }
93
41
  }
94
42
  }
95
- async _resourcePrepare(resource, context) {
96
- const { query } = context;
97
- const fn = resource.metadata['pre_' + query.method];
98
- if (fn && typeof fn === 'function') {
99
- await fn(context);
43
+ _requestContextToTask(executionContext, requestContext) {
44
+ if (requestContext instanceof BatchRequestContext) {
45
+ const children = requestContext.queries.map(q => this._requestContextToTask(executionContext, q));
46
+ return new Task(children, { bail: true });
100
47
  }
101
- }
102
- async _resourceExecute(document, resource, context) {
103
- if (resource instanceof CollectionResourceInfo) {
104
- const { query } = context;
105
- if (query.kind === 'SearchCollectionQuery') {
106
- const promises = [];
107
- let search;
108
- promises.push(this._collectionResourceExecute(document, resource, context)
109
- .then(v => search = v));
110
- if (query.count && resource.metadata.count) {
111
- const ctx = {
112
- query: new CollectionCountQuery(query.resource, { filter: query.filter }),
113
- resultPath: ''
114
- };
115
- Object.setPrototypeOf(ctx, context);
116
- promises.push(this._collectionResourceExecute(document, resource, ctx));
48
+ else if (requestContext instanceof SingleRequestContext) {
49
+ const { query } = requestContext;
50
+ const task = new Task(async () => {
51
+ if (query.resource) {
52
+ const { resource } = query;
53
+ // call pre_xxx method
54
+ const fn = resource.metadata['pre_' + query.method];
55
+ if (fn && typeof fn === 'function') {
56
+ await fn(requestContext);
57
+ }
58
+ await this._executeResourceQuery(this.document, resource, requestContext);
59
+ // todo execute sub property queries
60
+ return;
117
61
  }
118
- await Promise.all(promises);
119
- context.response = search;
120
- return;
121
- }
122
- context.response = await this._collectionResourceExecute(document, resource, context);
123
- return;
124
- }
125
- else if (resource instanceof SingletonResourceInfo) {
126
- context.response = await this._singletonResourceExecute(document, resource, context);
127
- return;
62
+ throw new InternalServerError('Not implemented yet');
63
+ }, {
64
+ id: requestContext.contentId,
65
+ exclusive: query.operation !== 'read'
66
+ });
67
+ task.on('finish', () => {
68
+ if (task.error) {
69
+ if (task.failedDependencies)
70
+ requestContext.errors.push(new FailedDependencyError());
71
+ else
72
+ requestContext.errors.push(wrapException(task.error));
73
+ return;
74
+ }
75
+ });
76
+ return task;
128
77
  }
129
- throw new Error(`Executing "${resource.kind}" has not been implemented yet`);
78
+ /* istanbul ignore next */
79
+ throw new TypeError('Invalid request context instance');
130
80
  }
131
81
  async _init(options) {
132
82
  if (options?.i18n instanceof I18n)
@@ -158,7 +108,39 @@ export class OpraAdapter {
158
108
  }
159
109
  }
160
110
  }
161
- async _collectionResourceExecute(document, resource, context) {
111
+ async _executeResourceQuery(document, resource, context) {
112
+ if (resource instanceof CollectionResourceInfo) {
113
+ const { query } = context;
114
+ if (query instanceof CollectionSearchQuery) {
115
+ const promises = [];
116
+ let search;
117
+ promises.push(this._executeCollectionResource(document, resource, context)
118
+ .then(v => search = v));
119
+ if (query.count && resource.metadata.count) {
120
+ const ctx = {
121
+ query: new CollectionCountQuery(query.resource, { filter: query.filter }),
122
+ resultPath: ''
123
+ };
124
+ Object.setPrototypeOf(ctx, context);
125
+ promises.push(this._executeCollectionResource(document, resource, ctx)
126
+ .then(r => {
127
+ context.responseHeaders[HttpHeaders.X_Opra_Count] = r;
128
+ }));
129
+ }
130
+ await Promise.all(promises);
131
+ context.response = search;
132
+ return;
133
+ }
134
+ context.response = await this._executeCollectionResource(document, resource, context);
135
+ return;
136
+ }
137
+ else if (resource instanceof SingletonResourceInfo) {
138
+ context.response = await this._executeSingletonResource(document, resource, context);
139
+ return;
140
+ }
141
+ throw new Error(`Executing "${resource.kind}" has not been implemented yet`);
142
+ }
143
+ async _executeCollectionResource(document, resource, context) {
162
144
  const method = context.query.method;
163
145
  const resolverInfo = resource.metadata[method];
164
146
  if (!(resolverInfo && resolverInfo.handler))
@@ -175,13 +157,12 @@ export class OpraAdapter {
175
157
  result = Array.isArray(result) ? result[0] : result;
176
158
  if (result)
177
159
  context.status = 201;
178
- context.responseHeaders.set(HttpHeaders.X_Opra_DataType, resource.dataType.name);
160
+ context.responseHeaders[HttpHeaders.X_Opra_DataType] = resource.dataType.name;
179
161
  return result;
180
162
  }
181
163
  case 'count': {
182
164
  const query = context.query;
183
165
  result = await resolverInfo.handler(context, query);
184
- context.responseHeaders.set(HttpHeaders.X_Opra_Count, result);
185
166
  return result;
186
167
  }
187
168
  case 'get': {
@@ -194,14 +175,14 @@ export class OpraAdapter {
194
175
  if (v.value === undefined)
195
176
  throw new ResourceNotFoundError(v.path);
196
177
  if (v.dataType)
197
- context.responseHeaders.set(HttpHeaders.X_Opra_DataType, v.dataType.name);
178
+ context.responseHeaders[HttpHeaders.X_Opra_DataType] = v.dataType.name;
198
179
  return v.value;
199
180
  }
200
181
  case 'search': {
201
182
  const query = context.query;
202
183
  result = await resolverInfo.handler(context, query);
203
184
  const items = Array.isArray(result) ? result : (context.response ? [result] : []);
204
- context.responseHeaders.set(HttpHeaders.X_Opra_DataType, resource.dataType.name);
185
+ context.responseHeaders[HttpHeaders.X_Opra_DataType] = resource.dataType.name;
205
186
  return items;
206
187
  }
207
188
  case 'update': {
@@ -210,7 +191,7 @@ export class OpraAdapter {
210
191
  result = Array.isArray(result) ? result[0] : result;
211
192
  if (!result)
212
193
  throw new ResourceNotFoundError(resource.name, query.keyValue);
213
- context.responseHeaders.set(HttpHeaders.X_Opra_DataType, resource.dataType.name);
194
+ context.responseHeaders[HttpHeaders.X_Opra_DataType] = resource.dataType.name;
214
195
  return result;
215
196
  }
216
197
  case 'delete':
@@ -247,7 +228,7 @@ export class OpraAdapter {
247
228
  }
248
229
  }
249
230
  }
250
- async _singletonResourceExecute(document, resource, context) {
231
+ async _executeSingletonResource(document, resource, context) {
251
232
  const method = context.query.method;
252
233
  const resolverInfo = resource.metadata[method];
253
234
  if (!(resolverInfo && resolverInfo.handler))
@@ -268,7 +249,7 @@ export class OpraAdapter {
268
249
  if (v.value === undefined)
269
250
  throw new ResourceNotFoundError(v.path);
270
251
  if (v.dataType)
271
- context.responseHeaders.set(HttpHeaders.X_Opra_DataType, v.dataType.name);
252
+ context.responseHeaders[HttpHeaders.X_Opra_DataType] = v.dataType.name;
272
253
  return v.value;
273
254
  }
274
255
  }
@@ -286,7 +267,7 @@ export class OpraAdapter {
286
267
  }
287
268
  if (method === 'create')
288
269
  context.status = 201;
289
- context.responseHeaders.set(HttpHeaders.X_Opra_DataType, resource.dataType.name);
270
+ context.responseHeaders[HttpHeaders.X_Opra_DataType] = resource.dataType.name;
290
271
  return result;
291
272
  }
292
273
  async _pathWalkThrough(query, dataType, value, parentPath) {