@opra/core 0.3.0 → 0.5.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/adapter/adapter.js +318 -0
- package/cjs/{implementation → adapter}/express-adapter.js +3 -6
- package/cjs/adapter/http-adapter.js +241 -0
- package/cjs/adapter/metadata-resource.js +23 -0
- package/cjs/{implementation → adapter}/query-context.js +3 -3
- package/cjs/index.js +6 -6
- package/cjs/interfaces/resource.interface.js +2 -0
- package/cjs/services/json-collection-service.js +14 -14
- package/cjs/services/json-singleton-service.js +97 -0
- package/esm/{implementation → adapter}/adapter.d.ts +17 -9
- package/esm/adapter/adapter.js +314 -0
- package/esm/{implementation → adapter}/express-adapter.d.ts +2 -2
- package/esm/{implementation → adapter}/express-adapter.js +3 -6
- package/esm/{implementation → adapter}/http-adapter.d.ts +2 -3
- package/esm/adapter/http-adapter.js +237 -0
- package/esm/adapter/metadata-resource.d.ts +8 -0
- package/esm/adapter/metadata-resource.js +20 -0
- package/esm/{implementation → adapter}/query-context.d.ts +6 -7
- package/esm/{implementation → adapter}/query-context.js +1 -1
- package/esm/index.d.ts +6 -6
- package/esm/index.js +6 -6
- package/esm/interfaces/resource.interface.d.ts +22 -0
- package/esm/interfaces/resource.interface.js +1 -0
- package/esm/services/json-collection-service.d.ts +11 -12
- package/esm/services/json-collection-service.js +15 -15
- package/esm/services/json-singleton-service.d.ts +39 -0
- package/esm/services/json-singleton-service.js +92 -0
- package/esm/types.d.ts +2 -8
- package/esm/utils/create-i18n.d.ts +1 -1
- package/package.json +15 -13
- package/cjs/enums/http-headers.enum.js +0 -395
- package/cjs/enums/http-status.enum.js +0 -300
- package/cjs/enums/index.js +0 -5
- package/cjs/implementation/adapter-utils/entity-resource-execute.util.js +0 -86
- package/cjs/implementation/adapter-utils/resource-execute.util.js +0 -11
- package/cjs/implementation/adapter-utils/resource-prepare.util.js +0 -11
- package/cjs/implementation/adapter.js +0 -130
- package/cjs/implementation/headers-map.js +0 -18
- package/cjs/implementation/http-adapter.js +0 -253
- package/cjs/interfaces/entity-service.interface.js +0 -30
- package/esm/enums/http-headers.enum.d.ts +0 -370
- package/esm/enums/http-headers.enum.js +0 -392
- package/esm/enums/http-status.enum.d.ts +0 -290
- package/esm/enums/http-status.enum.js +0 -297
- package/esm/enums/index.d.ts +0 -2
- package/esm/enums/index.js +0 -2
- package/esm/implementation/adapter-utils/entity-resource-execute.util.d.ts +0 -3
- package/esm/implementation/adapter-utils/entity-resource-execute.util.js +0 -82
- package/esm/implementation/adapter-utils/resource-execute.util.d.ts +0 -3
- package/esm/implementation/adapter-utils/resource-execute.util.js +0 -7
- package/esm/implementation/adapter-utils/resource-prepare.util.d.ts +0 -3
- package/esm/implementation/adapter-utils/resource-prepare.util.js +0 -7
- package/esm/implementation/adapter.js +0 -126
- package/esm/implementation/headers-map.d.ts +0 -5
- package/esm/implementation/headers-map.js +0 -14
- package/esm/implementation/http-adapter.js +0 -249
- package/esm/interfaces/entity-service.interface.d.ts +0 -19
- package/esm/interfaces/entity-service.interface.js +0 -26
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OpraAdapter = void 0;
|
|
4
|
+
const strict_typed_events_1 = require("strict-typed-events");
|
|
5
|
+
const common_1 = require("@opra/common");
|
|
6
|
+
const exception_1 = require("@opra/exception");
|
|
7
|
+
const i18n_1 = require("@opra/i18n");
|
|
8
|
+
const schema_1 = require("@opra/schema");
|
|
9
|
+
const create_i18n_js_1 = require("../utils/create-i18n.js");
|
|
10
|
+
const metadata_resource_js_1 = require("./metadata-resource.js");
|
|
11
|
+
class OpraAdapter {
|
|
12
|
+
document;
|
|
13
|
+
i18n;
|
|
14
|
+
userContextResolver;
|
|
15
|
+
// protected _metadataResource: SingletonResourceInfo;
|
|
16
|
+
_internalResources = new common_1.ResponsiveMap();
|
|
17
|
+
constructor(document) {
|
|
18
|
+
this.document = document;
|
|
19
|
+
}
|
|
20
|
+
async handler(executionContext) {
|
|
21
|
+
let queryContexts;
|
|
22
|
+
let userContext;
|
|
23
|
+
let failed = false;
|
|
24
|
+
try {
|
|
25
|
+
queryContexts = this.prepareRequests(executionContext);
|
|
26
|
+
let stop = false;
|
|
27
|
+
// Read requests can be executed simultaneously, write request should be executed one by one
|
|
28
|
+
let promises;
|
|
29
|
+
let exclusive = false;
|
|
30
|
+
for (const context of queryContexts) {
|
|
31
|
+
exclusive = exclusive || context.query.operation !== 'read';
|
|
32
|
+
// Wait previous read requests before executing update request
|
|
33
|
+
if (exclusive && promises) {
|
|
34
|
+
await Promise.allSettled(promises);
|
|
35
|
+
promises = undefined;
|
|
36
|
+
}
|
|
37
|
+
// If previous request in bucket had an error and executed an update
|
|
38
|
+
// we do not execute next requests
|
|
39
|
+
if (stop) {
|
|
40
|
+
context.errors.push(new exception_1.FailedDependencyError());
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
const promise = (async () => {
|
|
45
|
+
// if (context.query.method === 'metadata') {
|
|
46
|
+
// await this._getSchemaExecute(context); //todo
|
|
47
|
+
// return;
|
|
48
|
+
// }
|
|
49
|
+
const resource = context.query.resource;
|
|
50
|
+
await this._resourcePrepare(resource, context);
|
|
51
|
+
if (this.userContextResolver && !userContext)
|
|
52
|
+
userContext = this.userContextResolver({
|
|
53
|
+
executionContext,
|
|
54
|
+
isBatch: this.isBatch(executionContext)
|
|
55
|
+
});
|
|
56
|
+
context.userContext = userContext;
|
|
57
|
+
await this._resourceExecute(this.document, resource, context);
|
|
58
|
+
})().catch(e => {
|
|
59
|
+
context.errors.push(e);
|
|
60
|
+
});
|
|
61
|
+
if (exclusive)
|
|
62
|
+
await promise;
|
|
63
|
+
else {
|
|
64
|
+
promises = promises || [];
|
|
65
|
+
promises.push(promise);
|
|
66
|
+
}
|
|
67
|
+
// todo execute sub property queries
|
|
68
|
+
}
|
|
69
|
+
catch (e) {
|
|
70
|
+
context.errors.unshift(e);
|
|
71
|
+
}
|
|
72
|
+
if (context.errors.length) {
|
|
73
|
+
// noinspection SuspiciousTypeOfGuard
|
|
74
|
+
context.errors = context.errors.map(e => (0, exception_1.wrapException)(e));
|
|
75
|
+
if (exclusive)
|
|
76
|
+
stop = stop || !!context.errors.find(e => !(e.issue.severity === 'warning' || e.issue.severity === 'info'));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (promises)
|
|
80
|
+
await Promise.allSettled(promises);
|
|
81
|
+
await this.sendResponse(executionContext, queryContexts);
|
|
82
|
+
}
|
|
83
|
+
catch (e) {
|
|
84
|
+
failed = true;
|
|
85
|
+
const error = (0, exception_1.wrapException)(e);
|
|
86
|
+
await this.sendError(executionContext, error);
|
|
87
|
+
}
|
|
88
|
+
finally {
|
|
89
|
+
if (executionContext instanceof strict_typed_events_1.AsyncEventEmitter) {
|
|
90
|
+
await executionContext
|
|
91
|
+
.emitAsyncSerial('finish', {
|
|
92
|
+
userContext,
|
|
93
|
+
failed
|
|
94
|
+
}).catch();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
async _resourcePrepare(resource, context) {
|
|
99
|
+
const { query } = context;
|
|
100
|
+
const fn = resource.metadata['pre_' + query.method];
|
|
101
|
+
if (fn && typeof fn === 'function') {
|
|
102
|
+
await fn(context);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async _resourceExecute(document, resource, context) {
|
|
106
|
+
if (resource instanceof schema_1.CollectionResourceInfo) {
|
|
107
|
+
const { query } = context;
|
|
108
|
+
if (query.kind === 'SearchCollectionQuery') {
|
|
109
|
+
const promises = [];
|
|
110
|
+
let search;
|
|
111
|
+
promises.push(this._collectionResourceExecute(document, resource, context)
|
|
112
|
+
.then(v => search = v));
|
|
113
|
+
if (query.count && resource.metadata.count) {
|
|
114
|
+
const ctx = {
|
|
115
|
+
query: new schema_1.CollectionCountQuery(query.resource, { filter: query.filter }),
|
|
116
|
+
resultPath: ''
|
|
117
|
+
};
|
|
118
|
+
Object.setPrototypeOf(ctx, context);
|
|
119
|
+
promises.push(this._collectionResourceExecute(document, resource, ctx));
|
|
120
|
+
}
|
|
121
|
+
await Promise.all(promises);
|
|
122
|
+
context.response = search;
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
context.response = await this._collectionResourceExecute(document, resource, context);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
else if (resource instanceof schema_1.SingletonResourceInfo) {
|
|
129
|
+
context.response = await this._singletonResourceExecute(document, resource, context);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
throw new Error(`Executing "${resource.kind}" has not been implemented yet`);
|
|
133
|
+
}
|
|
134
|
+
async _init(options) {
|
|
135
|
+
if (options?.i18n instanceof i18n_1.I18n)
|
|
136
|
+
this.i18n = options.i18n;
|
|
137
|
+
else if (typeof options?.i18n === 'function')
|
|
138
|
+
this.i18n = await options.i18n();
|
|
139
|
+
else
|
|
140
|
+
this.i18n = await (0, create_i18n_js_1.createI18n)(options?.i18n);
|
|
141
|
+
this.i18n = this.i18n || i18n_1.I18n.defaultInstance;
|
|
142
|
+
if (!this.i18n.isInitialized)
|
|
143
|
+
await this.i18n.init();
|
|
144
|
+
this.userContextResolver = options?.userContext;
|
|
145
|
+
const metadataResource = new metadata_resource_js_1.MetadataResource();
|
|
146
|
+
const metadataResourceInfo = new schema_1.SingletonResourceInfo(this.document, '$metadata', this.document.getComplexDataType('object'), {
|
|
147
|
+
kind: 'SingletonResource',
|
|
148
|
+
type: 'object',
|
|
149
|
+
instance: metadataResource,
|
|
150
|
+
get: {
|
|
151
|
+
handler: metadataResource.get.bind(metadataResource)
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
this._internalResources.set(metadataResourceInfo.name, metadataResourceInfo);
|
|
155
|
+
metadataResource.init(metadataResourceInfo);
|
|
156
|
+
for (const r of this.document.resources.values()) {
|
|
157
|
+
if (r.instance) {
|
|
158
|
+
const init = r.instance.init;
|
|
159
|
+
if (init)
|
|
160
|
+
await init.call(r.instance, r);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
async _collectionResourceExecute(document, resource, context) {
|
|
165
|
+
const method = context.query.method;
|
|
166
|
+
const resolverInfo = resource.metadata[method];
|
|
167
|
+
if (!(resolverInfo && resolverInfo.handler))
|
|
168
|
+
throw new exception_1.ForbiddenError({
|
|
169
|
+
message: (0, i18n_1.translate)('RESOLVER_FORBIDDEN', { method }, `The resource endpoint does not accept '{{method}}' operations`),
|
|
170
|
+
severity: 'error',
|
|
171
|
+
code: 'RESOLVER_FORBIDDEN'
|
|
172
|
+
});
|
|
173
|
+
let result;
|
|
174
|
+
switch (method) {
|
|
175
|
+
case 'create': {
|
|
176
|
+
const query = context.query;
|
|
177
|
+
result = await resolverInfo.handler(context, query.data, query);
|
|
178
|
+
result = Array.isArray(result) ? result[0] : result;
|
|
179
|
+
if (result)
|
|
180
|
+
context.status = 201;
|
|
181
|
+
context.responseHeaders.set(common_1.HttpHeaders.X_Opra_DataType, resource.dataType.name);
|
|
182
|
+
return result;
|
|
183
|
+
}
|
|
184
|
+
case 'count': {
|
|
185
|
+
const query = context.query;
|
|
186
|
+
result = await resolverInfo.handler(context, query);
|
|
187
|
+
context.responseHeaders.set(common_1.HttpHeaders.X_Opra_Count, result);
|
|
188
|
+
return result;
|
|
189
|
+
}
|
|
190
|
+
case 'get': {
|
|
191
|
+
const query = context.query;
|
|
192
|
+
result = await resolverInfo.handler(context, query.keyValue, query);
|
|
193
|
+
result = Array.isArray(result) ? result[0] : result;
|
|
194
|
+
if (!result)
|
|
195
|
+
throw new exception_1.ResourceNotFoundError(resource.name, query.keyValue);
|
|
196
|
+
const v = await this._pathWalkThrough(query, query.dataType, result, resource.name);
|
|
197
|
+
if (v.value === undefined)
|
|
198
|
+
throw new exception_1.ResourceNotFoundError(v.path);
|
|
199
|
+
if (v.dataType)
|
|
200
|
+
context.responseHeaders.set(common_1.HttpHeaders.X_Opra_DataType, v.dataType.name);
|
|
201
|
+
return v.value;
|
|
202
|
+
}
|
|
203
|
+
case 'search': {
|
|
204
|
+
const query = context.query;
|
|
205
|
+
result = await resolverInfo.handler(context, query);
|
|
206
|
+
const items = Array.isArray(result) ? result : (context.response ? [result] : []);
|
|
207
|
+
context.responseHeaders.set(common_1.HttpHeaders.X_Opra_DataType, resource.dataType.name);
|
|
208
|
+
return items;
|
|
209
|
+
}
|
|
210
|
+
case 'update': {
|
|
211
|
+
const query = context.query;
|
|
212
|
+
result = await resolverInfo.handler(context, query.keyValue, query.data, query);
|
|
213
|
+
result = Array.isArray(result) ? result[0] : result;
|
|
214
|
+
if (!result)
|
|
215
|
+
throw new exception_1.ResourceNotFoundError(resource.name, query.keyValue);
|
|
216
|
+
context.responseHeaders.set(common_1.HttpHeaders.X_Opra_DataType, resource.dataType.name);
|
|
217
|
+
return result;
|
|
218
|
+
}
|
|
219
|
+
case 'delete':
|
|
220
|
+
case 'deleteMany':
|
|
221
|
+
case 'updateMany': {
|
|
222
|
+
switch (method) {
|
|
223
|
+
case 'delete': {
|
|
224
|
+
const query = context.query;
|
|
225
|
+
result = await resolverInfo.handler(context, query.keyValue, query);
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
case 'deleteMany': {
|
|
229
|
+
const query = context.query;
|
|
230
|
+
result = await resolverInfo.handler(context, query);
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
case 'updateMany': {
|
|
234
|
+
const query = context.query;
|
|
235
|
+
result = await resolverInfo.handler(context, query.data, query);
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
let affected;
|
|
240
|
+
if (typeof result === 'number')
|
|
241
|
+
affected = result;
|
|
242
|
+
if (typeof result === 'boolean')
|
|
243
|
+
affected = result ? 1 : 0;
|
|
244
|
+
if (typeof result === 'object')
|
|
245
|
+
affected = result.affectedRows || result.affected;
|
|
246
|
+
return {
|
|
247
|
+
operation: context.query.method,
|
|
248
|
+
affected
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
async _singletonResourceExecute(document, resource, context) {
|
|
254
|
+
const method = context.query.method;
|
|
255
|
+
const resolverInfo = resource.metadata[method];
|
|
256
|
+
if (!(resolverInfo && resolverInfo.handler))
|
|
257
|
+
throw new exception_1.ForbiddenError({
|
|
258
|
+
message: (0, i18n_1.translate)('RESOLVER_FORBIDDEN', { method }, `The resource endpoint does not accept '{{method}}' operations`),
|
|
259
|
+
severity: 'error',
|
|
260
|
+
code: 'RESOLVER_FORBIDDEN'
|
|
261
|
+
});
|
|
262
|
+
let result = await resolverInfo.handler(context);
|
|
263
|
+
switch (method) {
|
|
264
|
+
case 'get': {
|
|
265
|
+
const query = context.query;
|
|
266
|
+
result = await resolverInfo.handler(context, query);
|
|
267
|
+
result = Array.isArray(result) ? result[0] : result;
|
|
268
|
+
if (!result)
|
|
269
|
+
throw new exception_1.ResourceNotFoundError(resource.name);
|
|
270
|
+
const v = await this._pathWalkThrough(query, query.dataType, result, resource.name);
|
|
271
|
+
if (v.value === undefined)
|
|
272
|
+
throw new exception_1.ResourceNotFoundError(v.path);
|
|
273
|
+
if (v.dataType)
|
|
274
|
+
context.responseHeaders.set(common_1.HttpHeaders.X_Opra_DataType, v.dataType.name);
|
|
275
|
+
return v.value;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
if (!result)
|
|
279
|
+
return;
|
|
280
|
+
result = Array.isArray(result) ? result[0] : result;
|
|
281
|
+
let dataType = resource.dataType;
|
|
282
|
+
if (context.resultPath) {
|
|
283
|
+
const pathArray = context.resultPath.split('.');
|
|
284
|
+
for (const field of pathArray) {
|
|
285
|
+
const prop = dataType instanceof schema_1.ComplexType ? dataType.fields.get(field) : undefined;
|
|
286
|
+
dataType = prop && prop.type ? this.document.types.get(prop.type) : undefined;
|
|
287
|
+
result = result && typeof result === 'object' && result[field];
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
if (method === 'create')
|
|
291
|
+
context.status = 201;
|
|
292
|
+
context.responseHeaders.set(common_1.HttpHeaders.X_Opra_DataType, resource.dataType.name);
|
|
293
|
+
return result;
|
|
294
|
+
}
|
|
295
|
+
async _pathWalkThrough(query, dataType, value, parentPath) {
|
|
296
|
+
const { child } = query;
|
|
297
|
+
if (!child)
|
|
298
|
+
return { value, dataType, path: parentPath };
|
|
299
|
+
// Make a case in sensitive lookup
|
|
300
|
+
const fieldNameLower = child.fieldName.toLowerCase();
|
|
301
|
+
const path = parentPath + (parentPath ? '.' : '') + child.fieldName;
|
|
302
|
+
for (const key of Object.keys(value)) {
|
|
303
|
+
if (key.toLowerCase() === fieldNameLower) {
|
|
304
|
+
let v = value[key];
|
|
305
|
+
if (v == null)
|
|
306
|
+
return { path };
|
|
307
|
+
if (child.child && child.dataType instanceof schema_1.ComplexType) {
|
|
308
|
+
if (Array.isArray(v))
|
|
309
|
+
v = v[0];
|
|
310
|
+
return this._pathWalkThrough(child, child.dataType, v, path);
|
|
311
|
+
}
|
|
312
|
+
return { value: v, dataType: child.dataType, path };
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return { path };
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
exports.OpraAdapter = OpraAdapter;
|
|
@@ -7,12 +7,9 @@ const strict_typed_events_1 = require("strict-typed-events");
|
|
|
7
7
|
const url_1 = require("@opra/url");
|
|
8
8
|
const http_adapter_js_1 = require("./http-adapter.js");
|
|
9
9
|
class OpraExpressAdapter extends http_adapter_js_1.OpraHttpAdapter {
|
|
10
|
-
static async init(app,
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
...options,
|
|
14
|
-
i18n
|
|
15
|
-
});
|
|
10
|
+
static async init(app, document, options) {
|
|
11
|
+
const adapter = new OpraExpressAdapter(document);
|
|
12
|
+
await adapter._init(options);
|
|
16
13
|
const prefix = '/' + (0, url_1.normalizePath)(options?.prefix, true);
|
|
17
14
|
app.use(prefix, body_parser_1.default.json());
|
|
18
15
|
app.use(prefix, (request, response, next) => {
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OpraHttpAdapter = void 0;
|
|
4
|
+
const common_1 = require("@opra/common");
|
|
5
|
+
const exception_1 = require("@opra/exception");
|
|
6
|
+
const schema_1 = require("@opra/schema");
|
|
7
|
+
const url_1 = require("@opra/url");
|
|
8
|
+
const adapter_js_1 = require("./adapter.js");
|
|
9
|
+
const query_context_js_1 = require("./query-context.js");
|
|
10
|
+
class OpraHttpAdapter extends adapter_js_1.OpraAdapter {
|
|
11
|
+
prepareRequests(executionContext) {
|
|
12
|
+
const req = executionContext.getRequestWrapper();
|
|
13
|
+
// todo implement batch requests
|
|
14
|
+
if (this.isBatch(executionContext)) {
|
|
15
|
+
throw new Error('not implemented yet');
|
|
16
|
+
}
|
|
17
|
+
const url = new url_1.OpraURL(req.getUrl());
|
|
18
|
+
return [
|
|
19
|
+
this.prepareRequest(executionContext, url, req.getMethod(), new common_1.HeadersMap(req.getHeaders()), req.getBody())
|
|
20
|
+
];
|
|
21
|
+
}
|
|
22
|
+
prepareRequest(executionContext, url, method, headers, body) {
|
|
23
|
+
if (!url.path.size)
|
|
24
|
+
throw new exception_1.BadRequestError();
|
|
25
|
+
if (method !== 'GET' && url.path.size > 1)
|
|
26
|
+
throw new exception_1.BadRequestError();
|
|
27
|
+
const query = this.buildQuery(url, method, body);
|
|
28
|
+
if (!query)
|
|
29
|
+
throw new exception_1.MethodNotAllowedError({
|
|
30
|
+
message: `Method "${method}" is not allowed by target endpoint`
|
|
31
|
+
});
|
|
32
|
+
return new query_context_js_1.QueryContext({
|
|
33
|
+
service: this.document,
|
|
34
|
+
executionContext,
|
|
35
|
+
query,
|
|
36
|
+
headers: new common_1.HeadersMap(),
|
|
37
|
+
params: url.searchParams,
|
|
38
|
+
continueOnError: query.operation === 'read'
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
buildQuery(url, method, body) {
|
|
42
|
+
const pathLen = url.path.size;
|
|
43
|
+
let p = url.path.get(0);
|
|
44
|
+
let resource = this._internalResources.get(p.resource) || this.document.getResource(p.resource);
|
|
45
|
+
let container;
|
|
46
|
+
let pathIndex = 0;
|
|
47
|
+
while (resource && resource instanceof schema_1.ContainerResourceInfo) {
|
|
48
|
+
container = resource;
|
|
49
|
+
p = url.path.get(++pathIndex);
|
|
50
|
+
resource = container.getResource(p.resource);
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
method = method.toUpperCase();
|
|
54
|
+
let query;
|
|
55
|
+
if (resource instanceof schema_1.SingletonResourceInfo && !p.key) {
|
|
56
|
+
switch (method) {
|
|
57
|
+
case 'GET': {
|
|
58
|
+
query = new schema_1.SingletonGetQuery(resource);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
else if (resource instanceof schema_1.CollectionResourceInfo) {
|
|
63
|
+
switch (method) {
|
|
64
|
+
case 'GET': {
|
|
65
|
+
if (p.key) {
|
|
66
|
+
const searchParams = url.searchParams;
|
|
67
|
+
query = new schema_1.CollectionGetQuery(resource, p.key, {
|
|
68
|
+
pick: searchParams.get('$pick'),
|
|
69
|
+
omit: searchParams.get('$omit'),
|
|
70
|
+
include: searchParams.get('$include')
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
const searchParams = url.searchParams;
|
|
75
|
+
query = new schema_1.CollectionSearchQuery(resource, {
|
|
76
|
+
filter: searchParams.get('$filter'),
|
|
77
|
+
limit: searchParams.get('$limit'),
|
|
78
|
+
skip: searchParams.get('$skip'),
|
|
79
|
+
distinct: searchParams.get('$distinct'),
|
|
80
|
+
count: searchParams.get('$count'),
|
|
81
|
+
sort: searchParams.get('$sort'),
|
|
82
|
+
pick: searchParams.get('$pick'),
|
|
83
|
+
omit: searchParams.get('$omit'),
|
|
84
|
+
include: searchParams.get('$include')
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
case 'DELETE': {
|
|
90
|
+
const searchParams = url.searchParams;
|
|
91
|
+
query = p.key
|
|
92
|
+
? new schema_1.CollectionDeleteQuery(resource, p.key)
|
|
93
|
+
: new schema_1.CollectionDeleteManyQuery(resource, {
|
|
94
|
+
filter: searchParams.get('$filter'),
|
|
95
|
+
});
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
case 'POST': {
|
|
99
|
+
if (!p.key) {
|
|
100
|
+
const searchParams = url.searchParams;
|
|
101
|
+
query = new schema_1.CollectionCreateQuery(resource, body, {
|
|
102
|
+
pick: searchParams.get('$pick'),
|
|
103
|
+
omit: searchParams.get('$omit'),
|
|
104
|
+
include: searchParams.get('$include')
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
case 'PATCH': {
|
|
110
|
+
if (p.key) {
|
|
111
|
+
const searchParams = url.searchParams;
|
|
112
|
+
query = new schema_1.CollectionUpdateQuery(resource, p.key, body, {
|
|
113
|
+
pick: searchParams.get('$pick'),
|
|
114
|
+
omit: searchParams.get('$omit'),
|
|
115
|
+
include: searchParams.get('$include')
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
const searchParams = url.searchParams;
|
|
120
|
+
query = new schema_1.CollectionUpdateManyQuery(resource, body, {
|
|
121
|
+
filter: searchParams.get('$filter')
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
else
|
|
129
|
+
throw new exception_1.InternalServerError();
|
|
130
|
+
if (query instanceof schema_1.SingletonGetQuery || query instanceof schema_1.CollectionGetQuery || query instanceof schema_1.FieldGetQuery) {
|
|
131
|
+
// Move through properties
|
|
132
|
+
let parentType;
|
|
133
|
+
const curPath = [];
|
|
134
|
+
let parent = query;
|
|
135
|
+
while (++pathIndex < pathLen) {
|
|
136
|
+
p = url.path.get(pathIndex);
|
|
137
|
+
parentType = parent.dataType;
|
|
138
|
+
if (parent.dataType instanceof schema_1.UnionType) {
|
|
139
|
+
if (parent.dataType.name === 'any')
|
|
140
|
+
parentType = this.document.getComplexDataType('object');
|
|
141
|
+
else
|
|
142
|
+
throw new TypeError(`"${resource.name}.${curPath.join()}" is a UnionType and needs type casting.`);
|
|
143
|
+
}
|
|
144
|
+
if (!(parentType instanceof schema_1.ComplexType))
|
|
145
|
+
throw new TypeError(`"${resource.name}.${curPath.join()}" is not a ComplexType and has no fields.`);
|
|
146
|
+
curPath.push(p.resource);
|
|
147
|
+
parent.child = new schema_1.FieldGetQuery(parent, p.resource, { castingType: parentType });
|
|
148
|
+
parent = parent.child;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return query;
|
|
152
|
+
}
|
|
153
|
+
catch (e) {
|
|
154
|
+
if (e instanceof exception_1.OpraException)
|
|
155
|
+
throw e;
|
|
156
|
+
throw new exception_1.BadRequestError(e);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
async sendResponse(executionContext, queryContexts) {
|
|
160
|
+
const outputPackets = [];
|
|
161
|
+
for (const ctx of queryContexts) {
|
|
162
|
+
const v = this.createOutput(ctx);
|
|
163
|
+
outputPackets.push(v);
|
|
164
|
+
}
|
|
165
|
+
if (this.isBatch(executionContext)) {
|
|
166
|
+
// this.writeError([], new InternalServerError({message: 'Not implemented yet'}));
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
if (!outputPackets.length)
|
|
170
|
+
return this.sendError(executionContext, new exception_1.NotFoundError());
|
|
171
|
+
const out = outputPackets[0];
|
|
172
|
+
const resp = executionContext.getResponseWrapper();
|
|
173
|
+
resp.setStatus(out.status);
|
|
174
|
+
resp.setHeader(common_1.HttpHeaders.Content_Type, 'application/json');
|
|
175
|
+
resp.setHeader(common_1.HttpHeaders.Cache_Control, 'no-cache');
|
|
176
|
+
resp.setHeader(common_1.HttpHeaders.Pragma, 'no-cache');
|
|
177
|
+
resp.setHeader(common_1.HttpHeaders.Expires, '-1');
|
|
178
|
+
resp.setHeader(common_1.HttpHeaders.X_Opra_Version, schema_1.OpraSchema.Version);
|
|
179
|
+
if (out.headers) {
|
|
180
|
+
for (const [k, v] of Object.entries(out.headers)) {
|
|
181
|
+
resp.setHeader(k, v);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
resp.send(JSON.stringify(out.body));
|
|
185
|
+
resp.end();
|
|
186
|
+
}
|
|
187
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
188
|
+
isBatch(executionContext) {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
createOutput(ctx) {
|
|
192
|
+
const { query } = ctx;
|
|
193
|
+
let body;
|
|
194
|
+
let status = ctx.status || 0;
|
|
195
|
+
const errors = ctx.errors.map(e => (0, exception_1.wrapException)(e));
|
|
196
|
+
if (errors && errors.length) {
|
|
197
|
+
// Sort errors from fatal to info
|
|
198
|
+
errors.sort((a, b) => {
|
|
199
|
+
const i = exception_1.IssueSeverity.Keys.indexOf(a.issue.severity) - exception_1.IssueSeverity.Keys.indexOf(b.issue.severity);
|
|
200
|
+
if (i === 0)
|
|
201
|
+
return b.status - a.status;
|
|
202
|
+
return i;
|
|
203
|
+
});
|
|
204
|
+
if (!status || status < common_1.HttpStatus.BAD_REQUEST) {
|
|
205
|
+
status = errors[0].status;
|
|
206
|
+
if (status < common_1.HttpStatus.BAD_REQUEST)
|
|
207
|
+
status = common_1.HttpStatus.INTERNAL_SERVER_ERROR;
|
|
208
|
+
}
|
|
209
|
+
body = {
|
|
210
|
+
operation: ctx.query.method,
|
|
211
|
+
errors: errors.map(e => e.issue)
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
body = ctx.response;
|
|
216
|
+
status = status || (query.operation === 'create' ? common_1.HttpStatus.CREATED : common_1.HttpStatus.OK);
|
|
217
|
+
}
|
|
218
|
+
body = this.i18n.deep(body);
|
|
219
|
+
return {
|
|
220
|
+
status,
|
|
221
|
+
headers: ctx.responseHeaders.toObject(),
|
|
222
|
+
body
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
async sendError(executionContext, error) {
|
|
226
|
+
const resp = executionContext.getResponseWrapper();
|
|
227
|
+
resp.setStatus(error.status || 500);
|
|
228
|
+
resp.setHeader(common_1.HttpHeaders.Content_Type, 'application/json');
|
|
229
|
+
resp.setHeader(common_1.HttpHeaders.Cache_Control, 'no-cache');
|
|
230
|
+
resp.setHeader(common_1.HttpHeaders.Pragma, 'no-cache');
|
|
231
|
+
resp.setHeader(common_1.HttpHeaders.Expires, '-1');
|
|
232
|
+
resp.setHeader(common_1.HttpHeaders.X_Opra_Version, schema_1.OpraSchema.Version);
|
|
233
|
+
const issue = this.i18n.deep(error.issue);
|
|
234
|
+
const body = {
|
|
235
|
+
operation: 'unknown',
|
|
236
|
+
errors: [issue]
|
|
237
|
+
};
|
|
238
|
+
resp.send(JSON.stringify(body));
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
exports.OpraHttpAdapter = OpraHttpAdapter;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MetadataResource = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const schema_1 = require("@opra/schema");
|
|
6
|
+
const json_singleton_service_js_1 = require("../services/json-singleton-service.js");
|
|
7
|
+
let MetadataResource = class MetadataResource {
|
|
8
|
+
service;
|
|
9
|
+
init(resource) {
|
|
10
|
+
this.service = new json_singleton_service_js_1.JsonSingletonService(resource.dataType, {
|
|
11
|
+
data: resource.document.getMetadata(true)
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
get() {
|
|
15
|
+
return this.service.get();
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
MetadataResource = tslib_1.__decorate([
|
|
19
|
+
(0, schema_1.OprSingletonResource)(Object, {
|
|
20
|
+
name: '$metadata'
|
|
21
|
+
})
|
|
22
|
+
], MetadataResource);
|
|
23
|
+
exports.MetadataResource = MetadataResource;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.QueryContext = void 0;
|
|
4
|
+
const common_1 = require("@opra/common");
|
|
4
5
|
const url_1 = require("@opra/url");
|
|
5
|
-
const headers_map_js_1 = require("./headers-map.js");
|
|
6
6
|
class QueryContext {
|
|
7
7
|
service;
|
|
8
8
|
executionContext;
|
|
@@ -24,8 +24,8 @@ class QueryContext {
|
|
|
24
24
|
this.query = args.query;
|
|
25
25
|
// this.response = new QueryResponse();
|
|
26
26
|
this.params = this.params || new url_1.OpraURLSearchParams();
|
|
27
|
-
this.headers = new
|
|
28
|
-
this.responseHeaders = new
|
|
27
|
+
this.headers = new common_1.HeadersMap(args.headers);
|
|
28
|
+
this.responseHeaders = new common_1.HeadersMap();
|
|
29
29
|
this.resultPath = this.resultPath || '';
|
|
30
30
|
}
|
|
31
31
|
get type() {
|
package/cjs/index.js
CHANGED
|
@@ -3,12 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const tslib_1 = require("tslib");
|
|
4
4
|
require("reflect-metadata");
|
|
5
5
|
tslib_1.__exportStar(require("./types.js"), exports);
|
|
6
|
-
tslib_1.__exportStar(require("./enums/index.js"), exports);
|
|
7
6
|
tslib_1.__exportStar(require("./interfaces/execution-context.interface.js"), exports);
|
|
8
|
-
tslib_1.__exportStar(require("./interfaces/
|
|
9
|
-
tslib_1.__exportStar(require("./
|
|
10
|
-
tslib_1.__exportStar(require("./
|
|
11
|
-
tslib_1.__exportStar(require("./
|
|
12
|
-
tslib_1.__exportStar(require("./
|
|
7
|
+
tslib_1.__exportStar(require("./interfaces/resource.interface.js"), exports);
|
|
8
|
+
tslib_1.__exportStar(require("./adapter/query-context.js"), exports);
|
|
9
|
+
tslib_1.__exportStar(require("./adapter/adapter.js"), exports);
|
|
10
|
+
tslib_1.__exportStar(require("./adapter/http-adapter.js"), exports);
|
|
11
|
+
tslib_1.__exportStar(require("./adapter/express-adapter.js"), exports);
|
|
13
12
|
tslib_1.__exportStar(require("./services/data-service.js"), exports);
|
|
14
13
|
tslib_1.__exportStar(require("./services/json-collection-service.js"), exports);
|
|
14
|
+
tslib_1.__exportStar(require("./services/json-singleton-service.js"), exports);
|