@opra/core 0.0.12 → 0.1.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.
- package/cjs/enums/http-headers.enum.js +2 -1
- package/cjs/exception/http-errors/not-acceptable.error.js +26 -0
- package/cjs/exception/index.js +2 -0
- package/cjs/exception/resource-errors/resource-not-found.error.js +19 -0
- package/cjs/implementation/adapter-utils/entity-resource-execute.util.js +84 -0
- package/cjs/implementation/adapter-utils/resource-execute.util.js +11 -0
- package/cjs/implementation/adapter-utils/resource-prepare.util.js +11 -0
- package/cjs/implementation/{adapter/adapter.js → adapter.js} +40 -7
- package/cjs/implementation/{adapter/express-adapter.js → express-adapter.js} +0 -0
- package/cjs/implementation/http-adapter.js +267 -0
- package/cjs/implementation/query-context.js +5 -3
- package/cjs/index.js +5 -15
- package/cjs/{services/entity-resource-controller.js → interfaces/entity-service.interface.js} +0 -0
- package/cjs/interfaces/query.interface.js +26 -9
- package/cjs/services/json-data-service.js +241 -4
- package/cjs/utils/create-i18n.js +1 -1
- package/cjs/utils/get-caller-file.util.js +6 -1
- package/cjs/utils/{string-path-to-object-tree.js → path-to-tree.js} +3 -3
- package/esm/enums/http-headers.enum.d.ts +2 -1
- package/esm/enums/http-headers.enum.js +2 -1
- package/esm/exception/http-errors/not-acceptable.error.d.ts +10 -0
- package/esm/exception/http-errors/not-acceptable.error.js +22 -0
- package/esm/exception/index.d.ts +2 -0
- package/esm/exception/index.js +2 -0
- package/esm/exception/resource-errors/resource-not-found.error.d.ts +4 -0
- package/esm/exception/resource-errors/resource-not-found.error.js +15 -0
- package/esm/implementation/adapter-utils/entity-resource-execute.util.d.ts +3 -0
- package/esm/implementation/adapter-utils/entity-resource-execute.util.js +80 -0
- package/esm/implementation/adapter-utils/resource-execute.util.d.ts +3 -0
- package/esm/implementation/adapter-utils/resource-execute.util.js +7 -0
- package/esm/implementation/adapter-utils/resource-prepare.util.d.ts +3 -0
- package/esm/implementation/adapter-utils/resource-prepare.util.js +7 -0
- package/esm/implementation/{adapter/adapter.d.ts → adapter.d.ts} +5 -9
- package/esm/implementation/{adapter/adapter.js → adapter.js} +39 -6
- package/esm/implementation/{adapter/express-adapter.d.ts → express-adapter.d.ts} +2 -2
- package/esm/implementation/{adapter/express-adapter.js → express-adapter.js} +0 -0
- package/esm/implementation/{adapter/http-adapter.d.ts → http-adapter.d.ts} +6 -6
- package/esm/implementation/http-adapter.js +263 -0
- package/esm/implementation/query-context.d.ts +3 -4
- package/esm/implementation/query-context.js +5 -3
- package/esm/index.d.ts +5 -15
- package/esm/index.js +5 -15
- package/esm/{services/entity-resource-controller.d.ts → interfaces/entity-service.interface.d.ts} +0 -0
- package/esm/{services/entity-resource-controller.js → interfaces/entity-service.interface.js} +0 -0
- package/esm/interfaces/query.interface.d.ts +31 -25
- package/esm/interfaces/query.interface.js +26 -9
- package/esm/services/json-data-service.d.ts +66 -7
- package/esm/services/json-data-service.js +241 -4
- package/esm/types.d.ts +10 -8
- package/esm/utils/create-i18n.d.ts +1 -1
- package/esm/utils/create-i18n.js +1 -1
- package/esm/utils/get-caller-file.util.d.ts +1 -1
- package/esm/utils/get-caller-file.util.js +6 -1
- package/esm/utils/path-to-tree.d.ts +4 -0
- package/esm/utils/{string-path-to-object-tree.js → path-to-tree.js} +1 -1
- package/i18n/en/error.json +3 -0
- package/package.json +6 -7
- package/cjs/constants.js +0 -5
- package/cjs/decorators/entity-resource.decorator.js +0 -24
- package/cjs/implementation/adapter/http-adapter.js +0 -238
- package/cjs/implementation/data-type/complex-type.js +0 -39
- package/cjs/implementation/data-type/data-type.js +0 -35
- package/cjs/implementation/data-type/entity-type.js +0 -33
- package/cjs/implementation/data-type/simple-type.js +0 -30
- package/cjs/implementation/opra-document.js +0 -116
- package/cjs/implementation/opra-service.js +0 -59
- package/cjs/implementation/resource/container-resource-handler.js +0 -30
- package/cjs/implementation/resource/entity-resource-handler.js +0 -80
- package/cjs/implementation/resource/resource-handler.js +0 -31
- package/cjs/implementation/schema-generator.js +0 -163
- package/cjs/interfaces/opra-schema.metadata.js +0 -2
- package/cjs/interfaces/resource-container.interface.js +0 -2
- package/cjs/utils/class-utils.js +0 -37
- package/cjs/utils/headers.js +0 -58
- package/cjs/utils/internal-data-types.js +0 -81
- package/cjs/utils/responsive-object.js +0 -49
- package/cjs/utils/terminal-utils.js +0 -7
- package/esm/constants.d.ts +0 -2
- package/esm/constants.js +0 -2
- package/esm/decorators/entity-resource.decorator.d.ts +0 -5
- package/esm/decorators/entity-resource.decorator.js +0 -19
- package/esm/implementation/adapter/http-adapter.js +0 -234
- package/esm/implementation/data-type/complex-type.d.ts +0 -18
- package/esm/implementation/data-type/complex-type.js +0 -35
- package/esm/implementation/data-type/data-type.d.ts +0 -15
- package/esm/implementation/data-type/data-type.js +0 -31
- package/esm/implementation/data-type/entity-type.d.ts +0 -10
- package/esm/implementation/data-type/entity-type.js +0 -29
- package/esm/implementation/data-type/simple-type.d.ts +0 -15
- package/esm/implementation/data-type/simple-type.js +0 -26
- package/esm/implementation/opra-document.d.ts +0 -26
- package/esm/implementation/opra-document.js +0 -111
- package/esm/implementation/opra-service.d.ts +0 -19
- package/esm/implementation/opra-service.js +0 -55
- package/esm/implementation/resource/container-resource-handler.d.ts +0 -14
- package/esm/implementation/resource/container-resource-handler.js +0 -26
- package/esm/implementation/resource/entity-resource-handler.d.ts +0 -18
- package/esm/implementation/resource/entity-resource-handler.js +0 -75
- package/esm/implementation/resource/resource-handler.d.ts +0 -15
- package/esm/implementation/resource/resource-handler.js +0 -27
- package/esm/implementation/schema-generator.d.ts +0 -21
- package/esm/implementation/schema-generator.js +0 -159
- package/esm/interfaces/opra-schema.metadata.d.ts +0 -14
- package/esm/interfaces/opra-schema.metadata.js +0 -1
- package/esm/interfaces/resource-container.interface.d.ts +0 -6
- package/esm/interfaces/resource-container.interface.js +0 -1
- package/esm/utils/class-utils.d.ts +0 -6
- package/esm/utils/class-utils.js +0 -30
- package/esm/utils/headers.d.ts +0 -9
- package/esm/utils/headers.js +0 -55
- package/esm/utils/internal-data-types.d.ts +0 -5
- package/esm/utils/internal-data-types.js +0 -78
- package/esm/utils/responsive-object.d.ts +0 -3
- package/esm/utils/responsive-object.js +0 -45
- package/esm/utils/string-path-to-object-tree.d.ts +0 -4
- package/esm/utils/terminal-utils.d.ts +0 -4
- package/esm/utils/terminal-utils.js +0 -4
|
@@ -8,7 +8,8 @@ exports.HttpHeaders = void 0;
|
|
|
8
8
|
var HttpHeaders;
|
|
9
9
|
(function (HttpHeaders) {
|
|
10
10
|
/* *** Custom Headers *** */
|
|
11
|
-
HttpHeaders["X_Opra_Version"] = "X-
|
|
11
|
+
HttpHeaders["X_Opra_Version"] = "X-OPRA-Version";
|
|
12
|
+
HttpHeaders["X_Opra_Schema"] = "X-OPRA-Schema";
|
|
12
13
|
/* *** Authentication *** */
|
|
13
14
|
/**
|
|
14
15
|
* Defines the authentication method that should be used to access a resource.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NotAcceptableError = void 0;
|
|
4
|
+
const i18n_1 = require("@opra/i18n");
|
|
5
|
+
const index_js_1 = require("../../enums/index.js");
|
|
6
|
+
const api_exception_js_1 = require("../api-exception.js");
|
|
7
|
+
/**
|
|
8
|
+
* 406 Not Acceptable
|
|
9
|
+
* This response is sent when the web server, after performing server-driven content negotiation,
|
|
10
|
+
* doesn't find any content that conforms to the criteria given by the user agent.
|
|
11
|
+
*/
|
|
12
|
+
class NotAcceptableError extends api_exception_js_1.ApiException {
|
|
13
|
+
constructor(response, cause) {
|
|
14
|
+
super(response, cause);
|
|
15
|
+
this.status = index_js_1.HttpStatus.NOT_ACCEPTABLE;
|
|
16
|
+
}
|
|
17
|
+
_initResponse(response) {
|
|
18
|
+
super._initResponse({
|
|
19
|
+
message: (0, i18n_1.translate)('error:NOT_ACCEPTABLE', 'Not Acceptable'),
|
|
20
|
+
severity: 'error',
|
|
21
|
+
code: 'NOT_ACCEPTABLE',
|
|
22
|
+
...response
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
exports.NotAcceptableError = NotAcceptableError;
|
package/cjs/exception/index.js
CHANGED
|
@@ -7,7 +7,9 @@ tslib_1.__exportStar(require("./http-errors/failed-dependency.error.js"), export
|
|
|
7
7
|
tslib_1.__exportStar(require("./http-errors/forbidden.error.js"), exports);
|
|
8
8
|
tslib_1.__exportStar(require("./http-errors/internal-server.error.js"), exports);
|
|
9
9
|
tslib_1.__exportStar(require("./http-errors/method-not-allowed.error.js"), exports);
|
|
10
|
+
tslib_1.__exportStar(require("./http-errors/not-acceptable.error.js"), exports);
|
|
10
11
|
tslib_1.__exportStar(require("./http-errors/not-found.error.js"), exports);
|
|
11
12
|
tslib_1.__exportStar(require("./http-errors/unauthorized.error.js"), exports);
|
|
12
13
|
tslib_1.__exportStar(require("./http-errors/unprocessable-entity.error.js"), exports);
|
|
13
14
|
tslib_1.__exportStar(require("./resource-errors/resource-conflict.error.js"), exports);
|
|
15
|
+
tslib_1.__exportStar(require("./resource-errors/resource-not-found.error.js"), exports);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ResourceNotFoundError = void 0;
|
|
4
|
+
const i18n_1 = require("@opra/i18n");
|
|
5
|
+
const index_js_1 = require("../../enums/index.js");
|
|
6
|
+
const api_exception_js_1 = require("../api-exception.js");
|
|
7
|
+
class ResourceNotFoundError extends api_exception_js_1.ApiException {
|
|
8
|
+
constructor(resource, keyValue, cause) {
|
|
9
|
+
super({
|
|
10
|
+
message: (0, i18n_1.translate)(`error:RESOURCE_NOT_FOUND`, { resource: resource + '@' + keyValue }),
|
|
11
|
+
severity: 'error',
|
|
12
|
+
code: 'RESOURCE_NOT_FOUND',
|
|
13
|
+
subject: resource,
|
|
14
|
+
key: keyValue
|
|
15
|
+
}, cause);
|
|
16
|
+
this.status = index_js_1.HttpStatus.NOT_FOUND;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.ResourceNotFoundError = ResourceNotFoundError;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.entityResourceExecute = void 0;
|
|
4
|
+
const i18n_1 = require("@opra/i18n");
|
|
5
|
+
const schema_1 = require("@opra/schema");
|
|
6
|
+
const index_js_1 = require("../../enums/index.js");
|
|
7
|
+
const index_js_2 = require("../../exception/index.js");
|
|
8
|
+
const query_interface_js_1 = require("../../interfaces/query.interface.js");
|
|
9
|
+
async function entityResourceExecute(service, resource, context) {
|
|
10
|
+
const { query } = context;
|
|
11
|
+
if (query_interface_js_1.OpraQuery.isSearchQuery(query)) {
|
|
12
|
+
const promises = [];
|
|
13
|
+
let search;
|
|
14
|
+
let count;
|
|
15
|
+
promises.push(executeFn(service, resource, context, query.queryType)
|
|
16
|
+
.then(v => search = v));
|
|
17
|
+
if (query.count) {
|
|
18
|
+
promises.push(executeFn(service, resource, context, 'count')
|
|
19
|
+
.then(v => count = v));
|
|
20
|
+
}
|
|
21
|
+
await Promise.all(promises);
|
|
22
|
+
context.response.value = {
|
|
23
|
+
...search,
|
|
24
|
+
...count
|
|
25
|
+
};
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
context.response.value = await executeFn(service, resource, context, query.queryType);
|
|
29
|
+
}
|
|
30
|
+
exports.entityResourceExecute = entityResourceExecute;
|
|
31
|
+
async function executeFn(service, resource, context, queryType) {
|
|
32
|
+
const resolverInfo = resource.metadata.methods?.[queryType];
|
|
33
|
+
if (!resolverInfo.handler)
|
|
34
|
+
throw new index_js_2.ForbiddenError({
|
|
35
|
+
message: (0, i18n_1.translate)('RESOLVER_FORBIDDEN', { queryType }),
|
|
36
|
+
severity: 'error',
|
|
37
|
+
code: 'RESOLVER_FORBIDDEN'
|
|
38
|
+
});
|
|
39
|
+
let result = await resolverInfo.handler(context);
|
|
40
|
+
switch (queryType) {
|
|
41
|
+
case 'search':
|
|
42
|
+
context.response.headers.set(index_js_1.HttpHeaders.X_Opra_Schema, '/$schema/types/' + resource.dataType.name);
|
|
43
|
+
return {
|
|
44
|
+
items: Array.isArray(result) ? result : (context.response.value ? [result] : [])
|
|
45
|
+
};
|
|
46
|
+
case 'get':
|
|
47
|
+
case 'update':
|
|
48
|
+
if (!result) {
|
|
49
|
+
const query = context.query;
|
|
50
|
+
throw new index_js_2.ResourceNotFoundError(resource.name, query.keyValue);
|
|
51
|
+
}
|
|
52
|
+
break;
|
|
53
|
+
case 'count':
|
|
54
|
+
return { count: result || 0 };
|
|
55
|
+
case 'delete':
|
|
56
|
+
case 'deleteMany':
|
|
57
|
+
case 'updateMany':
|
|
58
|
+
let affectedRecords;
|
|
59
|
+
if (typeof result === 'number')
|
|
60
|
+
affectedRecords = result;
|
|
61
|
+
if (typeof result === 'boolean')
|
|
62
|
+
affectedRecords = result ? 1 : 0;
|
|
63
|
+
if (typeof result === 'object')
|
|
64
|
+
affectedRecords = result.affectedRows || result.affectedRecords;
|
|
65
|
+
return { affectedRecords };
|
|
66
|
+
}
|
|
67
|
+
if (!result)
|
|
68
|
+
return;
|
|
69
|
+
result = Array.isArray(result) ? result[0] : result;
|
|
70
|
+
let dataType = resource.dataType;
|
|
71
|
+
if (context.resultPath) {
|
|
72
|
+
const pathArray = context.resultPath.split('.');
|
|
73
|
+
for (const field of pathArray) {
|
|
74
|
+
const prop = dataType instanceof schema_1.ComplexType ? dataType.fields.get(field) : undefined;
|
|
75
|
+
dataType = prop && prop.type ? service.types.get(prop.type) : undefined;
|
|
76
|
+
result = result && typeof result === 'object' && result[field];
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (queryType === 'create')
|
|
80
|
+
context.response.status = 201;
|
|
81
|
+
if (dataType)
|
|
82
|
+
context.response.headers.set(index_js_1.HttpHeaders.X_Opra_Schema, '/$schema/types/' + dataType.name);
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resourceExecute = void 0;
|
|
4
|
+
const schema_1 = require("@opra/schema");
|
|
5
|
+
const entity_resource_execute_util_js_1 = require("./entity-resource-execute.util.js");
|
|
6
|
+
async function resourceExecute(service, resource, context) {
|
|
7
|
+
if (resource instanceof schema_1.EntityResource)
|
|
8
|
+
return await (0, entity_resource_execute_util_js_1.entityResourceExecute)(service, resource, context);
|
|
9
|
+
throw new Error(`Executing "${resource.kind}" has not been implemented yet`);
|
|
10
|
+
}
|
|
11
|
+
exports.resourceExecute = resourceExecute;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resourcePrepare = void 0;
|
|
4
|
+
async function resourcePrepare(resource, context) {
|
|
5
|
+
const { query } = context;
|
|
6
|
+
const fn = resource.metadata['pre_' + query.queryType];
|
|
7
|
+
if (fn && typeof fn === 'function') {
|
|
8
|
+
await fn(context);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
exports.resourcePrepare = resourcePrepare;
|
|
@@ -3,9 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.OpraAdapter = void 0;
|
|
4
4
|
const strict_typed_events_1 = require("strict-typed-events");
|
|
5
5
|
const i18n_1 = require("@opra/i18n");
|
|
6
|
-
const index_js_1 = require("
|
|
7
|
-
const
|
|
8
|
-
const
|
|
6
|
+
const index_js_1 = require("../enums/index.js");
|
|
7
|
+
const index_js_2 = require("../exception/index.js");
|
|
8
|
+
const wrap_error_js_1 = require("../exception/wrap-error.js");
|
|
9
|
+
const create_i18n_js_1 = require("../utils/create-i18n.js");
|
|
10
|
+
const resource_execute_util_js_1 = require("./adapter-utils/resource-execute.util.js");
|
|
11
|
+
const resource_prepare_util_js_1 = require("./adapter-utils/resource-prepare.util.js");
|
|
9
12
|
class OpraAdapter {
|
|
10
13
|
service;
|
|
11
14
|
i18n;
|
|
@@ -37,20 +40,24 @@ class OpraAdapter {
|
|
|
37
40
|
// If previous request in bucket had an error and executed an update
|
|
38
41
|
// we do not execute next requests
|
|
39
42
|
if (stop) {
|
|
40
|
-
context.response.errors.push(new
|
|
43
|
+
context.response.errors.push(new index_js_2.FailedDependencyError());
|
|
41
44
|
continue;
|
|
42
45
|
}
|
|
43
46
|
try {
|
|
44
|
-
const resource = context.query.resource;
|
|
45
47
|
const promise = (async () => {
|
|
46
|
-
|
|
48
|
+
if (context.query.queryType === 'schema') {
|
|
49
|
+
await this._getSchemaExecute(context);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const resource = context.query.resource;
|
|
53
|
+
await (0, resource_prepare_util_js_1.resourcePrepare)(resource, context);
|
|
47
54
|
if (this.userContextResolver && !userContext)
|
|
48
55
|
userContext = this.userContextResolver({
|
|
49
56
|
executionContext,
|
|
50
57
|
isBatch: this.isBatch(executionContext)
|
|
51
58
|
});
|
|
52
59
|
context.userContext = userContext;
|
|
53
|
-
await
|
|
60
|
+
await (0, resource_execute_util_js_1.resourceExecute)(this.service, resource, context);
|
|
54
61
|
})().catch(e => {
|
|
55
62
|
context.response.errors.push(e);
|
|
56
63
|
});
|
|
@@ -91,6 +98,32 @@ class OpraAdapter {
|
|
|
91
98
|
}
|
|
92
99
|
}
|
|
93
100
|
}
|
|
101
|
+
async _getSchemaExecute(ctx) {
|
|
102
|
+
const query = ctx.query;
|
|
103
|
+
let out;
|
|
104
|
+
if (query.resourcePath.length > 2)
|
|
105
|
+
throw new index_js_2.BadRequestError();
|
|
106
|
+
if (query.resourcePath?.length) {
|
|
107
|
+
if (query.resourcePath[0] === 'resources') {
|
|
108
|
+
const resource = this.service.getResource(query.resourcePath[1]);
|
|
109
|
+
out = resource.getSchema(true);
|
|
110
|
+
query.resourcePath[1] = resource.name;
|
|
111
|
+
ctx.response.headers.set(index_js_1.HttpHeaders.X_Opra_Schema, 'http://www.oprajs.com/reference/v1/schema#' + resource.kind);
|
|
112
|
+
}
|
|
113
|
+
else if (query.resourcePath[0] === 'types') {
|
|
114
|
+
const dataType = this.service.getDataType(query.resourcePath[1]);
|
|
115
|
+
out = dataType.getSchema(true);
|
|
116
|
+
query.resourcePath[1] = dataType.name;
|
|
117
|
+
ctx.response.headers.set(index_js_1.HttpHeaders.X_Opra_Schema, 'http://www.oprajs.com/reference/v1/schema#' + dataType.kind);
|
|
118
|
+
}
|
|
119
|
+
else
|
|
120
|
+
throw new index_js_2.BadRequestError();
|
|
121
|
+
ctx.response.value = out;
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
ctx.response.headers.set(index_js_1.HttpHeaders.X_Opra_Schema, 'http://www.oprajs.com/reference/v1/schema');
|
|
125
|
+
ctx.response.value = this.service.getSchema(true);
|
|
126
|
+
}
|
|
94
127
|
static async initI18n(options) {
|
|
95
128
|
if (options?.i18n instanceof i18n_1.I18n)
|
|
96
129
|
return options.i18n;
|
|
File without changes
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OpraHttpAdapter = void 0;
|
|
4
|
+
const schema_1 = require("@opra/schema");
|
|
5
|
+
const url_1 = require("@opra/url");
|
|
6
|
+
const index_js_1 = require("../enums/index.js");
|
|
7
|
+
const index_js_2 = require("../exception/index.js");
|
|
8
|
+
const wrap_error_js_1 = require("../exception/wrap-error.js");
|
|
9
|
+
const query_interface_js_1 = require("../interfaces/query.interface.js");
|
|
10
|
+
const adapter_js_1 = require("./adapter.js");
|
|
11
|
+
const query_context_js_1 = require("./query-context.js");
|
|
12
|
+
class OpraHttpAdapter extends adapter_js_1.OpraAdapter {
|
|
13
|
+
prepareRequests(executionContext) {
|
|
14
|
+
const req = executionContext.getRequestWrapper();
|
|
15
|
+
// todo implement batch requests
|
|
16
|
+
if (this.isBatch(executionContext)) {
|
|
17
|
+
throw new Error('not implemented yet');
|
|
18
|
+
}
|
|
19
|
+
const url = new url_1.OpraURL(req.getUrl());
|
|
20
|
+
return [
|
|
21
|
+
this.prepareRequest(executionContext, url, req.getMethod(), new schema_1.ResponsiveMap(req.getHeaders()), req.getBody())
|
|
22
|
+
];
|
|
23
|
+
}
|
|
24
|
+
prepareRequest(executionContext, url, method, headers, body) {
|
|
25
|
+
if (!url.path.size)
|
|
26
|
+
throw new index_js_2.BadRequestError();
|
|
27
|
+
if (method !== 'GET' && url.path.size > 1)
|
|
28
|
+
throw new index_js_2.BadRequestError();
|
|
29
|
+
const query = this.buildQuery(url, method, body);
|
|
30
|
+
if (!query)
|
|
31
|
+
throw new index_js_2.MethodNotAllowedError({
|
|
32
|
+
message: `Method "${method}" is not allowed by target endpoint`
|
|
33
|
+
});
|
|
34
|
+
return new query_context_js_1.QueryContext({
|
|
35
|
+
service: this.service,
|
|
36
|
+
executionContext,
|
|
37
|
+
query,
|
|
38
|
+
headers,
|
|
39
|
+
params: url.searchParams,
|
|
40
|
+
continueOnError: query.operation === 'read'
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
buildGetSchemaQuery(url) {
|
|
44
|
+
const pathLen = url.path.size;
|
|
45
|
+
const resourcePath = [];
|
|
46
|
+
let pathIndex = 0;
|
|
47
|
+
while (pathIndex < pathLen) {
|
|
48
|
+
const p = url.path.get(pathIndex++);
|
|
49
|
+
if (p.key)
|
|
50
|
+
throw new index_js_2.BadRequestError();
|
|
51
|
+
if (p.resource !== '$schema') {
|
|
52
|
+
if (pathIndex === 1)
|
|
53
|
+
resourcePath.push('resources');
|
|
54
|
+
resourcePath.push(p.resource);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const opts = {
|
|
58
|
+
pick: url.searchParams.get('$pick'),
|
|
59
|
+
omit: url.searchParams.get('$omit'),
|
|
60
|
+
include: url.searchParams.get('$include'),
|
|
61
|
+
};
|
|
62
|
+
return query_interface_js_1.OpraQuery.forGetSchema(resourcePath, opts);
|
|
63
|
+
}
|
|
64
|
+
buildQuery(url, method, body) {
|
|
65
|
+
let container = this.service;
|
|
66
|
+
try {
|
|
67
|
+
const pathLen = url.path.size;
|
|
68
|
+
// Check if requesting metadata
|
|
69
|
+
for (let i = 0; i < pathLen; i++) {
|
|
70
|
+
const p = url.path.get(i);
|
|
71
|
+
if (p.resource === '$schema') {
|
|
72
|
+
if (method !== 'GET')
|
|
73
|
+
return;
|
|
74
|
+
return this.buildGetSchemaQuery(url);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
let pathIndex = 0;
|
|
78
|
+
while (pathIndex < pathLen) {
|
|
79
|
+
let p = url.path.get(pathIndex++);
|
|
80
|
+
const resource = container.getResource(p.resource);
|
|
81
|
+
// Move through path directories (containers)
|
|
82
|
+
if (resource instanceof schema_1.ContainerResource) {
|
|
83
|
+
container = resource;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
method = method.toUpperCase();
|
|
87
|
+
if (resource instanceof schema_1.EntityResource) {
|
|
88
|
+
const scope = p.key ? 'instance' : 'collection';
|
|
89
|
+
if (pathIndex < pathLen && !(method === 'GET' && scope === 'instance'))
|
|
90
|
+
return;
|
|
91
|
+
let query;
|
|
92
|
+
switch (method) {
|
|
93
|
+
case 'GET': {
|
|
94
|
+
if (scope === 'collection') {
|
|
95
|
+
query = query_interface_js_1.OpraQuery.forSearch(resource, {
|
|
96
|
+
filter: url.searchParams.get('$filter'),
|
|
97
|
+
limit: url.searchParams.get('$limit'),
|
|
98
|
+
skip: url.searchParams.get('$skip'),
|
|
99
|
+
distinct: url.searchParams.get('$distinct'),
|
|
100
|
+
count: url.searchParams.get('$count'),
|
|
101
|
+
sort: url.searchParams.get('$sort'),
|
|
102
|
+
pick: url.searchParams.get('$pick'),
|
|
103
|
+
omit: url.searchParams.get('$omit'),
|
|
104
|
+
include: url.searchParams.get('$include'),
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
query = query_interface_js_1.OpraQuery.forGetEntity(resource, p.key, {
|
|
109
|
+
pick: url.searchParams.get('$pick'),
|
|
110
|
+
omit: url.searchParams.get('$omit'),
|
|
111
|
+
include: url.searchParams.get('$include')
|
|
112
|
+
});
|
|
113
|
+
// Move through properties
|
|
114
|
+
let nested;
|
|
115
|
+
let path = resource.name;
|
|
116
|
+
while (pathIndex < pathLen) {
|
|
117
|
+
const dataType = nested
|
|
118
|
+
? this.service.getDataType(nested.property.type || 'string')
|
|
119
|
+
: query.resource.dataType;
|
|
120
|
+
if (!(dataType instanceof schema_1.ComplexType))
|
|
121
|
+
throw new Error(`"${path}" is not a ComplexType and has no fields.`);
|
|
122
|
+
p = url.path.get(pathIndex++);
|
|
123
|
+
path += '.' + p.resource;
|
|
124
|
+
const prop = dataType.fields.get(p.resource);
|
|
125
|
+
if (!prop)
|
|
126
|
+
throw new index_js_2.NotFoundError({ message: `Invalid or unknown resource path (${path})` });
|
|
127
|
+
const q = query_interface_js_1.OpraQuery.forGetProperty(prop);
|
|
128
|
+
if (nested) {
|
|
129
|
+
nested.nested = q;
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
query.nested = q;
|
|
133
|
+
}
|
|
134
|
+
nested = q;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
case 'DELETE': {
|
|
140
|
+
if (scope === 'collection') {
|
|
141
|
+
query = query_interface_js_1.OpraQuery.forDeleteMany(resource, {
|
|
142
|
+
filter: url.searchParams.get('$filter'),
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
query = query_interface_js_1.OpraQuery.forDelete(resource, p.key);
|
|
147
|
+
}
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
case 'POST': {
|
|
151
|
+
if (scope === 'collection') {
|
|
152
|
+
query = query_interface_js_1.OpraQuery.forCreate(resource, body, {
|
|
153
|
+
pick: url.searchParams.get('$pick'),
|
|
154
|
+
omit: url.searchParams.get('$omit'),
|
|
155
|
+
include: url.searchParams.get('$include')
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
case 'PATCH': {
|
|
161
|
+
if (scope === 'collection') {
|
|
162
|
+
query = query_interface_js_1.OpraQuery.forUpdateMany(resource, body, {
|
|
163
|
+
filter: url.searchParams.get('$filter')
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
query = query_interface_js_1.OpraQuery.forUpdate(resource, p.key, body, {
|
|
168
|
+
pick: url.searchParams.get('$pick'),
|
|
169
|
+
omit: url.searchParams.get('$omit'),
|
|
170
|
+
include: url.searchParams.get('$include')
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return query;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
throw new index_js_2.InternalServerError();
|
|
180
|
+
}
|
|
181
|
+
catch (e) {
|
|
182
|
+
throw index_js_2.BadRequestError.wrap(e);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
async sendResponse(executionContext, queryContexts) {
|
|
186
|
+
const outputPackets = [];
|
|
187
|
+
for (const ctx of queryContexts) {
|
|
188
|
+
const v = this.createOutput(ctx);
|
|
189
|
+
outputPackets.push(v);
|
|
190
|
+
}
|
|
191
|
+
if (this.isBatch(executionContext)) {
|
|
192
|
+
// this.writeError([], new InternalServerError({message: 'Not implemented yet'}));
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
if (!outputPackets.length) {
|
|
196
|
+
const err = new index_js_2.NotFoundError();
|
|
197
|
+
outputPackets.push({
|
|
198
|
+
status: err.status,
|
|
199
|
+
body: {
|
|
200
|
+
errors: [err.response]
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
const out = outputPackets[0];
|
|
205
|
+
const resp = executionContext.getResponseWrapper();
|
|
206
|
+
resp.setStatus(out.status);
|
|
207
|
+
resp.setHeader(index_js_1.HttpHeaders.Content_Type, 'application/json');
|
|
208
|
+
resp.setHeader(index_js_1.HttpHeaders.Cache_Control, 'no-cache');
|
|
209
|
+
resp.setHeader(index_js_1.HttpHeaders.Pragma, 'no-cache');
|
|
210
|
+
resp.setHeader(index_js_1.HttpHeaders.Expires, '-1');
|
|
211
|
+
resp.setHeader(index_js_1.HttpHeaders.X_Opra_Version, schema_1.OpraSchema.Version);
|
|
212
|
+
if (out.headers) {
|
|
213
|
+
for (const [k, v] of Object.entries(out.headers)) {
|
|
214
|
+
resp.setHeader(k, v);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
resp.send(JSON.stringify(out.body));
|
|
218
|
+
}
|
|
219
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
220
|
+
isBatch(executionContext) {
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
createOutput(ctx) {
|
|
224
|
+
const { query } = ctx;
|
|
225
|
+
let status = ctx.response.status;
|
|
226
|
+
let body = ctx.response.value || {};
|
|
227
|
+
const errors = ctx.response.errors?.map(e => (0, wrap_error_js_1.wrapError)(e));
|
|
228
|
+
if (errors && errors.length) {
|
|
229
|
+
if (!status || status < 400) {
|
|
230
|
+
status = 0;
|
|
231
|
+
for (const e of errors) {
|
|
232
|
+
status = Math.max(status, e.status || status);
|
|
233
|
+
}
|
|
234
|
+
if (status < index_js_1.HttpStatus.BAD_REQUEST)
|
|
235
|
+
status = index_js_1.HttpStatus.INTERNAL_SERVER_ERROR;
|
|
236
|
+
}
|
|
237
|
+
body.errors = errors.map(e => e.response);
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
delete body.errors;
|
|
241
|
+
status = status || (query.operation === 'create' ? index_js_1.HttpStatus.CREATED : index_js_1.HttpStatus.OK);
|
|
242
|
+
}
|
|
243
|
+
// Convert headers map to object
|
|
244
|
+
const headers = Array.from(ctx.response.headers.keys()).map(k => k.toLowerCase()).sort()
|
|
245
|
+
.reduce((a, k) => {
|
|
246
|
+
a[k] = ctx.response.headers.get(k);
|
|
247
|
+
return a;
|
|
248
|
+
}, {});
|
|
249
|
+
body = this.i18n.deep(body);
|
|
250
|
+
return {
|
|
251
|
+
status,
|
|
252
|
+
headers,
|
|
253
|
+
body
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
async sendError(executionContext, error) {
|
|
257
|
+
const resp = executionContext.getResponseWrapper();
|
|
258
|
+
resp.setStatus(error.status || 500);
|
|
259
|
+
resp.setHeader(index_js_1.HttpHeaders.Content_Type, 'application/json');
|
|
260
|
+
resp.setHeader(index_js_1.HttpHeaders.Cache_Control, 'no-cache');
|
|
261
|
+
resp.setHeader(index_js_1.HttpHeaders.Pragma, 'no-cache');
|
|
262
|
+
resp.setHeader(index_js_1.HttpHeaders.Expires, '-1');
|
|
263
|
+
resp.setHeader(index_js_1.HttpHeaders.X_Opra_Version, schema_1.OpraSchema.Version);
|
|
264
|
+
resp.send(JSON.stringify(error.response));
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
exports.OpraHttpAdapter = OpraHttpAdapter;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.QueryResponse = exports.QueryContext = void 0;
|
|
4
|
+
const schema_1 = require("@opra/schema");
|
|
4
5
|
const url_1 = require("@opra/url");
|
|
5
|
-
const
|
|
6
|
+
const index_js_1 = require("../enums/index.js");
|
|
6
7
|
class QueryContext {
|
|
7
8
|
service;
|
|
8
9
|
executionContext;
|
|
@@ -18,7 +19,7 @@ class QueryContext {
|
|
|
18
19
|
Object.assign(this, args);
|
|
19
20
|
this.response = new QueryResponse();
|
|
20
21
|
this.params = this.params || new url_1.OpraURLSearchParams();
|
|
21
|
-
this.headers = this.headers ||
|
|
22
|
+
this.headers = this.headers || new schema_1.ResponsiveMap();
|
|
22
23
|
this.resultPath = this.resultPath || '';
|
|
23
24
|
}
|
|
24
25
|
get type() {
|
|
@@ -32,7 +33,7 @@ class QueryContext {
|
|
|
32
33
|
}
|
|
33
34
|
exports.QueryContext = QueryContext;
|
|
34
35
|
class QueryResponse {
|
|
35
|
-
headers
|
|
36
|
+
headers;
|
|
36
37
|
errors = [];
|
|
37
38
|
status;
|
|
38
39
|
value;
|
|
@@ -40,6 +41,7 @@ class QueryResponse {
|
|
|
40
41
|
constructor(args) {
|
|
41
42
|
if (args)
|
|
42
43
|
Object.assign(this, args);
|
|
44
|
+
this.headers = new schema_1.ResponsiveMap(undefined, Array.from(Object.values(index_js_1.HttpHeaders)));
|
|
43
45
|
}
|
|
44
46
|
}
|
|
45
47
|
exports.QueryResponse = QueryResponse;
|
package/cjs/index.js
CHANGED
|
@@ -2,25 +2,15 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const tslib_1 = require("tslib");
|
|
4
4
|
require("reflect-metadata");
|
|
5
|
-
tslib_1.__exportStar(require("./constants.js"), exports);
|
|
6
5
|
tslib_1.__exportStar(require("./types.js"), exports);
|
|
7
6
|
tslib_1.__exportStar(require("./enums/index.js"), exports);
|
|
8
7
|
tslib_1.__exportStar(require("./exception/index.js"), exports);
|
|
9
|
-
tslib_1.__exportStar(require("./decorators/entity-resource.decorator.js"), exports);
|
|
10
8
|
tslib_1.__exportStar(require("./interfaces/execution-context.interface.js"), exports);
|
|
11
9
|
tslib_1.__exportStar(require("./interfaces/query.interface.js"), exports);
|
|
12
|
-
tslib_1.__exportStar(require("./interfaces/
|
|
10
|
+
tslib_1.__exportStar(require("./interfaces/entity-service.interface.js"), exports);
|
|
13
11
|
tslib_1.__exportStar(require("./implementation/query-context.js"), exports);
|
|
14
|
-
tslib_1.__exportStar(require("./implementation/
|
|
15
|
-
tslib_1.__exportStar(require("./implementation/
|
|
16
|
-
tslib_1.__exportStar(require("./implementation/
|
|
17
|
-
tslib_1.__exportStar(require("./
|
|
18
|
-
tslib_1.__exportStar(require("./implementation/adapter/http-adapter.js"), exports);
|
|
19
|
-
tslib_1.__exportStar(require("./implementation/adapter/express-adapter.js"), exports);
|
|
20
|
-
tslib_1.__exportStar(require("./implementation/data-type/data-type.js"), exports);
|
|
21
|
-
tslib_1.__exportStar(require("./implementation/data-type/complex-type.js"), exports);
|
|
22
|
-
tslib_1.__exportStar(require("./implementation/data-type/simple-type.js"), exports);
|
|
23
|
-
tslib_1.__exportStar(require("./implementation/resource/resource-handler.js"), exports);
|
|
24
|
-
tslib_1.__exportStar(require("./implementation/resource/entity-resource-handler.js"), exports);
|
|
25
|
-
tslib_1.__exportStar(require("./services/entity-resource-controller.js"), exports);
|
|
12
|
+
tslib_1.__exportStar(require("./implementation/adapter.js"), exports);
|
|
13
|
+
tslib_1.__exportStar(require("./implementation/http-adapter.js"), exports);
|
|
14
|
+
tslib_1.__exportStar(require("./implementation/express-adapter.js"), exports);
|
|
15
|
+
tslib_1.__exportStar(require("./services/data-service.js"), exports);
|
|
26
16
|
tslib_1.__exportStar(require("./services/json-data-service.js"), exports);
|
package/cjs/{services/entity-resource-controller.js → interfaces/entity-service.interface.js}
RENAMED
|
File without changes
|