@opra/core 0.0.11 → 0.0.13
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/constants.js +2 -1
- package/cjs/decorators/{entity-resource.decorator.js → api-entity-resource.decorator.js} +0 -0
- package/cjs/decorators/api-resolver.decorator.js +13 -0
- 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/adapter.js +31 -1
- package/cjs/implementation/adapter/http-adapter.js +117 -90
- package/cjs/implementation/data-type/complex-type.js +14 -0
- package/cjs/implementation/data-type/data-type.js +12 -0
- package/cjs/implementation/data-type/entity-type.js +4 -3
- package/cjs/implementation/opra-document.js +2 -6
- package/cjs/implementation/opra-service.js +22 -0
- package/cjs/implementation/resource/entity-resource-handler.js +35 -12
- package/cjs/implementation/resource/resource-handler.js +14 -0
- package/cjs/implementation/schema-generator.js +32 -11
- package/cjs/index.js +1 -1
- package/cjs/interfaces/{opra-schema.metadata.js → metadata/api-resolver.metadata.js} +0 -0
- package/cjs/interfaces/metadata/opra-schema.metadata.js +11 -0
- package/cjs/interfaces/query.interface.js +19 -2
- package/cjs/services/json-data-service.js +235 -4
- package/cjs/utils/create-i18n.js +1 -1
- package/cjs/utils/get-caller-file.util.js +6 -1
- package/esm/constants.d.ts +1 -0
- package/esm/constants.js +1 -0
- package/esm/decorators/{entity-resource.decorator.d.ts → api-entity-resource.decorator.d.ts} +1 -1
- package/esm/decorators/{entity-resource.decorator.js → api-entity-resource.decorator.js} +0 -0
- package/esm/decorators/api-resolver.decorator.d.ts +2 -0
- package/esm/decorators/api-resolver.decorator.js +9 -0
- 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/adapter.d.ts +1 -5
- package/esm/implementation/adapter/adapter.js +32 -2
- package/esm/implementation/adapter/http-adapter.d.ts +2 -1
- package/esm/implementation/adapter/http-adapter.js +118 -91
- package/esm/implementation/data-type/complex-type.d.ts +1 -0
- package/esm/implementation/data-type/complex-type.js +13 -0
- package/esm/implementation/data-type/data-type.d.ts +2 -0
- package/esm/implementation/data-type/data-type.js +12 -0
- package/esm/implementation/data-type/entity-type.js +3 -3
- package/esm/implementation/opra-document.js +2 -6
- package/esm/implementation/opra-service.d.ts +1 -0
- package/esm/implementation/opra-service.js +21 -0
- package/esm/implementation/resource/entity-resource-handler.d.ts +1 -1
- package/esm/implementation/resource/entity-resource-handler.js +35 -12
- package/esm/implementation/resource/resource-handler.d.ts +3 -3
- package/esm/implementation/resource/resource-handler.js +13 -0
- package/esm/implementation/schema-generator.js +33 -12
- package/esm/index.d.ts +1 -1
- package/esm/index.js +1 -1
- package/esm/interfaces/metadata/api-resolver.metadata.d.ts +3 -0
- package/esm/interfaces/{opra-schema.metadata.js → metadata/api-resolver.metadata.js} +0 -0
- package/esm/interfaces/metadata/opra-schema.metadata.d.ts +7 -0
- package/esm/interfaces/metadata/opra-schema.metadata.js +10 -0
- package/esm/interfaces/query.interface.d.ts +13 -5
- package/esm/interfaces/query.interface.js +19 -2
- package/esm/services/json-data-service.d.ts +66 -7
- package/esm/services/json-data-service.js +235 -4
- package/esm/types.d.ts +10 -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/i18n/en/error.json +3 -0
- package/package.json +6 -7
- package/esm/interfaces/opra-schema.metadata.d.ts +0 -14
package/cjs/constants.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.RESOURCE_METADATA = exports.OpraVersion = void 0;
|
|
3
|
+
exports.RESOLVER_METADATA = exports.RESOURCE_METADATA = exports.OpraVersion = void 0;
|
|
4
4
|
exports.OpraVersion = '1.0';
|
|
5
5
|
exports.RESOURCE_METADATA = 'opra:resource.metadata';
|
|
6
|
+
exports.RESOLVER_METADATA = 'opra:resolver.metadata';
|
|
File without changes
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ApiResolver = void 0;
|
|
4
|
+
const constants_js_1 = require("../constants.js");
|
|
5
|
+
function ApiResolver(options) {
|
|
6
|
+
return (target, propertyKey) => {
|
|
7
|
+
const metadata = {
|
|
8
|
+
...options
|
|
9
|
+
};
|
|
10
|
+
Reflect.defineMetadata(constants_js_1.RESOLVER_METADATA, metadata, target, propertyKey);
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
exports.ApiResolver = ApiResolver;
|
|
@@ -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;
|
|
@@ -41,8 +41,12 @@ class OpraAdapter {
|
|
|
41
41
|
continue;
|
|
42
42
|
}
|
|
43
43
|
try {
|
|
44
|
-
const resource = context.query.resource;
|
|
45
44
|
const promise = (async () => {
|
|
45
|
+
if (context.query.queryType === 'metadata') {
|
|
46
|
+
await this._metadataExecute(context);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const resource = context.query.resource;
|
|
46
50
|
await resource.prepare(context);
|
|
47
51
|
if (this.userContextResolver && !userContext)
|
|
48
52
|
userContext = this.userContextResolver({
|
|
@@ -91,6 +95,32 @@ class OpraAdapter {
|
|
|
91
95
|
}
|
|
92
96
|
}
|
|
93
97
|
}
|
|
98
|
+
async _metadataExecute(ctx) {
|
|
99
|
+
const query = ctx.query;
|
|
100
|
+
let out;
|
|
101
|
+
if (query.resourcePath.length > 2)
|
|
102
|
+
throw new index_js_1.BadRequestError();
|
|
103
|
+
if (query.resourcePath?.length) {
|
|
104
|
+
if (query.resourcePath[0] === 'resources') {
|
|
105
|
+
const resource = this.service.getResource(query.resourcePath[1]);
|
|
106
|
+
out = resource.getMetadata();
|
|
107
|
+
query.resourcePath[1] = resource.name;
|
|
108
|
+
}
|
|
109
|
+
else if (query.resourcePath[0] === 'types') {
|
|
110
|
+
const dataType = this.service.getDataType(query.resourcePath[1]);
|
|
111
|
+
out = dataType.getMetadata();
|
|
112
|
+
query.resourcePath[1] = dataType.name;
|
|
113
|
+
}
|
|
114
|
+
else
|
|
115
|
+
throw new index_js_1.BadRequestError();
|
|
116
|
+
ctx.response.value = {
|
|
117
|
+
'@opra:metadata': '/$metadata/' + query.resourcePath.join('/'),
|
|
118
|
+
...out
|
|
119
|
+
};
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
ctx.response.value = this.service.getMetadata();
|
|
123
|
+
}
|
|
94
124
|
static async initI18n(options) {
|
|
95
125
|
if (options?.i18n instanceof i18n_1.I18n)
|
|
96
126
|
return options.i18n;
|
|
@@ -33,7 +33,7 @@ class OpraHttpAdapter extends adapter_js_1.OpraAdapter {
|
|
|
33
33
|
const query = this.buildQuery(url, method, body);
|
|
34
34
|
if (!query)
|
|
35
35
|
throw new index_js_2.MethodNotAllowedError({
|
|
36
|
-
message: `Method "${method}" is not allowed by target
|
|
36
|
+
message: `Method "${method}" is not allowed by target endpoint`
|
|
37
37
|
});
|
|
38
38
|
return new query_context_js_1.QueryContext({
|
|
39
39
|
service: this.service,
|
|
@@ -44,119 +44,146 @@ class OpraHttpAdapter extends adapter_js_1.OpraAdapter {
|
|
|
44
44
|
continueOnError: query.operation === 'read'
|
|
45
45
|
});
|
|
46
46
|
}
|
|
47
|
+
buildMetadataQuery(url) {
|
|
48
|
+
const pathLen = url.path.size;
|
|
49
|
+
const resourcePath = [];
|
|
50
|
+
let pathIndex = 0;
|
|
51
|
+
while (pathIndex < pathLen) {
|
|
52
|
+
const p = url.path.get(pathIndex++);
|
|
53
|
+
if (p.key)
|
|
54
|
+
throw new index_js_2.BadRequestError();
|
|
55
|
+
if (p.resource !== '$metadata') {
|
|
56
|
+
if (pathIndex === 1)
|
|
57
|
+
resourcePath.push('resources');
|
|
58
|
+
resourcePath.push(p.resource);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const opts = {
|
|
62
|
+
pick: url.searchParams.get('$pick'),
|
|
63
|
+
omit: url.searchParams.get('$omit'),
|
|
64
|
+
include: url.searchParams.get('$include'),
|
|
65
|
+
};
|
|
66
|
+
return query_interface_js_1.OpraQuery.forGetMetadata(resourcePath, opts);
|
|
67
|
+
}
|
|
47
68
|
buildQuery(url, method, body) {
|
|
48
69
|
let container = this.service;
|
|
49
70
|
try {
|
|
50
|
-
let pathIndex = 0;
|
|
51
71
|
const pathLen = url.path.size;
|
|
72
|
+
// Check if requesting metadata
|
|
73
|
+
for (let i = 0; i < pathLen; i++) {
|
|
74
|
+
const p = url.path.get(i);
|
|
75
|
+
if (p.resource === '$metadata') {
|
|
76
|
+
if (method !== 'GET')
|
|
77
|
+
return;
|
|
78
|
+
return this.buildMetadataQuery(url);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
let pathIndex = 0;
|
|
52
82
|
while (pathIndex < pathLen) {
|
|
53
83
|
let p = url.path.get(pathIndex++);
|
|
54
84
|
const resource = container.getResource(p.resource);
|
|
55
85
|
// Move through path directories (containers)
|
|
56
86
|
if (resource instanceof container_resource_handler_js_1.ContainerResourceHandler) {
|
|
57
87
|
container = resource;
|
|
88
|
+
continue;
|
|
58
89
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
query.nested = q;
|
|
107
|
-
}
|
|
108
|
-
nested = q;
|
|
90
|
+
method = method.toUpperCase();
|
|
91
|
+
if (resource instanceof entity_resource_handler_js_1.EntityResourceHandler) {
|
|
92
|
+
const scope = p.key ? 'instance' : 'collection';
|
|
93
|
+
if (pathIndex < pathLen && !(method === 'GET' && scope === 'instance'))
|
|
94
|
+
return;
|
|
95
|
+
let query;
|
|
96
|
+
switch (method) {
|
|
97
|
+
case 'GET': {
|
|
98
|
+
if (scope === 'collection') {
|
|
99
|
+
query = query_interface_js_1.OpraQuery.forSearch(resource, {
|
|
100
|
+
filter: url.searchParams.get('$filter'),
|
|
101
|
+
limit: url.searchParams.get('$limit'),
|
|
102
|
+
skip: url.searchParams.get('$skip'),
|
|
103
|
+
distinct: url.searchParams.get('$distinct'),
|
|
104
|
+
count: url.searchParams.get('$count'),
|
|
105
|
+
sort: url.searchParams.get('$sort'),
|
|
106
|
+
pick: url.searchParams.get('$pick'),
|
|
107
|
+
omit: url.searchParams.get('$omit'),
|
|
108
|
+
include: url.searchParams.get('$include'),
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
query = query_interface_js_1.OpraQuery.forGetEntity(resource, p.key, {
|
|
113
|
+
pick: url.searchParams.get('$pick'),
|
|
114
|
+
omit: url.searchParams.get('$omit'),
|
|
115
|
+
include: url.searchParams.get('$include')
|
|
116
|
+
});
|
|
117
|
+
// Move through properties
|
|
118
|
+
let nested;
|
|
119
|
+
let path = resource.name;
|
|
120
|
+
while (pathIndex < pathLen) {
|
|
121
|
+
const dataType = nested
|
|
122
|
+
? this.service.getDataType(nested.property.type || 'string')
|
|
123
|
+
: query.resource.dataType;
|
|
124
|
+
if (!(dataType instanceof complex_type_js_1.ComplexType))
|
|
125
|
+
throw new Error(`"${path}" is not a ComplexType and has no properties.`);
|
|
126
|
+
p = url.path.get(pathIndex++);
|
|
127
|
+
path += '.' + p.resource;
|
|
128
|
+
const prop = dataType.properties?.[p.resource];
|
|
129
|
+
if (!prop)
|
|
130
|
+
throw new index_js_2.NotFoundError({ message: `Invalid or unknown resource path (${path})` });
|
|
131
|
+
const q = query_interface_js_1.OpraQuery.forGetProperty(prop);
|
|
132
|
+
if (nested) {
|
|
133
|
+
nested.nested = q;
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
query.nested = q;
|
|
109
137
|
}
|
|
138
|
+
nested = q;
|
|
110
139
|
}
|
|
111
|
-
break;
|
|
112
140
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
query = query_interface_js_1.OpraQuery.forDelete(resource, p.key);
|
|
121
|
-
}
|
|
122
|
-
break;
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
case 'DELETE': {
|
|
144
|
+
if (scope === 'collection') {
|
|
145
|
+
query = query_interface_js_1.OpraQuery.forDeleteMany(resource, {
|
|
146
|
+
filter: url.searchParams.get('$filter'),
|
|
147
|
+
});
|
|
123
148
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
query = query_interface_js_1.OpraQuery.forCreate(resource, body, {
|
|
127
|
-
pick: url.searchParams.get('$pick'),
|
|
128
|
-
omit: url.searchParams.get('$omit'),
|
|
129
|
-
include: url.searchParams.get('$include')
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
break;
|
|
149
|
+
else {
|
|
150
|
+
query = query_interface_js_1.OpraQuery.forDelete(resource, p.key);
|
|
133
151
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
case 'POST': {
|
|
155
|
+
if (scope === 'collection') {
|
|
156
|
+
query = query_interface_js_1.OpraQuery.forCreate(resource, body, {
|
|
157
|
+
pick: url.searchParams.get('$pick'),
|
|
158
|
+
omit: url.searchParams.get('$omit'),
|
|
159
|
+
include: url.searchParams.get('$include')
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
case 'PATCH': {
|
|
165
|
+
if (scope === 'collection') {
|
|
166
|
+
query = query_interface_js_1.OpraQuery.forUpdateMany(resource, body, {
|
|
167
|
+
filter: url.searchParams.get('$filter')
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
query = query_interface_js_1.OpraQuery.forUpdate(resource, p.key, body, {
|
|
172
|
+
pick: url.searchParams.get('$pick'),
|
|
173
|
+
omit: url.searchParams.get('$omit'),
|
|
174
|
+
include: url.searchParams.get('$include')
|
|
175
|
+
});
|
|
148
176
|
}
|
|
177
|
+
break;
|
|
149
178
|
}
|
|
150
|
-
return query;
|
|
151
179
|
}
|
|
180
|
+
return query;
|
|
152
181
|
}
|
|
153
182
|
}
|
|
154
183
|
throw new index_js_2.InternalServerError();
|
|
155
184
|
}
|
|
156
185
|
catch (e) {
|
|
157
|
-
|
|
158
|
-
throw e;
|
|
159
|
-
throw new index_js_2.BadRequestError({ message: e.message });
|
|
186
|
+
throw index_js_2.BadRequestError.wrap(e);
|
|
160
187
|
}
|
|
161
188
|
}
|
|
162
189
|
async sendResponse(executionContext, queryContexts) {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ComplexType = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const putil_merge_1 = tslib_1.__importDefault(require("putil-merge"));
|
|
4
6
|
const responsive_object_js_1 = require("../../utils/responsive-object.js");
|
|
5
7
|
const terminal_utils_js_1 = require("../../utils/terminal-utils.js");
|
|
6
8
|
const data_type_js_1 = require("./data-type.js");
|
|
@@ -28,6 +30,18 @@ class ComplexType extends data_type_js_1.DataType {
|
|
|
28
30
|
throw new Error(`"${this.name}" type has no property named "${name}"`);
|
|
29
31
|
return t;
|
|
30
32
|
}
|
|
33
|
+
getMetadata() {
|
|
34
|
+
const out = super.getMetadata();
|
|
35
|
+
if (this.additionalProperties)
|
|
36
|
+
out.additionalProperties = this.additionalProperties;
|
|
37
|
+
if (this.ownProperties) {
|
|
38
|
+
out.properties = {};
|
|
39
|
+
for (const [k, prop] of Object.entries(this.ownProperties)) {
|
|
40
|
+
out.properties[k] = (0, putil_merge_1.default)({}, prop, { deep: true });
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return out;
|
|
44
|
+
}
|
|
31
45
|
toString() {
|
|
32
46
|
return `[${Object.getPrototypeOf(this).constructor.name} ${this.name}]`;
|
|
33
47
|
}
|
|
@@ -25,11 +25,23 @@ class DataType {
|
|
|
25
25
|
get description() {
|
|
26
26
|
return this._args.description;
|
|
27
27
|
}
|
|
28
|
+
get abstract() {
|
|
29
|
+
return !!this._args.abstract;
|
|
30
|
+
}
|
|
28
31
|
get ctor() {
|
|
29
32
|
return this._args.ctor;
|
|
30
33
|
}
|
|
31
34
|
is(typeName) {
|
|
32
35
|
return this.name === typeName || !!(this.base && this.base.is(typeName));
|
|
33
36
|
}
|
|
37
|
+
getMetadata() {
|
|
38
|
+
return {
|
|
39
|
+
kind: this.kind,
|
|
40
|
+
base: this.base?.name,
|
|
41
|
+
abstract: this.abstract ? true : undefined,
|
|
42
|
+
name: this.name,
|
|
43
|
+
description: this.description,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
34
46
|
}
|
|
35
47
|
exports.DataType = DataType;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.EntityType = void 0;
|
|
4
|
-
const
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const Optionals = tslib_1.__importStar(require("@opra/optionals"));
|
|
5
6
|
const complex_type_js_1 = require("./complex-type.js");
|
|
6
7
|
class EntityType extends complex_type_js_1.ComplexType {
|
|
7
8
|
constructor(owner, args, base) {
|
|
@@ -10,8 +11,8 @@ class EntityType extends complex_type_js_1.ComplexType {
|
|
|
10
11
|
}, base);
|
|
11
12
|
this._args.kind = 'EntityType';
|
|
12
13
|
// Try to determine primary key info from SQB
|
|
13
|
-
if (args.ctor) {
|
|
14
|
-
const sqbEntity =
|
|
14
|
+
if (Optionals.SqbConnect && args.ctor) {
|
|
15
|
+
const sqbEntity = Optionals.SqbConnect.EntityMetadata.get(args.ctor);
|
|
15
16
|
if (sqbEntity?.indexes) {
|
|
16
17
|
const primaryIndex = sqbEntity.indexes.find(x => x.primary);
|
|
17
18
|
if (primaryIndex) {
|
|
@@ -74,7 +74,8 @@ class OpraDocument {
|
|
|
74
74
|
recursiveSet.add(schema.name);
|
|
75
75
|
let baseType;
|
|
76
76
|
if (schema.base) {
|
|
77
|
-
|
|
77
|
+
baseType = this.types[schema.base];
|
|
78
|
+
if (!baseType) {
|
|
78
79
|
const baseSchema = dataTypes.find(dt => dt.name.toLowerCase() === schema.base?.toLowerCase()) ||
|
|
79
80
|
internal_data_types_js_1.internalDataTypes.get(schema.base.toLowerCase());
|
|
80
81
|
if (!baseSchema)
|
|
@@ -106,11 +107,6 @@ class OpraDocument {
|
|
|
106
107
|
return dataType;
|
|
107
108
|
};
|
|
108
109
|
dataTypes.forEach(dataType => processDataType(dataType));
|
|
109
|
-
// Sort data types by name
|
|
110
|
-
const newTypes = (0, responsive_object_js_1.Responsive)();
|
|
111
|
-
Object.keys(this.types).sort()
|
|
112
|
-
.forEach(name => newTypes[name] = this.types[name]);
|
|
113
|
-
this._types = newTypes;
|
|
114
110
|
}
|
|
115
111
|
}
|
|
116
112
|
exports.OpraDocument = OpraDocument;
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.OpraService = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const putil_merge_1 = tslib_1.__importDefault(require("putil-merge"));
|
|
4
6
|
const schema_1 = require("@opra/schema");
|
|
7
|
+
const constants_js_1 = require("../constants.js");
|
|
8
|
+
const internal_data_types_js_1 = require("../utils/internal-data-types.js");
|
|
5
9
|
const responsive_object_js_1 = require("../utils/responsive-object.js");
|
|
6
10
|
const entity_type_js_1 = require("./data-type/entity-type.js");
|
|
7
11
|
const opra_document_js_1 = require("./opra-document.js");
|
|
@@ -32,6 +36,24 @@ class OpraService extends opra_document_js_1.OpraDocument {
|
|
|
32
36
|
throw new Error(`"${name}" is not an EntityResource`);
|
|
33
37
|
return t;
|
|
34
38
|
}
|
|
39
|
+
getMetadata() {
|
|
40
|
+
const out = {
|
|
41
|
+
'@opra:metadata': '/$metadata',
|
|
42
|
+
version: constants_js_1.OpraVersion,
|
|
43
|
+
servers: this.servers?.map(x => (0, putil_merge_1.default)({}, x, { deep: true })),
|
|
44
|
+
info: (0, putil_merge_1.default)({}, this.info, { deep: true }),
|
|
45
|
+
types: [],
|
|
46
|
+
resources: []
|
|
47
|
+
};
|
|
48
|
+
for (const [k, dataType] of Object.entries(this.types)) {
|
|
49
|
+
if (!internal_data_types_js_1.internalDataTypes.has(k))
|
|
50
|
+
out.types.push(dataType.getMetadata());
|
|
51
|
+
}
|
|
52
|
+
for (const resource of Object.values(this.resources)) {
|
|
53
|
+
out.resources.push(resource.getMetadata());
|
|
54
|
+
}
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
35
57
|
_addResources(resources) {
|
|
36
58
|
for (const r of resources) {
|
|
37
59
|
if (schema_1.OpraSchema.isEntityResource(r)) {
|
|
@@ -3,11 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.EntityResourceHandler = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const lodash_1 = tslib_1.__importDefault(require("lodash"));
|
|
6
|
+
const i18n_1 = require("@opra/i18n");
|
|
7
|
+
const index_js_1 = require("../../exception/index.js");
|
|
6
8
|
const query_interface_js_1 = require("../../interfaces/query.interface.js");
|
|
9
|
+
const complex_type_js_1 = require("../data-type/complex-type.js");
|
|
7
10
|
const entity_type_js_1 = require("../data-type/entity-type.js");
|
|
8
11
|
const resource_handler_js_1 = require("./resource-handler.js");
|
|
9
|
-
var isSearchQuery = query_interface_js_1.OpraQuery.isSearchQuery;
|
|
10
|
-
const index_js_1 = require("../../exception/index.js");
|
|
11
12
|
class EntityResourceHandler extends resource_handler_js_1.ResourceHandler {
|
|
12
13
|
service;
|
|
13
14
|
dataType;
|
|
@@ -24,7 +25,7 @@ class EntityResourceHandler extends resource_handler_js_1.ResourceHandler {
|
|
|
24
25
|
}
|
|
25
26
|
async execute(ctx) {
|
|
26
27
|
const { query } = ctx;
|
|
27
|
-
if (isSearchQuery(query)) {
|
|
28
|
+
if (query_interface_js_1.OpraQuery.isSearchQuery(query)) {
|
|
28
29
|
const promises = [];
|
|
29
30
|
let search;
|
|
30
31
|
let count;
|
|
@@ -43,12 +44,28 @@ class EntityResourceHandler extends resource_handler_js_1.ResourceHandler {
|
|
|
43
44
|
}
|
|
44
45
|
ctx.response.value = await this._executeFn(ctx, query.queryType);
|
|
45
46
|
}
|
|
46
|
-
async _executeFn(ctx,
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
async _executeFn(ctx, queryType) {
|
|
48
|
+
const resolverInfo = this._args.resolvers?.[queryType];
|
|
49
|
+
if (resolverInfo.forbidden || !resolverInfo.handler)
|
|
50
|
+
throw new index_js_1.ForbiddenError({
|
|
51
|
+
message: (0, i18n_1.translate)('RESOLVER_FORBIDDEN', { queryType }),
|
|
52
|
+
severity: 'error',
|
|
53
|
+
code: 'RESOLVER_FORBIDDEN'
|
|
54
|
+
});
|
|
55
|
+
let result = await resolverInfo.handler(ctx);
|
|
56
|
+
switch (queryType) {
|
|
50
57
|
case 'search':
|
|
51
|
-
return {
|
|
58
|
+
return {
|
|
59
|
+
'@opra:metadata': '/$metadata/types/' + this.dataType.name,
|
|
60
|
+
items: Array.isArray(result) ? result : (ctx.response.value ? [result] : [])
|
|
61
|
+
};
|
|
62
|
+
case 'get':
|
|
63
|
+
case 'update':
|
|
64
|
+
if (!result) {
|
|
65
|
+
const query = ctx.query;
|
|
66
|
+
throw new index_js_1.ResourceNotFoundError(this.name, query.keyValue);
|
|
67
|
+
}
|
|
68
|
+
break;
|
|
52
69
|
case 'count':
|
|
53
70
|
return { count: result || 0 };
|
|
54
71
|
case 'delete':
|
|
@@ -63,18 +80,24 @@ class EntityResourceHandler extends resource_handler_js_1.ResourceHandler {
|
|
|
63
80
|
affectedRecords = result.affectedRows || result.affectedRecords;
|
|
64
81
|
return { affectedRecords };
|
|
65
82
|
}
|
|
66
|
-
result = Array.isArray(result) ? result[0] : result;
|
|
67
83
|
if (!result)
|
|
68
|
-
|
|
84
|
+
return;
|
|
85
|
+
result = Array.isArray(result) ? result[0] : result;
|
|
86
|
+
let dataType = this.dataType;
|
|
69
87
|
if (ctx.resultPath) {
|
|
70
88
|
const pathArray = ctx.resultPath.split('.');
|
|
71
89
|
for (const property of pathArray) {
|
|
90
|
+
const prop = dataType instanceof complex_type_js_1.ComplexType ? dataType.properties?.[property] : undefined;
|
|
91
|
+
dataType = prop && prop.type ? this.service.types[prop.type || 'string'] : undefined;
|
|
72
92
|
result = result && typeof result === 'object' && result[property];
|
|
73
93
|
}
|
|
74
94
|
}
|
|
75
|
-
if (
|
|
95
|
+
if (queryType === 'create')
|
|
76
96
|
ctx.response.status = 201;
|
|
77
|
-
return
|
|
97
|
+
return {
|
|
98
|
+
'@opra:metadata': dataType ? '/$metadata/types/' + dataType.name : '__unknown__',
|
|
99
|
+
...result
|
|
100
|
+
};
|
|
78
101
|
}
|
|
79
102
|
}
|
|
80
103
|
exports.EntityResourceHandler = EntityResourceHandler;
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ResourceHandler = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const putil_merge_1 = tslib_1.__importDefault(require("putil-merge"));
|
|
4
6
|
const terminal_utils_js_1 = require("../../utils/terminal-utils.js");
|
|
5
7
|
class ResourceHandler {
|
|
6
8
|
_args;
|
|
9
|
+
path;
|
|
7
10
|
constructor(args) {
|
|
8
11
|
this._args = args;
|
|
9
12
|
}
|
|
@@ -23,6 +26,17 @@ class ResourceHandler {
|
|
|
23
26
|
await fn(ctx);
|
|
24
27
|
}
|
|
25
28
|
}
|
|
29
|
+
getMetadata() {
|
|
30
|
+
return (0, putil_merge_1.default)({}, {
|
|
31
|
+
...this._args,
|
|
32
|
+
instance: undefined,
|
|
33
|
+
}, {
|
|
34
|
+
deep: true,
|
|
35
|
+
filter: (source, key) => {
|
|
36
|
+
return (key !== 'instance' && typeof source[key] !== 'function' && source[key] != null);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
}
|
|
26
40
|
[terminal_utils_js_1.nodeInspectCustom]() {
|
|
27
41
|
return `[${terminal_utils_js_1.colorFgYellow + Object.getPrototypeOf(this).constructor.name + terminal_utils_js_1.colorReset}` +
|
|
28
42
|
` ${terminal_utils_js_1.colorFgMagenta + this.name + terminal_utils_js_1.colorReset}]`;
|