@opra/core 0.0.5 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/exception/api-exception.js +22 -14
- package/cjs/exception/errors/bad-request.error.js +3 -2
- package/cjs/exception/errors/failed-dependency.error.js +3 -2
- package/cjs/exception/errors/forbidden.error.js +3 -2
- package/cjs/exception/errors/internal-server.error.js +3 -2
- package/cjs/exception/errors/method-not-allowed.error.js +3 -2
- package/cjs/exception/errors/not-found.error.js +3 -2
- package/cjs/exception/errors/unauthorized.error.js +3 -2
- package/cjs/exception/errors/unprocessable-entity.error.js +3 -2
- package/cjs/exception/wrap-error.js +17 -0
- package/cjs/implementation/adapter/adapter.js +72 -44
- package/cjs/implementation/adapter/express-adapter.js +4 -4
- package/cjs/implementation/adapter/http-adapter.js +26 -43
- package/cjs/implementation/opra-service.js +3 -3
- package/cjs/implementation/resource/container-resource-handler.js +30 -0
- package/cjs/implementation/resource/entity-resource-handler.js +76 -0
- package/cjs/implementation/resource/{resource-info.js → resource-handler.js} +10 -3
- package/cjs/implementation/schema-generator.js +8 -5
- package/cjs/index.js +5 -3
- package/cjs/interfaces/execution-query.interface.js +6 -6
- package/cjs/interfaces/{entity-resource.interface.js → user-context.interface.js} +0 -0
- package/cjs/services/entity-resource-controller.js +30 -0
- package/esm/exception/api-exception.d.ts +5 -4
- package/esm/exception/api-exception.js +22 -14
- package/esm/exception/errors/bad-request.error.d.ts +1 -1
- package/esm/exception/errors/bad-request.error.js +3 -2
- package/esm/exception/errors/failed-dependency.error.d.ts +1 -1
- package/esm/exception/errors/failed-dependency.error.js +3 -2
- package/esm/exception/errors/forbidden.error.d.ts +1 -1
- package/esm/exception/errors/forbidden.error.js +3 -2
- package/esm/exception/errors/internal-server.error.d.ts +1 -1
- package/esm/exception/errors/internal-server.error.js +3 -2
- package/esm/exception/errors/method-not-allowed.error.d.ts +1 -1
- package/esm/exception/errors/method-not-allowed.error.js +3 -2
- package/esm/exception/errors/not-found.error.d.ts +1 -1
- package/esm/exception/errors/not-found.error.js +3 -2
- package/esm/exception/errors/unauthorized.error.d.ts +1 -1
- package/esm/exception/errors/unauthorized.error.js +3 -2
- package/esm/exception/errors/unprocessable-entity.error.d.ts +1 -1
- package/esm/exception/errors/unprocessable-entity.error.js +3 -2
- package/esm/exception/wrap-error.d.ts +2 -0
- package/esm/exception/wrap-error.js +13 -0
- package/esm/implementation/adapter/adapter.d.ts +43 -8
- package/esm/implementation/adapter/adapter.js +73 -45
- package/esm/implementation/adapter/express-adapter.d.ts +3 -4
- package/esm/implementation/adapter/express-adapter.js +4 -4
- package/esm/implementation/adapter/http-adapter.d.ts +6 -5
- package/esm/implementation/adapter/http-adapter.js +26 -43
- package/esm/implementation/opra-service.d.ts +6 -6
- package/esm/implementation/opra-service.js +3 -3
- package/esm/implementation/resource/container-resource-handler.d.ts +14 -0
- package/esm/implementation/resource/{container-resource-controller.js → container-resource-handler.js} +8 -4
- package/esm/implementation/resource/{entity-resource-info.d.ts → entity-resource-handler.d.ts} +4 -10
- package/esm/implementation/resource/entity-resource-handler.js +71 -0
- package/esm/implementation/resource/resource-handler.d.ts +15 -0
- package/esm/implementation/resource/{resource-info.js → resource-handler.js} +8 -1
- package/esm/implementation/schema-generator.js +8 -5
- package/esm/index.d.ts +5 -3
- package/esm/index.js +5 -3
- package/esm/interfaces/execution-query.interface.d.ts +19 -19
- package/esm/interfaces/execution-query.interface.js +6 -6
- package/esm/interfaces/resource-container.interface.d.ts +4 -4
- package/esm/interfaces/user-context.interface.d.ts +3 -0
- package/esm/interfaces/{entity-resource.interface.js → user-context.interface.js} +0 -0
- package/esm/services/entity-resource-controller.d.ts +17 -0
- package/esm/services/entity-resource-controller.js +26 -0
- package/esm/types.d.ts +1 -1
- package/package.json +8 -9
- package/cjs/implementation/resource/container-resource-controller.js +0 -26
- package/cjs/implementation/resource/entity-resource-info.js +0 -68
- package/esm/implementation/resource/container-resource-controller.d.ts +0 -12
- package/esm/implementation/resource/entity-resource-info.js +0 -63
- package/esm/implementation/resource/resource-info.d.ts +0 -10
- package/esm/interfaces/entity-resource.interface.d.ts +0 -9
|
@@ -2,20 +2,22 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ApiException = void 0;
|
|
4
4
|
const index_js_1 = require("../enums/index.js");
|
|
5
|
-
const nodeEnv = process.env.NODE_ENV || '';
|
|
6
5
|
/**
|
|
7
6
|
* Defines the base Opra exception, which is handled by the default Exceptions Handler.
|
|
8
7
|
*/
|
|
9
8
|
class ApiException extends Error {
|
|
10
|
-
|
|
9
|
+
static stackAsDiagnostics = false;
|
|
11
10
|
response;
|
|
12
11
|
status;
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
cause;
|
|
13
|
+
constructor(response, cause) {
|
|
14
|
+
super('');
|
|
15
15
|
this._initName();
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
if (cause)
|
|
17
|
+
Object.defineProperty(this, 'cause', { enumerable: false, configurable: true, writable: true, value: cause });
|
|
18
|
+
else if (response instanceof Error)
|
|
19
|
+
Object.defineProperty(this, 'cause', { enumerable: false, configurable: true, writable: true, value: response });
|
|
20
|
+
this.status = index_js_1.HttpStatus.INTERNAL_SERVER_ERROR;
|
|
19
21
|
this._initResponse(response);
|
|
20
22
|
}
|
|
21
23
|
setStatus(status) {
|
|
@@ -38,11 +40,7 @@ class ApiException extends Error {
|
|
|
38
40
|
this.response = {
|
|
39
41
|
message: x.message || x.details || ('' + init),
|
|
40
42
|
};
|
|
41
|
-
if (init instanceof Error)
|
|
42
|
-
if (nodeEnv === 'dev' || nodeEnv === 'development')
|
|
43
|
-
this.response.diagnostics = init.stack;
|
|
44
|
-
}
|
|
45
|
-
else
|
|
43
|
+
if (!(init instanceof Error))
|
|
46
44
|
Object.assign(this.response, init);
|
|
47
45
|
}
|
|
48
46
|
else {
|
|
@@ -50,14 +48,24 @@ class ApiException extends Error {
|
|
|
50
48
|
message: '' + init,
|
|
51
49
|
};
|
|
52
50
|
}
|
|
51
|
+
if (this.cause instanceof Error && this.cause.stack) {
|
|
52
|
+
if (ApiException.stackAsDiagnostics)
|
|
53
|
+
this.response.diagnostics = this.cause.stack.split('\n');
|
|
54
|
+
this.stack = this.cause.stack;
|
|
55
|
+
}
|
|
53
56
|
if (!this.response.severity)
|
|
54
57
|
if (this.status >= 500)
|
|
55
58
|
this.response.severity = 'fatal';
|
|
56
59
|
else
|
|
57
60
|
this.response.severity = 'error';
|
|
58
61
|
}
|
|
59
|
-
static wrap(response
|
|
60
|
-
|
|
62
|
+
static wrap(response) {
|
|
63
|
+
if (response instanceof ApiException)
|
|
64
|
+
return response;
|
|
65
|
+
const out = new this(response, response instanceof Error ? response : undefined);
|
|
66
|
+
if (response instanceof Error)
|
|
67
|
+
out.stack = response.stack;
|
|
68
|
+
return out;
|
|
61
69
|
}
|
|
62
70
|
}
|
|
63
71
|
exports.ApiException = ApiException;
|
|
@@ -10,13 +10,14 @@ const api_exception_js_1 = require("../api-exception.js");
|
|
|
10
10
|
* (e.g., malformed request syntax, invalid request message framing, or deceptive request routing).
|
|
11
11
|
*/
|
|
12
12
|
class BadRequestError extends api_exception_js_1.ApiException {
|
|
13
|
-
constructor(response) {
|
|
13
|
+
constructor(response, cause) {
|
|
14
14
|
super({
|
|
15
15
|
message: (0, i18n_1.translate)('error:BAD_REQUEST', 'Bad request'),
|
|
16
16
|
severity: 'error',
|
|
17
17
|
code: 'BAD_REQUEST',
|
|
18
18
|
...response
|
|
19
|
-
},
|
|
19
|
+
}, cause);
|
|
20
|
+
this.status = index_js_1.HttpStatus.BAD_REQUEST;
|
|
20
21
|
}
|
|
21
22
|
}
|
|
22
23
|
exports.BadRequestError = BadRequestError;
|
|
@@ -9,13 +9,14 @@ const api_exception_js_1 = require("../api-exception.js");
|
|
|
9
9
|
* The request failed due to failure of a previous request.
|
|
10
10
|
*/
|
|
11
11
|
class FailedDependencyError extends api_exception_js_1.ApiException {
|
|
12
|
-
constructor(response) {
|
|
12
|
+
constructor(response, cause) {
|
|
13
13
|
super({
|
|
14
14
|
message: (0, i18n_1.translate)('error:FAILED_DEPENDENCY', 'The request failed due to failure of a previous request.'),
|
|
15
15
|
severity: 'error',
|
|
16
16
|
code: 'FAILED_DEPENDENCY',
|
|
17
17
|
...response
|
|
18
|
-
},
|
|
18
|
+
}, cause);
|
|
19
|
+
this.status = index_js_1.HttpStatus.FAILED_DEPENDENCY;
|
|
19
20
|
}
|
|
20
21
|
}
|
|
21
22
|
exports.FailedDependencyError = FailedDependencyError;
|
|
@@ -11,13 +11,14 @@ const api_exception_js_1 = require("../api-exception.js");
|
|
|
11
11
|
* the client's identity is known to the server.
|
|
12
12
|
*/
|
|
13
13
|
class ForbiddenError extends api_exception_js_1.ApiException {
|
|
14
|
-
constructor(response) {
|
|
14
|
+
constructor(response, cause) {
|
|
15
15
|
super({
|
|
16
16
|
message: (0, i18n_1.translate)('error:FORBIDDEN', 'Forbidden'),
|
|
17
17
|
severity: 'error',
|
|
18
18
|
code: 'FORBIDDEN',
|
|
19
19
|
...response
|
|
20
|
-
},
|
|
20
|
+
}, cause);
|
|
21
|
+
this.status = index_js_1.HttpStatus.FORBIDDEN;
|
|
21
22
|
}
|
|
22
23
|
}
|
|
23
24
|
exports.ForbiddenError = ForbiddenError;
|
|
@@ -9,13 +9,14 @@ const api_exception_js_1 = require("../api-exception.js");
|
|
|
9
9
|
* The server has encountered a situation it does not know how to handle.
|
|
10
10
|
*/
|
|
11
11
|
class InternalServerError extends api_exception_js_1.ApiException {
|
|
12
|
-
constructor(response) {
|
|
12
|
+
constructor(response, cause) {
|
|
13
13
|
super({
|
|
14
14
|
message: (0, i18n_1.translate)('error:INTERNAL_SERVER_ERROR', 'Internal server error'),
|
|
15
15
|
severity: 'error',
|
|
16
16
|
code: 'INTERNAL_SERVER_ERROR',
|
|
17
17
|
...response
|
|
18
|
-
},
|
|
18
|
+
}, cause);
|
|
19
|
+
this.status = index_js_1.HttpStatus.INTERNAL_SERVER_ERROR;
|
|
19
20
|
}
|
|
20
21
|
}
|
|
21
22
|
exports.InternalServerError = InternalServerError;
|
|
@@ -10,13 +10,14 @@ const api_exception_js_1 = require("../api-exception.js");
|
|
|
10
10
|
* For example, an API may not allow calling DELETE to remove a resource.
|
|
11
11
|
*/
|
|
12
12
|
class MethodNotAllowedError extends api_exception_js_1.ApiException {
|
|
13
|
-
constructor(response) {
|
|
13
|
+
constructor(response, cause) {
|
|
14
14
|
super({
|
|
15
15
|
message: (0, i18n_1.translate)('error:METHOD_NOT_ALLOWED', 'Method Not Allowed'),
|
|
16
16
|
severity: 'error',
|
|
17
17
|
code: 'METHOD_NOT_ALLOWED',
|
|
18
18
|
...response
|
|
19
|
-
},
|
|
19
|
+
}, cause);
|
|
20
|
+
this.status = index_js_1.HttpStatus.METHOD_NOT_ALLOWED;
|
|
20
21
|
}
|
|
21
22
|
}
|
|
22
23
|
exports.MethodNotAllowedError = MethodNotAllowedError;
|
|
@@ -13,13 +13,14 @@ const api_exception_js_1 = require("../api-exception.js");
|
|
|
13
13
|
* frequent occurrence on the web.
|
|
14
14
|
*/
|
|
15
15
|
class NotFoundError extends api_exception_js_1.ApiException {
|
|
16
|
-
constructor(response) {
|
|
16
|
+
constructor(response, cause) {
|
|
17
17
|
super({
|
|
18
18
|
message: (0, i18n_1.translate)('error:NOT_FOUND', 'Not found'),
|
|
19
19
|
severity: 'error',
|
|
20
20
|
code: 'NOT_FOUND',
|
|
21
21
|
...response
|
|
22
|
-
},
|
|
22
|
+
}, cause);
|
|
23
|
+
this.status = index_js_1.HttpStatus.NOT_FOUND;
|
|
23
24
|
}
|
|
24
25
|
}
|
|
25
26
|
exports.NotFoundError = NotFoundError;
|
|
@@ -10,13 +10,14 @@ const api_exception_js_1 = require("../api-exception.js");
|
|
|
10
10
|
* That is, the client must authenticate itself to get the requested response.
|
|
11
11
|
*/
|
|
12
12
|
class UnauthorizedError extends api_exception_js_1.ApiException {
|
|
13
|
-
constructor(response) {
|
|
13
|
+
constructor(response, cause) {
|
|
14
14
|
super({
|
|
15
15
|
message: (0, i18n_1.translate)('error:UNAUTHORIZED', 'Unauthorized'),
|
|
16
16
|
severity: 'error',
|
|
17
17
|
code: 'UNAUTHORIZED',
|
|
18
18
|
...response
|
|
19
|
-
},
|
|
19
|
+
}, cause);
|
|
20
|
+
this.status = index_js_1.HttpStatus.UNAUTHORIZED;
|
|
20
21
|
}
|
|
21
22
|
}
|
|
22
23
|
exports.UnauthorizedError = UnauthorizedError;
|
|
@@ -9,13 +9,14 @@ const api_exception_js_1 = require("../api-exception.js");
|
|
|
9
9
|
* The request was well-formed but was unable to be followed due to semantic errors.
|
|
10
10
|
*/
|
|
11
11
|
class UnprocessableEntityError extends api_exception_js_1.ApiException {
|
|
12
|
-
constructor(response) {
|
|
12
|
+
constructor(response, cause) {
|
|
13
13
|
super({
|
|
14
14
|
message: (0, i18n_1.translate)('error:UNPROCESSABLE_ENTITY', 'Unprocessable entity'),
|
|
15
15
|
severity: 'error',
|
|
16
16
|
code: 'UNPROCESSABLE_ENTITY',
|
|
17
17
|
...response
|
|
18
|
-
},
|
|
18
|
+
}, cause);
|
|
19
|
+
this.status = index_js_1.HttpStatus.UNPROCESSABLE_ENTITY;
|
|
19
20
|
}
|
|
20
21
|
}
|
|
21
22
|
exports.UnprocessableEntityError = UnprocessableEntityError;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.wrapError = void 0;
|
|
4
|
+
const api_exception_js_1 = require("./api-exception.js");
|
|
5
|
+
const internal_server_error_js_1 = require("./errors/internal-server.error.js");
|
|
6
|
+
function wrapError(response) {
|
|
7
|
+
if (response instanceof api_exception_js_1.ApiException)
|
|
8
|
+
return response;
|
|
9
|
+
if (response instanceof Error) {
|
|
10
|
+
const x = response;
|
|
11
|
+
if (typeof x.status === 'number' || typeof x.getStatus === 'function')
|
|
12
|
+
return new api_exception_js_1.ApiException(response);
|
|
13
|
+
return new internal_server_error_js_1.InternalServerError(undefined, response);
|
|
14
|
+
}
|
|
15
|
+
return new internal_server_error_js_1.InternalServerError();
|
|
16
|
+
}
|
|
17
|
+
exports.wrapError = wrapError;
|
|
@@ -1,72 +1,100 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.OpraAdapter = void 0;
|
|
4
|
-
const types_1 = require("util/types");
|
|
5
4
|
const i18n_1 = require("@opra/i18n");
|
|
6
5
|
const index_js_1 = require("../../exception/index.js");
|
|
6
|
+
const wrap_error_js_1 = require("../../exception/wrap-error.js");
|
|
7
7
|
class OpraAdapter {
|
|
8
8
|
service;
|
|
9
9
|
i18n;
|
|
10
|
-
constructor(service,
|
|
10
|
+
constructor(service, i18n) {
|
|
11
11
|
this.service = service;
|
|
12
|
-
this.i18n =
|
|
12
|
+
this.i18n = i18n || i18n_1.I18n.defaultInstance;
|
|
13
13
|
}
|
|
14
|
-
async handler(adapterContext,
|
|
15
|
-
|
|
14
|
+
async handler(adapterContext, userContextResolver) {
|
|
15
|
+
if (!this.i18n.isInitialized)
|
|
16
|
+
await this.i18n.init();
|
|
17
|
+
const executionContexts = [];
|
|
18
|
+
let requests;
|
|
19
|
+
let userContext;
|
|
16
20
|
try {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
let stop = false;
|
|
25
|
-
// Read requests can be executed simultaneously, write request should be executed one by one
|
|
26
|
-
let promises;
|
|
27
|
-
let exclusive = false;
|
|
28
|
-
for (const ctx of executionContexts) {
|
|
29
|
-
const request = ctx.request;
|
|
30
|
-
const response = ctx.response;
|
|
31
|
-
exclusive = exclusive || request.query.operationType !== 'read';
|
|
32
|
-
try {
|
|
21
|
+
requests = this.prepareRequests(adapterContext);
|
|
22
|
+
let stop = false;
|
|
23
|
+
// Read requests can be executed simultaneously, write request should be executed one by one
|
|
24
|
+
let promises;
|
|
25
|
+
let exclusive = false;
|
|
26
|
+
for (const request of requests) {
|
|
27
|
+
exclusive = exclusive || request.query.operationType !== 'read';
|
|
33
28
|
// Wait previous read requests before executing update request
|
|
34
29
|
if (exclusive && promises) {
|
|
35
|
-
await Promise.
|
|
30
|
+
await Promise.allSettled(promises);
|
|
36
31
|
promises = undefined;
|
|
37
32
|
}
|
|
33
|
+
const resource = request.query.resource;
|
|
34
|
+
const context = this.createExecutionContext(adapterContext, request);
|
|
35
|
+
executionContexts.push(context);
|
|
38
36
|
// If previous request in bucket had an error and executed an update
|
|
39
37
|
// we do not execute next requests
|
|
40
38
|
if (stop) {
|
|
41
|
-
response.errors.push(new index_js_1.FailedDependencyError());
|
|
39
|
+
context.response.errors.push(new index_js_1.FailedDependencyError());
|
|
40
|
+
continue;
|
|
42
41
|
}
|
|
43
|
-
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
42
|
+
try {
|
|
43
|
+
const promise = (async () => {
|
|
44
|
+
await resource.prepare(context);
|
|
45
|
+
if (userContextResolver && !userContext)
|
|
46
|
+
userContext = userContextResolver(this.isBatch(adapterContext));
|
|
47
|
+
context.userContext = userContext;
|
|
48
|
+
await resource.execute(context);
|
|
49
|
+
})().catch(e => {
|
|
50
|
+
context.response.errors.push(e);
|
|
51
|
+
});
|
|
52
|
+
if (exclusive)
|
|
53
|
+
await promise;
|
|
54
|
+
else {
|
|
55
|
+
promises = promises || [];
|
|
56
|
+
promises.push(promise);
|
|
53
57
|
}
|
|
54
58
|
// todo execute sub property queries
|
|
55
59
|
}
|
|
60
|
+
catch (e) {
|
|
61
|
+
context.response.errors.unshift(e);
|
|
62
|
+
}
|
|
63
|
+
if (context.response.errors.length) {
|
|
64
|
+
// noinspection SuspiciousTypeOfGuard
|
|
65
|
+
context.response.errors = context.response.errors.map(e => (0, wrap_error_js_1.wrapError)(e));
|
|
66
|
+
if (exclusive)
|
|
67
|
+
stop = stop || !!context.response.errors.find(e => !(e.response.severity === 'warning' || e.response.severity === 'info'));
|
|
68
|
+
}
|
|
56
69
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
70
|
+
if (promises)
|
|
71
|
+
await Promise.allSettled(promises);
|
|
72
|
+
if (userContext && typeof userContext.onRequestFinish === 'function') {
|
|
73
|
+
try {
|
|
74
|
+
const hasError = !!executionContexts.find(ctx => ctx.response.errors?.length);
|
|
75
|
+
await userContext.onRequestFinish(hasError);
|
|
76
|
+
}
|
|
77
|
+
catch (e) {
|
|
78
|
+
await this.sendError(adapterContext, (0, wrap_error_js_1.wrapError)(e));
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
65
81
|
}
|
|
82
|
+
await this.sendResponse(adapterContext, executionContexts);
|
|
66
83
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
84
|
+
catch (e) {
|
|
85
|
+
const error = (0, wrap_error_js_1.wrapError)(e);
|
|
86
|
+
await this.sendError(adapterContext, error);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
static async initI18n(options) {
|
|
91
|
+
if (options?.i18n instanceof i18n_1.I18n)
|
|
92
|
+
return options.i18n;
|
|
93
|
+
if (typeof options?.i18n === 'function')
|
|
94
|
+
return options.i18n();
|
|
95
|
+
const instance = i18n_1.I18n.createInstance(options?.i18n);
|
|
96
|
+
await instance.init();
|
|
97
|
+
return instance;
|
|
70
98
|
}
|
|
71
99
|
}
|
|
72
100
|
exports.OpraAdapter = OpraAdapter;
|
|
@@ -4,20 +4,20 @@ exports.OpraExpressAdapter = void 0;
|
|
|
4
4
|
const url_1 = require("@opra/url");
|
|
5
5
|
const http_adapter_js_1 = require("./http-adapter.js");
|
|
6
6
|
class OpraExpressAdapter extends http_adapter_js_1.OpraHttpAdapter {
|
|
7
|
-
static init(app, service, options) {
|
|
8
|
-
const
|
|
7
|
+
static async init(app, service, options) {
|
|
8
|
+
const i18n = await this.initI18n(options);
|
|
9
|
+
const adapter = new OpraExpressAdapter(service, i18n);
|
|
9
10
|
const prefix = '/' + (0, url_1.normalizePath)(options?.prefix, true);
|
|
10
11
|
const userContextResolver = options?.userContext;
|
|
11
12
|
app.use(prefix, (request, response, next) => {
|
|
12
13
|
(async () => {
|
|
13
|
-
const userContext = userContextResolver && await userContextResolver(request);
|
|
14
14
|
const req = new ExpressRequestWrapper(request);
|
|
15
15
|
const res = new ExpressResponseWrapper(response);
|
|
16
16
|
const adapterContext = {
|
|
17
17
|
getRequest: () => req,
|
|
18
18
|
getResponse: () => res
|
|
19
19
|
};
|
|
20
|
-
await adapter.handler(adapterContext,
|
|
20
|
+
await adapter.handler(adapterContext, (isBatch) => userContextResolver && userContextResolver(request, { platform: 'express', isBatch }));
|
|
21
21
|
})().catch(e => next(e));
|
|
22
22
|
});
|
|
23
23
|
return adapter;
|
|
@@ -5,24 +5,27 @@ const url_1 = require("@opra/url");
|
|
|
5
5
|
const constants_js_1 = require("../../constants.js");
|
|
6
6
|
const index_js_1 = require("../../enums/index.js");
|
|
7
7
|
const index_js_2 = require("../../exception/index.js");
|
|
8
|
+
const wrap_error_js_1 = require("../../exception/wrap-error.js");
|
|
8
9
|
const execution_query_interface_js_1 = require("../../interfaces/execution-query.interface.js");
|
|
9
10
|
const headers_js_1 = require("../../utils/headers.js");
|
|
10
11
|
const complex_type_js_1 = require("../data-type/complex-type.js");
|
|
11
12
|
const execution_context_js_1 = require("../execution-context.js");
|
|
12
|
-
const
|
|
13
|
-
const
|
|
13
|
+
const container_resource_handler_js_1 = require("../resource/container-resource-handler.js");
|
|
14
|
+
const entity_resource_handler_js_1 = require("../resource/entity-resource-handler.js");
|
|
14
15
|
const adapter_js_1 = require("./adapter.js");
|
|
15
16
|
class OpraHttpAdapter extends adapter_js_1.OpraAdapter {
|
|
16
|
-
|
|
17
|
+
prepareRequests(adapterContext) {
|
|
17
18
|
const req = adapterContext.getRequest();
|
|
18
19
|
// todo implement batch requests
|
|
19
20
|
if (this.isBatch(adapterContext)) {
|
|
20
21
|
throw new Error('not implemented yet');
|
|
21
22
|
}
|
|
22
23
|
const url = new url_1.OpraURL(req.getUrl());
|
|
23
|
-
return [
|
|
24
|
+
return [
|
|
25
|
+
this.prepareRequest(adapterContext, url, req.getMethod(), headers_js_1.Headers.from(req.getHeaders()), req.getBody())
|
|
26
|
+
];
|
|
24
27
|
}
|
|
25
|
-
|
|
28
|
+
prepareRequest(adapterContext, url, method, headers, body) {
|
|
26
29
|
if (!url.path.size)
|
|
27
30
|
throw new index_js_2.BadRequestError();
|
|
28
31
|
if (method !== 'GET' && url.path.size > 1)
|
|
@@ -32,23 +35,21 @@ class OpraHttpAdapter extends adapter_js_1.OpraAdapter {
|
|
|
32
35
|
throw new index_js_2.MethodNotAllowedError({
|
|
33
36
|
message: `Method "${method}" is not allowed by target resource`
|
|
34
37
|
});
|
|
35
|
-
|
|
38
|
+
return new execution_context_js_1.ExecutionRequest({
|
|
36
39
|
query,
|
|
37
40
|
headers,
|
|
38
41
|
params: url.searchParams,
|
|
39
42
|
});
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
+
}
|
|
44
|
+
createExecutionContext(adapterContext, request) {
|
|
45
|
+
return new execution_context_js_1.ExecutionContext({
|
|
43
46
|
type: 'http',
|
|
44
47
|
service: this.service,
|
|
45
48
|
request,
|
|
46
|
-
response,
|
|
49
|
+
response: new execution_context_js_1.ExecutionResponse(),
|
|
47
50
|
adapterContext,
|
|
48
|
-
userContext,
|
|
49
51
|
continueOnError: request.query.operationType === 'read'
|
|
50
52
|
});
|
|
51
|
-
return executionContext;
|
|
52
53
|
}
|
|
53
54
|
buildQuery(url, method, body) {
|
|
54
55
|
let container = this.service;
|
|
@@ -59,12 +60,12 @@ class OpraHttpAdapter extends adapter_js_1.OpraAdapter {
|
|
|
59
60
|
let p = url.path.get(pathIndex++);
|
|
60
61
|
const resource = container.getResource(p.resource);
|
|
61
62
|
// Move through path directories (containers)
|
|
62
|
-
if (resource instanceof
|
|
63
|
+
if (resource instanceof container_resource_handler_js_1.ContainerResourceHandler) {
|
|
63
64
|
container = resource;
|
|
64
65
|
}
|
|
65
66
|
else {
|
|
66
67
|
method = method.toUpperCase();
|
|
67
|
-
if (resource instanceof
|
|
68
|
+
if (resource instanceof entity_resource_handler_js_1.EntityResourceHandler) {
|
|
68
69
|
const scope = p.key ? 'instance' : 'collection';
|
|
69
70
|
if (pathIndex < pathLen && !(method === 'GET' && scope === 'instance'))
|
|
70
71
|
return;
|
|
@@ -77,7 +78,7 @@ class OpraHttpAdapter extends adapter_js_1.OpraAdapter {
|
|
|
77
78
|
limit: url.searchParams.get('$limit'),
|
|
78
79
|
skip: url.searchParams.get('$skip'),
|
|
79
80
|
distinct: url.searchParams.get('$distinct'),
|
|
80
|
-
|
|
81
|
+
count: url.searchParams.get('$count'),
|
|
81
82
|
sort: url.searchParams.get('$sort'),
|
|
82
83
|
pick: url.searchParams.get('$pick'),
|
|
83
84
|
omit: url.searchParams.get('$omit'),
|
|
@@ -85,7 +86,7 @@ class OpraHttpAdapter extends adapter_js_1.OpraAdapter {
|
|
|
85
86
|
});
|
|
86
87
|
}
|
|
87
88
|
else {
|
|
88
|
-
query = execution_query_interface_js_1.ExecutionQuery.
|
|
89
|
+
query = execution_query_interface_js_1.ExecutionQuery.forGet(resource, p.key, {
|
|
89
90
|
pick: url.searchParams.get('$pick'),
|
|
90
91
|
omit: url.searchParams.get('$omit'),
|
|
91
92
|
include: url.searchParams.get('$include')
|
|
@@ -104,7 +105,7 @@ class OpraHttpAdapter extends adapter_js_1.OpraAdapter {
|
|
|
104
105
|
const prop = dataType.properties?.[p.resource];
|
|
105
106
|
if (!prop)
|
|
106
107
|
throw new index_js_2.NotFoundError({ message: `Invalid or unknown resource path (${path})` });
|
|
107
|
-
const q = execution_query_interface_js_1.ExecutionQuery.
|
|
108
|
+
const q = execution_query_interface_js_1.ExecutionQuery.forGetProperty(prop);
|
|
108
109
|
if (nested) {
|
|
109
110
|
nested.nested = q;
|
|
110
111
|
}
|
|
@@ -205,43 +206,25 @@ class OpraHttpAdapter extends adapter_js_1.OpraAdapter {
|
|
|
205
206
|
}
|
|
206
207
|
createOutput(ctx) {
|
|
207
208
|
const { query } = ctx.request;
|
|
208
|
-
// Determine response status
|
|
209
209
|
let status = ctx.response.status;
|
|
210
|
-
|
|
210
|
+
let body = ctx.response.value || {};
|
|
211
|
+
const errors = ctx.response.errors?.map(e => (0, wrap_error_js_1.wrapError)(e));
|
|
212
|
+
if (errors && errors.length) {
|
|
211
213
|
if (!status || status < 400) {
|
|
212
214
|
status = 0;
|
|
213
|
-
for (const e of
|
|
215
|
+
for (const e of errors) {
|
|
214
216
|
status = Math.max(status, e.status || status);
|
|
215
217
|
}
|
|
216
218
|
if (status < index_js_1.HttpStatus.BAD_REQUEST)
|
|
217
219
|
status = index_js_1.HttpStatus.INTERNAL_SERVER_ERROR;
|
|
218
220
|
}
|
|
219
|
-
|
|
220
|
-
else
|
|
221
|
-
status = status || (query.operationType === 'create' ? index_js_1.HttpStatus.CREATED : index_js_1.HttpStatus.OK);
|
|
222
|
-
let body;
|
|
223
|
-
let value = ctx.response.value;
|
|
224
|
-
if (query.queryType === 'search') {
|
|
225
|
-
body = {
|
|
226
|
-
// '@origin': ctx.resource.name + (ctx.request.resultPath ? '.' + ctx.request.resultPath : ''),
|
|
227
|
-
items: ctx.response.value,
|
|
228
|
-
total: ctx.response.total
|
|
229
|
-
};
|
|
221
|
+
body.errors = errors.map(e => e.response);
|
|
230
222
|
}
|
|
231
223
|
else {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
const pathArray = ctx.request.resultPath.split('.');
|
|
235
|
-
for (const property of pathArray) {
|
|
236
|
-
value = value && typeof value === 'object' && value[property];
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
body = value;
|
|
240
|
-
}
|
|
241
|
-
if (ctx.response.errors?.length) {
|
|
242
|
-
body = body || {};
|
|
243
|
-
body.errors = ctx.response.errors.map(e => e.response);
|
|
224
|
+
delete body.errors;
|
|
225
|
+
status = status || (query.operationType === 'create' ? index_js_1.HttpStatus.CREATED : index_js_1.HttpStatus.OK);
|
|
244
226
|
}
|
|
227
|
+
body = this.i18n.deep(body);
|
|
245
228
|
return {
|
|
246
229
|
status,
|
|
247
230
|
headers: ctx.response.headers,
|
|
@@ -5,7 +5,7 @@ const schema_1 = require("@opra/schema");
|
|
|
5
5
|
const responsive_object_js_1 = require("../utils/responsive-object.js");
|
|
6
6
|
const entity_type_js_1 = require("./data-type/entity-type.js");
|
|
7
7
|
const opra_document_js_1 = require("./opra-document.js");
|
|
8
|
-
const
|
|
8
|
+
const entity_resource_handler_js_1 = require("./resource/entity-resource-handler.js");
|
|
9
9
|
const schema_generator_js_1 = require("./schema-generator.js");
|
|
10
10
|
class OpraService extends opra_document_js_1.OpraDocument {
|
|
11
11
|
_resources = (0, responsive_object_js_1.Responsive)();
|
|
@@ -28,7 +28,7 @@ class OpraService extends opra_document_js_1.OpraDocument {
|
|
|
28
28
|
}
|
|
29
29
|
getEntityResource(name) {
|
|
30
30
|
const t = this.getResource(name);
|
|
31
|
-
if (!(t instanceof
|
|
31
|
+
if (!(t instanceof entity_resource_handler_js_1.EntityResourceHandler))
|
|
32
32
|
throw new Error(`"${name}" is not an EntityResource`);
|
|
33
33
|
return t;
|
|
34
34
|
}
|
|
@@ -40,7 +40,7 @@ class OpraService extends opra_document_js_1.OpraDocument {
|
|
|
40
40
|
throw new TypeError(`Datatype "${r.type}" declared in EntityResource (${r.name}) does not exists`);
|
|
41
41
|
if (!(dataType instanceof entity_type_js_1.EntityType))
|
|
42
42
|
throw new TypeError(`${r.type} is not an EntityType`);
|
|
43
|
-
this.resources[r.name] = new
|
|
43
|
+
this.resources[r.name] = new entity_resource_handler_js_1.EntityResourceHandler({ ...r, service: this, dataType });
|
|
44
44
|
}
|
|
45
45
|
else
|
|
46
46
|
throw new TypeError(`Unknown resource kind (${r.kind})`);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ContainerResourceHandler = void 0;
|
|
4
|
+
const entity_resource_handler_js_1 = require("./entity-resource-handler.js");
|
|
5
|
+
const resource_handler_js_1 = require("./resource-handler.js");
|
|
6
|
+
class ContainerResourceHandler extends resource_handler_js_1.ResourceHandler {
|
|
7
|
+
constructor(args) {
|
|
8
|
+
super({
|
|
9
|
+
kind: 'ContainerResource',
|
|
10
|
+
...args
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
getResource(name) {
|
|
14
|
+
const t = this._args.resources[name];
|
|
15
|
+
if (!t)
|
|
16
|
+
throw new Error(`Resource "${name}" does not exists`);
|
|
17
|
+
return t;
|
|
18
|
+
}
|
|
19
|
+
getEntityResource(name) {
|
|
20
|
+
const t = this.getResource(name);
|
|
21
|
+
if (!(t instanceof entity_resource_handler_js_1.EntityResourceHandler))
|
|
22
|
+
throw new Error(`"${name}" is not an EntityResource`);
|
|
23
|
+
return t;
|
|
24
|
+
}
|
|
25
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
26
|
+
execute(ctx) {
|
|
27
|
+
return Promise.resolve(undefined);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
exports.ContainerResourceHandler = ContainerResourceHandler;
|