@opra/core 0.0.8 → 0.0.9

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 (69) hide show
  1. package/cjs/exception/api-exception.js +30 -33
  2. package/cjs/exception/errors/bad-request.error.js +6 -3
  3. package/cjs/exception/errors/failed-dependency.error.js +6 -3
  4. package/cjs/exception/errors/forbidden.error.js +6 -3
  5. package/cjs/exception/errors/internal-server.error.js +9 -4
  6. package/cjs/exception/errors/method-not-allowed.error.js +6 -3
  7. package/cjs/exception/errors/not-found.error.js +6 -3
  8. package/cjs/exception/errors/unauthorized.error.js +6 -3
  9. package/cjs/exception/errors/unprocessable-entity.error.js +6 -3
  10. package/cjs/exception/wrap-error.js +1 -1
  11. package/cjs/implementation/adapter/adapter.js +29 -26
  12. package/cjs/implementation/adapter/express-adapter.js +40 -9
  13. package/cjs/implementation/adapter/http-adapter.js +27 -34
  14. package/cjs/implementation/{execution-context.js → query-context.js} +18 -22
  15. package/cjs/implementation/resource/entity-resource-handler.js +16 -12
  16. package/cjs/implementation/resource/resource-handler.js +1 -1
  17. package/cjs/implementation/schema-generator.js +38 -51
  18. package/cjs/index.js +3 -4
  19. package/cjs/interfaces/{http-context.interface.js → execution-context.interface.js} +0 -0
  20. package/cjs/interfaces/{execution-query.interface.js → query.interface.js} +16 -16
  21. package/cjs/utils/internal-data-types.js +54 -17
  22. package/esm/exception/api-exception.d.ts +3 -2
  23. package/esm/exception/api-exception.js +30 -33
  24. package/esm/exception/errors/bad-request.error.d.ts +2 -1
  25. package/esm/exception/errors/bad-request.error.js +6 -3
  26. package/esm/exception/errors/failed-dependency.error.d.ts +2 -1
  27. package/esm/exception/errors/failed-dependency.error.js +6 -3
  28. package/esm/exception/errors/forbidden.error.d.ts +2 -1
  29. package/esm/exception/errors/forbidden.error.js +6 -3
  30. package/esm/exception/errors/internal-server.error.d.ts +2 -1
  31. package/esm/exception/errors/internal-server.error.js +8 -4
  32. package/esm/exception/errors/method-not-allowed.error.d.ts +2 -1
  33. package/esm/exception/errors/method-not-allowed.error.js +6 -3
  34. package/esm/exception/errors/not-found.error.d.ts +2 -1
  35. package/esm/exception/errors/not-found.error.js +6 -3
  36. package/esm/exception/errors/unauthorized.error.d.ts +2 -1
  37. package/esm/exception/errors/unauthorized.error.js +6 -3
  38. package/esm/exception/errors/unprocessable-entity.error.d.ts +2 -1
  39. package/esm/exception/errors/unprocessable-entity.error.js +6 -3
  40. package/esm/exception/wrap-error.js +1 -1
  41. package/esm/implementation/adapter/adapter.d.ts +22 -14
  42. package/esm/implementation/adapter/adapter.js +29 -26
  43. package/esm/implementation/adapter/express-adapter.d.ts +2 -2
  44. package/esm/implementation/adapter/express-adapter.js +40 -9
  45. package/esm/implementation/adapter/http-adapter.d.ts +11 -12
  46. package/esm/implementation/adapter/http-adapter.js +27 -34
  47. package/esm/implementation/query-context.d.ts +32 -0
  48. package/esm/implementation/{execution-context.js → query-context.js} +15 -18
  49. package/esm/implementation/resource/container-resource-handler.d.ts +2 -2
  50. package/esm/implementation/resource/entity-resource-handler.d.ts +3 -3
  51. package/esm/implementation/resource/entity-resource-handler.js +16 -12
  52. package/esm/implementation/resource/resource-handler.d.ts +3 -3
  53. package/esm/implementation/resource/resource-handler.js +1 -1
  54. package/esm/implementation/schema-generator.js +38 -51
  55. package/esm/index.d.ts +3 -4
  56. package/esm/index.js +3 -4
  57. package/esm/interfaces/execution-context.interface.d.ts +39 -0
  58. package/esm/interfaces/{http-context.interface.js → execution-context.interface.js} +0 -0
  59. package/esm/interfaces/{execution-query.interface.d.ts → query.interface.d.ts} +31 -25
  60. package/esm/interfaces/{execution-query.interface.js → query.interface.js} +15 -15
  61. package/esm/services/entity-resource-controller.d.ts +11 -11
  62. package/esm/utils/internal-data-types.d.ts +2 -1
  63. package/esm/utils/internal-data-types.js +53 -16
  64. package/package.json +6 -5
  65. package/cjs/interfaces/user-context.interface.js +0 -2
  66. package/esm/implementation/execution-context.d.ts +0 -42
  67. package/esm/interfaces/http-context.interface.d.ts +0 -23
  68. package/esm/interfaces/user-context.interface.d.ts +0 -3
  69. package/esm/interfaces/user-context.interface.js +0 -1
@@ -4,5 +4,6 @@ import { ApiException, ErrorResponse } from '../api-exception.js';
4
4
  * The server has encountered a situation it does not know how to handle.
5
5
  */
6
6
  export declare class InternalServerError extends ApiException {
7
- constructor(response?: ErrorResponse, cause?: Error);
7
+ constructor(response?: string | ErrorResponse | Error, cause?: Error);
8
+ protected _initResponse(response: Partial<ErrorResponse>): void;
8
9
  }
@@ -1,3 +1,4 @@
1
+ import _ from 'lodash';
1
2
  import { translate } from '@opra/i18n';
2
3
  import { HttpStatus } from '../../enums/index.js';
3
4
  import { ApiException } from '../api-exception.js';
@@ -7,12 +8,15 @@ import { ApiException } from '../api-exception.js';
7
8
  */
8
9
  export class InternalServerError extends ApiException {
9
10
  constructor(response, cause) {
10
- super({
11
+ super(response, cause);
12
+ this.status = HttpStatus.INTERNAL_SERVER_ERROR;
13
+ }
14
+ _initResponse(response) {
15
+ super._initResponse({
11
16
  message: translate('error:INTERNAL_SERVER_ERROR', 'Internal server error'),
12
17
  severity: 'error',
13
18
  code: 'INTERNAL_SERVER_ERROR',
14
- ...response
15
- }, cause);
16
- this.status = HttpStatus.INTERNAL_SERVER_ERROR;
19
+ ..._.omitBy(response, _.isNil)
20
+ });
17
21
  }
18
22
  }
@@ -5,5 +5,6 @@ import { ApiException, ErrorResponse } from '../api-exception.js';
5
5
  * For example, an API may not allow calling DELETE to remove a resource.
6
6
  */
7
7
  export declare class MethodNotAllowedError extends ApiException {
8
- constructor(response?: ErrorResponse, cause?: Error);
8
+ constructor(response?: string | ErrorResponse | Error, cause?: Error);
9
+ protected _initResponse(response: Partial<ErrorResponse>): void;
9
10
  }
@@ -8,12 +8,15 @@ import { ApiException } from '../api-exception.js';
8
8
  */
9
9
  export class MethodNotAllowedError extends ApiException {
10
10
  constructor(response, cause) {
11
- super({
11
+ super(response, cause);
12
+ this.status = HttpStatus.METHOD_NOT_ALLOWED;
13
+ }
14
+ _initResponse(response) {
15
+ super._initResponse({
12
16
  message: translate('error:METHOD_NOT_ALLOWED', 'Method Not Allowed'),
13
17
  severity: 'error',
14
18
  code: 'METHOD_NOT_ALLOWED',
15
19
  ...response
16
- }, cause);
17
- this.status = HttpStatus.METHOD_NOT_ALLOWED;
20
+ });
18
21
  }
19
22
  }
@@ -8,5 +8,6 @@ import { ApiException, ErrorResponse } from '../api-exception.js';
8
8
  * frequent occurrence on the web.
9
9
  */
10
10
  export declare class NotFoundError extends ApiException {
11
- constructor(response?: ErrorResponse, cause?: Error);
11
+ constructor(response?: string | ErrorResponse | Error, cause?: Error);
12
+ protected _initResponse(response: Partial<ErrorResponse>): void;
12
13
  }
@@ -11,12 +11,15 @@ import { ApiException } from '../api-exception.js';
11
11
  */
12
12
  export class NotFoundError extends ApiException {
13
13
  constructor(response, cause) {
14
- super({
14
+ super(response, cause);
15
+ this.status = HttpStatus.NOT_FOUND;
16
+ }
17
+ _initResponse(response) {
18
+ super._initResponse({
15
19
  message: translate('error:NOT_FOUND', 'Not found'),
16
20
  severity: 'error',
17
21
  code: 'NOT_FOUND',
18
22
  ...response
19
- }, cause);
20
- this.status = HttpStatus.NOT_FOUND;
23
+ });
21
24
  }
22
25
  }
@@ -5,5 +5,6 @@ import { ApiException, ErrorResponse } from '../api-exception.js';
5
5
  * That is, the client must authenticate itself to get the requested response.
6
6
  */
7
7
  export declare class UnauthorizedError extends ApiException {
8
- constructor(response?: ErrorResponse, cause?: Error);
8
+ constructor(response?: string | ErrorResponse | Error, cause?: Error);
9
+ protected _initResponse(response: Partial<ErrorResponse>): void;
9
10
  }
@@ -8,12 +8,15 @@ import { ApiException } from '../api-exception.js';
8
8
  */
9
9
  export class UnauthorizedError extends ApiException {
10
10
  constructor(response, cause) {
11
- super({
11
+ super(response, cause);
12
+ this.status = HttpStatus.UNAUTHORIZED;
13
+ }
14
+ _initResponse(response) {
15
+ super._initResponse({
12
16
  message: translate('error:UNAUTHORIZED', 'Unauthorized'),
13
17
  severity: 'error',
14
18
  code: 'UNAUTHORIZED',
15
19
  ...response
16
- }, cause);
17
- this.status = HttpStatus.UNAUTHORIZED;
20
+ });
18
21
  }
19
22
  }
@@ -4,5 +4,6 @@ import { ApiException, ErrorResponse } from '../api-exception.js';
4
4
  * The request was well-formed but was unable to be followed due to semantic errors.
5
5
  */
6
6
  export declare class UnprocessableEntityError extends ApiException {
7
- constructor(response?: ErrorResponse, cause?: Error);
7
+ constructor(response?: string | ErrorResponse | Error, cause?: Error);
8
+ protected _initResponse(response: Partial<ErrorResponse>): void;
8
9
  }
@@ -7,12 +7,15 @@ import { ApiException } from '../api-exception.js';
7
7
  */
8
8
  export class UnprocessableEntityError extends ApiException {
9
9
  constructor(response, cause) {
10
- super({
10
+ super(response, cause);
11
+ this.status = HttpStatus.UNPROCESSABLE_ENTITY;
12
+ }
13
+ _initResponse(response) {
14
+ super._initResponse({
11
15
  message: translate('error:UNPROCESSABLE_ENTITY', 'Unprocessable entity'),
12
16
  severity: 'error',
13
17
  code: 'UNPROCESSABLE_ENTITY',
14
18
  ...response
15
- }, cause);
16
- this.status = HttpStatus.UNPROCESSABLE_ENTITY;
19
+ });
17
20
  }
18
21
  }
@@ -7,7 +7,7 @@ export function wrapError(response) {
7
7
  const x = response;
8
8
  if (typeof x.status === 'number' || typeof x.getStatus === 'function')
9
9
  return new ApiException(response);
10
- return new InternalServerError(undefined, response);
10
+ return new InternalServerError(response);
11
11
  }
12
12
  return new InternalServerError();
13
13
  }
@@ -1,14 +1,21 @@
1
1
  import { FallbackLng, I18n, LanguageResource } from '@opra/i18n';
2
2
  import { ApiException } from '../../exception/index.js';
3
- import { ExecutionContext, ExecutionRequest } from '../execution-context.js';
3
+ import { IExecutionContext } from '../../interfaces/execution-context.interface.js';
4
4
  import { OpraService } from '../opra-service.js';
5
+ import { QueryContext } from '../query-context.js';
5
6
  export declare namespace OpraAdapter {
7
+ type UserContextResolver = (args: {
8
+ executionContext: IExecutionContext;
9
+ isBatch: boolean;
10
+ }) => object | Promise<object>;
11
+ type RequestFinishEvent = (args: {
12
+ executionContext: IExecutionContext;
13
+ userContext?: any;
14
+ failed: boolean;
15
+ }) => Promise<void>;
6
16
  interface Options {
7
17
  i18n?: I18n | I18nOptions | (() => Promise<I18n>);
8
- userContext?: (request: any, options: {
9
- platform: string;
10
- isBatch: boolean;
11
- }) => object | Promise<object>;
18
+ userContext?: UserContextResolver;
12
19
  }
13
20
  interface I18nOptions {
14
21
  /**
@@ -37,17 +44,18 @@ export declare namespace OpraAdapter {
37
44
  */
38
45
  resourceDirs?: string[];
39
46
  }
40
- type UserContextResolver = (isBatch: boolean) => any | Promise<any>;
41
47
  }
42
- export declare abstract class OpraAdapter<TAdapterContext = any> {
48
+ export declare abstract class OpraAdapter<TExecutionContext extends IExecutionContext> {
43
49
  readonly service: OpraService;
44
50
  readonly i18n: I18n;
45
- constructor(service: OpraService, i18n?: I18n);
46
- protected abstract prepareRequests(adapterContext: TAdapterContext): ExecutionRequest[];
47
- protected abstract createExecutionContext(adapterContext: any, request: ExecutionRequest): ExecutionContext;
48
- protected abstract sendResponse(adapterContext: TAdapterContext, executionContexts: ExecutionContext[]): Promise<void>;
49
- protected abstract sendError(adapterContext: TAdapterContext, error: ApiException): Promise<void>;
50
- protected abstract isBatch(adapterContext: TAdapterContext): boolean;
51
- protected handler(adapterContext: TAdapterContext, userContextResolver?: OpraAdapter.UserContextResolver): Promise<void>;
51
+ readonly userContextResolver?: OpraAdapter.UserContextResolver;
52
+ constructor(service: OpraService, options?: Omit<OpraAdapter.Options, 'i18n'> & {
53
+ i18n: I18n;
54
+ });
55
+ protected abstract prepareRequests(executionContext: TExecutionContext): QueryContext[];
56
+ protected abstract sendResponse(executionContext: TExecutionContext, queryContexts: QueryContext[]): Promise<void>;
57
+ protected abstract sendError(executionContext: TExecutionContext, error: ApiException): Promise<void>;
58
+ protected abstract isBatch(executionContext: TExecutionContext): boolean;
59
+ protected handler(executionContext: TExecutionContext): Promise<void>;
52
60
  protected static initI18n(options?: OpraAdapter.Options): Promise<I18n>;
53
61
  }
@@ -1,35 +1,35 @@
1
+ import { AsyncEventEmitter } from 'strict-typed-events';
1
2
  import { I18n } from '@opra/i18n';
2
3
  import { FailedDependencyError } from '../../exception/index.js';
3
4
  import { wrapError } from '../../exception/wrap-error.js';
4
5
  export class OpraAdapter {
5
6
  service;
6
7
  i18n;
7
- constructor(service, i18n) {
8
+ userContextResolver;
9
+ constructor(service, options) {
8
10
  this.service = service;
9
- this.i18n = i18n || I18n.defaultInstance;
11
+ this.i18n = options?.i18n || I18n.defaultInstance;
12
+ this.userContextResolver = options?.userContext;
10
13
  }
11
- async handler(adapterContext, userContextResolver) {
14
+ async handler(executionContext) {
12
15
  if (!this.i18n.isInitialized)
13
16
  await this.i18n.init();
14
- const executionContexts = [];
15
- let requests;
17
+ let queryContexts;
16
18
  let userContext;
19
+ let failed = false;
17
20
  try {
18
- requests = this.prepareRequests(adapterContext);
21
+ queryContexts = this.prepareRequests(executionContext);
19
22
  let stop = false;
20
23
  // Read requests can be executed simultaneously, write request should be executed one by one
21
24
  let promises;
22
25
  let exclusive = false;
23
- for (const request of requests) {
24
- exclusive = exclusive || request.query.operationType !== 'read';
26
+ for (const context of queryContexts) {
27
+ exclusive = exclusive || context.query.operationType !== 'read';
25
28
  // Wait previous read requests before executing update request
26
29
  if (exclusive && promises) {
27
30
  await Promise.allSettled(promises);
28
31
  promises = undefined;
29
32
  }
30
- const resource = request.query.resource;
31
- const context = this.createExecutionContext(adapterContext, request);
32
- executionContexts.push(context);
33
33
  // If previous request in bucket had an error and executed an update
34
34
  // we do not execute next requests
35
35
  if (stop) {
@@ -37,10 +37,14 @@ export class OpraAdapter {
37
37
  continue;
38
38
  }
39
39
  try {
40
+ const resource = context.query.resource;
40
41
  const promise = (async () => {
41
42
  await resource.prepare(context);
42
- if (userContextResolver && !userContext)
43
- userContext = userContextResolver(this.isBatch(adapterContext));
43
+ if (this.userContextResolver && !userContext)
44
+ userContext = this.userContextResolver({
45
+ executionContext,
46
+ isBatch: this.isBatch(executionContext)
47
+ });
44
48
  context.userContext = userContext;
45
49
  await resource.execute(context);
46
50
  })().catch(e => {
@@ -66,22 +70,21 @@ export class OpraAdapter {
66
70
  }
67
71
  if (promises)
68
72
  await Promise.allSettled(promises);
69
- if (userContext && typeof userContext.onRequestFinish === 'function') {
70
- try {
71
- const hasError = !!executionContexts.find(ctx => ctx.response.errors?.length);
72
- await userContext.onRequestFinish(hasError);
73
- }
74
- catch (e) {
75
- await this.sendError(adapterContext, wrapError(e));
76
- return;
77
- }
78
- }
79
- await this.sendResponse(adapterContext, executionContexts);
73
+ await this.sendResponse(executionContext, queryContexts);
80
74
  }
81
75
  catch (e) {
76
+ failed = true;
82
77
  const error = wrapError(e);
83
- await this.sendError(adapterContext, error);
84
- return;
78
+ await this.sendError(executionContext, error);
79
+ }
80
+ finally {
81
+ if (executionContext instanceof AsyncEventEmitter) {
82
+ await executionContext
83
+ .emitAsyncSerial('finish', {
84
+ userContext,
85
+ failed
86
+ }).catch();
87
+ }
85
88
  }
86
89
  }
87
90
  static async initI18n(options) {
@@ -1,11 +1,11 @@
1
1
  import type { Application } from 'express';
2
- import type { IHttpAdapterContext } from '../../interfaces/http-context.interface.js';
2
+ import type { IHttpExecutionContext } from '../../interfaces/execution-context.interface';
3
3
  import type { OpraService } from '../opra-service.js';
4
4
  import { OpraHttpAdapter } from './http-adapter.js';
5
5
  export declare namespace OpraExpressAdapter {
6
6
  interface Options extends OpraHttpAdapter.Options {
7
7
  }
8
8
  }
9
- export declare class OpraExpressAdapter extends OpraHttpAdapter<IHttpAdapterContext> {
9
+ export declare class OpraExpressAdapter extends OpraHttpAdapter<IHttpExecutionContext> {
10
10
  static init(app: Application, service: OpraService, options?: OpraExpressAdapter.Options): Promise<OpraExpressAdapter>;
11
11
  }
@@ -1,25 +1,56 @@
1
+ import { AsyncEventEmitter } from 'strict-typed-events';
1
2
  import { normalizePath } from '@opra/url';
2
3
  import { OpraHttpAdapter } from './http-adapter.js';
3
4
  export class OpraExpressAdapter extends OpraHttpAdapter {
4
5
  static async init(app, service, options) {
5
6
  const i18n = await this.initI18n(options);
6
- const adapter = new OpraExpressAdapter(service, i18n);
7
+ const adapter = new OpraExpressAdapter(service, {
8
+ ...options,
9
+ i18n
10
+ });
7
11
  const prefix = '/' + normalizePath(options?.prefix, true);
8
- const userContextResolver = options?.userContext;
9
12
  app.use(prefix, (request, response, next) => {
10
13
  (async () => {
11
- const req = new ExpressRequestWrapper(request);
12
- const res = new ExpressResponseWrapper(response);
13
- const adapterContext = {
14
- getRequest: () => req,
15
- getResponse: () => res
16
- };
17
- await adapter.handler(adapterContext, (isBatch) => userContextResolver && userContextResolver(request, { platform: 'express', isBatch }));
14
+ const executionContext = new ExpressExecutionContext(request, response);
15
+ await adapter.handler(executionContext);
18
16
  })().catch(e => next(e));
19
17
  });
20
18
  return adapter;
21
19
  }
22
20
  }
21
+ class ExpressExecutionContext extends AsyncEventEmitter {
22
+ _request;
23
+ _response;
24
+ constructor(request, response) {
25
+ super();
26
+ this._request = new ExpressRequestWrapper(request);
27
+ this._response = new ExpressResponseWrapper(response);
28
+ }
29
+ getType() {
30
+ return 'http';
31
+ }
32
+ getPlatform() {
33
+ return 'express';
34
+ }
35
+ switchToHttp() {
36
+ return this;
37
+ }
38
+ getRequest() {
39
+ return this._request.getInstance();
40
+ }
41
+ getResponse() {
42
+ return this._response.getInstance();
43
+ }
44
+ getRequestWrapper() {
45
+ return this._request;
46
+ }
47
+ getResponseWrapper() {
48
+ return this._response;
49
+ }
50
+ onFinish(fn) {
51
+ this.on('finish', fn);
52
+ }
53
+ }
23
54
  class ExpressRequestWrapper {
24
55
  instance;
25
56
  constructor(instance) {
@@ -1,9 +1,9 @@
1
1
  import { OpraURL } from '@opra/url';
2
2
  import { ApiException } from '../../exception/index.js';
3
- import { ExecutionQuery } from '../../interfaces/execution-query.interface.js';
4
- import { IHttpAdapterContext } from '../../interfaces/http-context.interface.js';
3
+ import { IHttpExecutionContext } from '../../interfaces/execution-context.interface.js';
4
+ import { OpraQuery } from '../../interfaces/query.interface.js';
5
5
  import { HeadersObject } from '../../utils/headers.js';
6
- import { ExecutionContext, ExecutionRequest } from '../execution-context.js';
6
+ import { QueryContext } from '../query-context.js';
7
7
  import { OpraAdapter } from './adapter.js';
8
8
  export declare namespace OpraHttpAdapter {
9
9
  type Options = OpraAdapter.Options & {
@@ -15,14 +15,13 @@ interface PreparedOutput {
15
15
  headers?: Record<string, string>;
16
16
  body?: any;
17
17
  }
18
- export declare class OpraHttpAdapter<TAdapterContext extends IHttpAdapterContext> extends OpraAdapter<IHttpAdapterContext> {
19
- protected prepareRequests(adapterContext: TAdapterContext): ExecutionRequest[];
20
- prepareRequest(adapterContext: any, url: OpraURL, method: string, headers: HeadersObject, body?: any): ExecutionRequest;
21
- createExecutionContext(adapterContext: any, request: ExecutionRequest): ExecutionContext;
22
- buildQuery(url: OpraURL, method: string, body?: any): ExecutionQuery | undefined;
23
- protected sendResponse(adapterContext: TAdapterContext, executionContexts: ExecutionContext[]): Promise<void>;
24
- protected isBatch(adapterContext: TAdapterContext): boolean;
25
- protected createOutput(ctx: ExecutionContext): PreparedOutput;
26
- protected sendError(adapterContext: TAdapterContext, error: ApiException): Promise<void>;
18
+ export declare class OpraHttpAdapter<TExecutionContext extends IHttpExecutionContext> extends OpraAdapter<IHttpExecutionContext> {
19
+ protected prepareRequests(executionContext: TExecutionContext): QueryContext[];
20
+ prepareRequest(executionContext: IHttpExecutionContext, url: OpraURL, method: string, headers: HeadersObject, body?: any): QueryContext;
21
+ buildQuery(url: OpraURL, method: string, body?: any): OpraQuery | undefined;
22
+ protected sendResponse(executionContext: TExecutionContext, queryContexts: QueryContext[]): Promise<void>;
23
+ protected isBatch(executionContext: TExecutionContext): boolean;
24
+ protected createOutput(ctx: QueryContext): PreparedOutput;
25
+ protected sendError(executionContext: TExecutionContext, error: ApiException): Promise<void>;
27
26
  }
28
27
  export {};
@@ -3,26 +3,26 @@ import { OpraVersion } from '../../constants.js';
3
3
  import { HttpHeaders, HttpStatus } from '../../enums/index.js';
4
4
  import { ApiException, BadRequestError, InternalServerError, MethodNotAllowedError, NotFoundError, } from '../../exception/index.js';
5
5
  import { wrapError } from '../../exception/wrap-error.js';
6
- import { ExecutionQuery } from '../../interfaces/execution-query.interface.js';
6
+ import { OpraQuery } from '../../interfaces/query.interface.js';
7
7
  import { Headers } from '../../utils/headers.js';
8
8
  import { ComplexType } from '../data-type/complex-type.js';
9
- import { ExecutionContext, ExecutionRequest, ExecutionResponse } from '../execution-context.js';
9
+ import { QueryContext } from '../query-context.js';
10
10
  import { ContainerResourceHandler } from '../resource/container-resource-handler.js';
11
11
  import { EntityResourceHandler } from '../resource/entity-resource-handler.js';
12
12
  import { OpraAdapter } from './adapter.js';
13
13
  export class OpraHttpAdapter extends OpraAdapter {
14
- prepareRequests(adapterContext) {
15
- const req = adapterContext.getRequest();
14
+ prepareRequests(executionContext) {
15
+ const req = executionContext.getRequestWrapper();
16
16
  // todo implement batch requests
17
- if (this.isBatch(adapterContext)) {
17
+ if (this.isBatch(executionContext)) {
18
18
  throw new Error('not implemented yet');
19
19
  }
20
20
  const url = new OpraURL(req.getUrl());
21
21
  return [
22
- this.prepareRequest(adapterContext, url, req.getMethod(), Headers.from(req.getHeaders()), req.getBody())
22
+ this.prepareRequest(executionContext, url, req.getMethod(), Headers.from(req.getHeaders()), req.getBody())
23
23
  ];
24
24
  }
25
- prepareRequest(adapterContext, url, method, headers, body) {
25
+ prepareRequest(executionContext, url, method, headers, body) {
26
26
  if (!url.path.size)
27
27
  throw new BadRequestError();
28
28
  if (method !== 'GET' && url.path.size > 1)
@@ -32,20 +32,13 @@ export class OpraHttpAdapter extends OpraAdapter {
32
32
  throw new MethodNotAllowedError({
33
33
  message: `Method "${method}" is not allowed by target resource`
34
34
  });
35
- return new ExecutionRequest({
35
+ return new QueryContext({
36
+ service: this.service,
37
+ executionContext,
36
38
  query,
37
39
  headers,
38
40
  params: url.searchParams,
39
- });
40
- }
41
- createExecutionContext(adapterContext, request) {
42
- return new ExecutionContext({
43
- type: 'http',
44
- service: this.service,
45
- request,
46
- response: new ExecutionResponse(),
47
- adapterContext,
48
- continueOnError: request.query.operationType === 'read'
41
+ continueOnError: query.operationType === 'read'
49
42
  });
50
43
  }
51
44
  buildQuery(url, method, body) {
@@ -70,7 +63,7 @@ export class OpraHttpAdapter extends OpraAdapter {
70
63
  switch (method) {
71
64
  case 'GET': {
72
65
  if (scope === 'collection') {
73
- query = ExecutionQuery.forSearch(resource, {
66
+ query = OpraQuery.forSearch(resource, {
74
67
  filter: url.searchParams.get('$filter'),
75
68
  limit: url.searchParams.get('$limit'),
76
69
  skip: url.searchParams.get('$skip'),
@@ -83,7 +76,7 @@ export class OpraHttpAdapter extends OpraAdapter {
83
76
  });
84
77
  }
85
78
  else {
86
- query = ExecutionQuery.forGet(resource, p.key, {
79
+ query = OpraQuery.forGet(resource, p.key, {
87
80
  pick: url.searchParams.get('$pick'),
88
81
  omit: url.searchParams.get('$omit'),
89
82
  include: url.searchParams.get('$include')
@@ -102,7 +95,7 @@ export class OpraHttpAdapter extends OpraAdapter {
102
95
  const prop = dataType.properties?.[p.resource];
103
96
  if (!prop)
104
97
  throw new NotFoundError({ message: `Invalid or unknown resource path (${path})` });
105
- const q = ExecutionQuery.forGetProperty(prop);
98
+ const q = OpraQuery.forGetProperty(prop);
106
99
  if (nested) {
107
100
  nested.nested = q;
108
101
  }
@@ -116,18 +109,18 @@ export class OpraHttpAdapter extends OpraAdapter {
116
109
  }
117
110
  case 'DELETE': {
118
111
  if (scope === 'collection') {
119
- query = ExecutionQuery.forDeleteMany(resource, {
112
+ query = OpraQuery.forDeleteMany(resource, {
120
113
  filter: url.searchParams.get('$filter'),
121
114
  });
122
115
  }
123
116
  else {
124
- query = ExecutionQuery.forDelete(resource, p.key);
117
+ query = OpraQuery.forDelete(resource, p.key);
125
118
  }
126
119
  break;
127
120
  }
128
121
  case 'POST': {
129
122
  if (scope === 'collection') {
130
- query = ExecutionQuery.forCreate(resource, body, {
123
+ query = OpraQuery.forCreate(resource, body, {
131
124
  pick: url.searchParams.get('$pick'),
132
125
  omit: url.searchParams.get('$omit'),
133
126
  include: url.searchParams.get('$include')
@@ -137,12 +130,12 @@ export class OpraHttpAdapter extends OpraAdapter {
137
130
  }
138
131
  case 'PATCH': {
139
132
  if (scope === 'collection') {
140
- query = ExecutionQuery.forUpdateMany(resource, body, {
133
+ query = OpraQuery.forUpdateMany(resource, body, {
141
134
  filter: url.searchParams.get('$filter')
142
135
  });
143
136
  }
144
137
  else {
145
- query = ExecutionQuery.forUpdate(resource, p.key, body, {
138
+ query = OpraQuery.forUpdate(resource, p.key, body, {
146
139
  pick: url.searchParams.get('$pick'),
147
140
  omit: url.searchParams.get('$omit'),
148
141
  include: url.searchParams.get('$include')
@@ -163,13 +156,13 @@ export class OpraHttpAdapter extends OpraAdapter {
163
156
  throw new BadRequestError({ message: e.message });
164
157
  }
165
158
  }
166
- async sendResponse(adapterContext, executionContexts) {
159
+ async sendResponse(executionContext, queryContexts) {
167
160
  const outputPackets = [];
168
- for (const ctx of executionContexts) {
161
+ for (const ctx of queryContexts) {
169
162
  const v = this.createOutput(ctx);
170
163
  outputPackets.push(v);
171
164
  }
172
- if (this.isBatch(adapterContext)) {
165
+ if (this.isBatch(executionContext)) {
173
166
  // this.writeError([], new InternalServerError({message: 'Not implemented yet'}));
174
167
  return;
175
168
  }
@@ -183,7 +176,7 @@ export class OpraHttpAdapter extends OpraAdapter {
183
176
  });
184
177
  }
185
178
  const out = outputPackets[0];
186
- const resp = adapterContext.getResponse();
179
+ const resp = executionContext.getResponseWrapper();
187
180
  resp.setStatus(out.status);
188
181
  resp.setHeader(HttpHeaders.Content_Type, 'application/json');
189
182
  resp.setHeader(HttpHeaders.Cache_Control, 'no-cache');
@@ -198,11 +191,11 @@ export class OpraHttpAdapter extends OpraAdapter {
198
191
  resp.send(JSON.stringify(out.body));
199
192
  }
200
193
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
201
- isBatch(adapterContext) {
194
+ isBatch(executionContext) {
202
195
  return false;
203
196
  }
204
197
  createOutput(ctx) {
205
- const { query } = ctx.request;
198
+ const { query } = ctx;
206
199
  let status = ctx.response.status;
207
200
  let body = ctx.response.value || {};
208
201
  const errors = ctx.response.errors?.map(e => wrapError(e));
@@ -228,8 +221,8 @@ export class OpraHttpAdapter extends OpraAdapter {
228
221
  body
229
222
  };
230
223
  }
231
- async sendError(adapterContext, error) {
232
- const resp = adapterContext.getResponse();
224
+ async sendError(executionContext, error) {
225
+ const resp = executionContext.getResponseWrapper();
233
226
  resp.setStatus(error.status || 500);
234
227
  resp.setHeader(HttpHeaders.Content_Type, 'application/json');
235
228
  resp.setHeader(HttpHeaders.Cache_Control, 'no-cache');
@@ -0,0 +1,32 @@
1
+ import { SearchParams } from '@opra/url';
2
+ import { HttpStatus } from '../enums/index.js';
3
+ import { ApiException } from '../exception/index.js';
4
+ import { ContextType, IExecutionContext, IHttpExecutionContext } from '../interfaces/execution-context.interface.js';
5
+ import { OpraQuery } from '../interfaces/query.interface.js';
6
+ import { HeadersObject } from '../utils/headers.js';
7
+ import { OpraService } from './opra-service.js';
8
+ export declare type QueryContextArgs = Pick<QueryContext, 'service' | 'executionContext' | 'query' | 'params' | 'headers' | 'userContext' | 'parentValue' | 'continueOnError'>;
9
+ export declare class QueryContext {
10
+ readonly service: OpraService;
11
+ readonly executionContext: IExecutionContext;
12
+ readonly query: OpraQuery;
13
+ readonly params: SearchParams;
14
+ readonly headers: HeadersObject;
15
+ readonly parentValue?: any;
16
+ readonly resultPath: string;
17
+ readonly response: QueryResponse;
18
+ userContext?: any;
19
+ continueOnError?: boolean;
20
+ constructor(args: QueryContextArgs);
21
+ get type(): ContextType;
22
+ switchToHttp(): IHttpExecutionContext;
23
+ }
24
+ export declare type QueryResponseArgs = Pick<QueryResponse, 'status' | 'value' | 'total'>;
25
+ export declare class QueryResponse {
26
+ headers: HeadersObject;
27
+ errors: ApiException[];
28
+ status?: HttpStatus;
29
+ value?: any;
30
+ total?: number;
31
+ constructor(args?: QueryResponseArgs);
32
+ }