@opra/core 0.10.0 → 0.13.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 +43 -17
- package/cjs/adapter/http-adapter.js +18 -18
- package/cjs/index.js +0 -1
- package/cjs/interfaces/logger.interface.js +2 -0
- package/cjs/services/json-singleton-service.js +9 -7
- package/esm/adapter/adapter.d.ts +4 -0
- package/esm/adapter/adapter.js +44 -18
- package/esm/adapter/http-adapter.js +19 -19
- package/esm/adapter/request-contexts/batch-request-context.d.ts +1 -1
- package/esm/adapter/request-contexts/request-context.d.ts +3 -3
- package/esm/adapter/request-contexts/single-request-context.d.ts +1 -1
- package/esm/enums/issue-severity.enum.d.ts +1 -1
- package/esm/index.d.ts +0 -1
- package/esm/index.js +0 -1
- package/esm/interfaces/execution-context.interface.d.ts +1 -1
- package/esm/interfaces/logger.interface.d.ts +7 -0
- package/esm/interfaces/logger.interface.js +1 -0
- package/esm/interfaces/resource.interface.d.ts +1 -0
- package/esm/services/json-singleton-service.js +2 -1
- package/package.json +19 -18
- package/cjs/services/json-collection-service.js +0 -497
- package/esm/services/json-collection-service.d.ts +0 -83
- package/esm/services/json-collection-service.js +0 -492
package/cjs/adapter/adapter.js
CHANGED
|
@@ -14,6 +14,17 @@ class OpraAdapter {
|
|
|
14
14
|
this.document = document;
|
|
15
15
|
this._internalResources = new common_1.ResponsiveMap();
|
|
16
16
|
}
|
|
17
|
+
async close() {
|
|
18
|
+
const promises = [];
|
|
19
|
+
for (const r of this.document.resources.values()) {
|
|
20
|
+
if (r.instance) {
|
|
21
|
+
const shutDown = r.instance.shutDown;
|
|
22
|
+
if (shutDown)
|
|
23
|
+
promises.push((async () => shutDown.call(r.instance))());
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
await Promise.allSettled(promises);
|
|
27
|
+
}
|
|
17
28
|
async handler(executionContext) {
|
|
18
29
|
let requestContext;
|
|
19
30
|
let failed = false;
|
|
@@ -29,6 +40,13 @@ class OpraAdapter {
|
|
|
29
40
|
failed = true;
|
|
30
41
|
const error = (0, common_1.wrapException)(e);
|
|
31
42
|
await this.sendError(executionContext, error);
|
|
43
|
+
if (this.logger)
|
|
44
|
+
try {
|
|
45
|
+
this.logger.error(e);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// noop
|
|
49
|
+
}
|
|
32
50
|
}
|
|
33
51
|
finally {
|
|
34
52
|
if (executionContext instanceof strict_typed_events_1.AsyncEventEmitter) {
|
|
@@ -70,6 +88,13 @@ class OpraAdapter {
|
|
|
70
88
|
requestContext.errors.push(new common_1.FailedDependencyError());
|
|
71
89
|
else
|
|
72
90
|
requestContext.errors.push((0, common_1.wrapException)(task.error));
|
|
91
|
+
if (this.logger)
|
|
92
|
+
try {
|
|
93
|
+
this.logger.error(task.error);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// noop
|
|
97
|
+
}
|
|
73
98
|
return;
|
|
74
99
|
}
|
|
75
100
|
});
|
|
@@ -79,6 +104,7 @@ class OpraAdapter {
|
|
|
79
104
|
throw new TypeError('Invalid request context instance');
|
|
80
105
|
}
|
|
81
106
|
async _init(options) {
|
|
107
|
+
this.logger = options?.logger;
|
|
82
108
|
if (options?.i18n instanceof common_1.I18n)
|
|
83
109
|
this.i18n = options.i18n;
|
|
84
110
|
else if (typeof options?.i18n === 'function')
|
|
@@ -124,7 +150,7 @@ class OpraAdapter {
|
|
|
124
150
|
Object.setPrototypeOf(ctx, context);
|
|
125
151
|
promises.push(this._executeCollectionResource(document, resource, ctx)
|
|
126
152
|
.then(r => {
|
|
127
|
-
context.responseHeaders[common_1.
|
|
153
|
+
context.responseHeaders[common_1.HttpHeaderCodes.X_Opra_Count] = r;
|
|
128
154
|
}));
|
|
129
155
|
}
|
|
130
156
|
await Promise.all(promises);
|
|
@@ -142,8 +168,8 @@ class OpraAdapter {
|
|
|
142
168
|
}
|
|
143
169
|
async _executeCollectionResource(document, resource, context) {
|
|
144
170
|
const method = context.query.method;
|
|
145
|
-
const
|
|
146
|
-
if (!
|
|
171
|
+
const handler = resource.getHandler(method);
|
|
172
|
+
if (!handler)
|
|
147
173
|
throw new common_1.ForbiddenError({
|
|
148
174
|
message: (0, common_1.translate)('RESOLVER_FORBIDDEN', { method }, `The resource endpoint does not accept '{{method}}' operations`),
|
|
149
175
|
severity: 'error',
|
|
@@ -153,21 +179,21 @@ class OpraAdapter {
|
|
|
153
179
|
switch (method) {
|
|
154
180
|
case 'create': {
|
|
155
181
|
const query = context.query;
|
|
156
|
-
result = await
|
|
182
|
+
result = await handler(context, query.data, query);
|
|
157
183
|
result = Array.isArray(result) ? result[0] : result;
|
|
158
184
|
if (result)
|
|
159
185
|
context.status = 201;
|
|
160
|
-
context.responseHeaders[common_1.
|
|
186
|
+
context.responseHeaders[common_1.HttpHeaderCodes.X_Opra_DataType] = resource.dataType.name;
|
|
161
187
|
return result;
|
|
162
188
|
}
|
|
163
189
|
case 'count': {
|
|
164
190
|
const query = context.query;
|
|
165
|
-
result = await
|
|
191
|
+
result = await handler(context, query);
|
|
166
192
|
return result;
|
|
167
193
|
}
|
|
168
194
|
case 'get': {
|
|
169
195
|
const query = context.query;
|
|
170
|
-
result = await
|
|
196
|
+
result = await handler(context, query.keyValue, query);
|
|
171
197
|
result = Array.isArray(result) ? result[0] : result;
|
|
172
198
|
if (!result)
|
|
173
199
|
throw new common_1.ResourceNotFoundError(resource.name, query.keyValue);
|
|
@@ -175,23 +201,23 @@ class OpraAdapter {
|
|
|
175
201
|
if (v.value === undefined)
|
|
176
202
|
throw new common_1.ResourceNotFoundError(v.path);
|
|
177
203
|
if (v.dataType)
|
|
178
|
-
context.responseHeaders[common_1.
|
|
204
|
+
context.responseHeaders[common_1.HttpHeaderCodes.X_Opra_DataType] = v.dataType.name;
|
|
179
205
|
return v.value;
|
|
180
206
|
}
|
|
181
207
|
case 'search': {
|
|
182
208
|
const query = context.query;
|
|
183
|
-
result = await
|
|
209
|
+
result = await handler(context, query);
|
|
184
210
|
const items = Array.isArray(result) ? result : (context.response ? [result] : []);
|
|
185
|
-
context.responseHeaders[common_1.
|
|
211
|
+
context.responseHeaders[common_1.HttpHeaderCodes.X_Opra_DataType] = resource.dataType.name;
|
|
186
212
|
return items;
|
|
187
213
|
}
|
|
188
214
|
case 'update': {
|
|
189
215
|
const query = context.query;
|
|
190
|
-
result = await
|
|
216
|
+
result = await handler(context, query.keyValue, query.data, query);
|
|
191
217
|
result = Array.isArray(result) ? result[0] : result;
|
|
192
218
|
if (!result)
|
|
193
219
|
throw new common_1.ResourceNotFoundError(resource.name, query.keyValue);
|
|
194
|
-
context.responseHeaders[common_1.
|
|
220
|
+
context.responseHeaders[common_1.HttpHeaderCodes.X_Opra_DataType] = resource.dataType.name;
|
|
195
221
|
return result;
|
|
196
222
|
}
|
|
197
223
|
case 'delete':
|
|
@@ -200,17 +226,17 @@ class OpraAdapter {
|
|
|
200
226
|
switch (method) {
|
|
201
227
|
case 'delete': {
|
|
202
228
|
const query = context.query;
|
|
203
|
-
result = await
|
|
229
|
+
result = await handler(context, query.keyValue, query);
|
|
204
230
|
break;
|
|
205
231
|
}
|
|
206
232
|
case 'deleteMany': {
|
|
207
233
|
const query = context.query;
|
|
208
|
-
result = await
|
|
234
|
+
result = await handler(context, query);
|
|
209
235
|
break;
|
|
210
236
|
}
|
|
211
237
|
case 'updateMany': {
|
|
212
238
|
const query = context.query;
|
|
213
|
-
result = await
|
|
239
|
+
result = await handler(context, query.data, query);
|
|
214
240
|
break;
|
|
215
241
|
}
|
|
216
242
|
}
|
|
@@ -249,7 +275,7 @@ class OpraAdapter {
|
|
|
249
275
|
if (v.value === undefined)
|
|
250
276
|
throw new common_1.ResourceNotFoundError(v.path);
|
|
251
277
|
if (v.dataType)
|
|
252
|
-
context.responseHeaders[common_1.
|
|
278
|
+
context.responseHeaders[common_1.HttpHeaderCodes.X_Opra_DataType] = v.dataType.name;
|
|
253
279
|
return v.value;
|
|
254
280
|
}
|
|
255
281
|
}
|
|
@@ -267,7 +293,7 @@ class OpraAdapter {
|
|
|
267
293
|
}
|
|
268
294
|
if (method === 'create')
|
|
269
295
|
context.status = 201;
|
|
270
|
-
context.responseHeaders[common_1.
|
|
296
|
+
context.responseHeaders[common_1.HttpHeaderCodes.X_Opra_DataType] = resource.dataType.name;
|
|
271
297
|
return result;
|
|
272
298
|
}
|
|
273
299
|
async _pathWalkThrough(query, dataType, value, parentPath) {
|
|
@@ -261,9 +261,9 @@ class OpraHttpAdapter extends adapter_js_1.OpraAdapter {
|
|
|
261
261
|
// protected async sendBatchResponse(executionContext: TExecutionContext, requestContext: BatchRequestContext) {
|
|
262
262
|
// const resp = executionContext.getResponse();
|
|
263
263
|
// resp.setStatus(HttpStatus.OK);
|
|
264
|
-
// resp.setHeader(
|
|
265
|
-
// resp.setHeader(
|
|
266
|
-
// resp.setHeader(
|
|
264
|
+
// resp.setHeader(HttpHeaderCodes.Cache_Control, 'no-cache');
|
|
265
|
+
// resp.setHeader(HttpHeaderCodes.Pragma, 'no-cache');
|
|
266
|
+
// resp.setHeader(HttpHeaderCodes.Expires, '-1');
|
|
267
267
|
// if (requestContext.headers) {
|
|
268
268
|
// for (const [k, v] of Object.entries(requestContext.headers)) {
|
|
269
269
|
// if (v)
|
|
@@ -271,8 +271,8 @@ class OpraHttpAdapter extends adapter_js_1.OpraAdapter {
|
|
|
271
271
|
// }
|
|
272
272
|
// }
|
|
273
273
|
// const boundary = 'batch_' + uuid();
|
|
274
|
-
// resp.setHeader(
|
|
275
|
-
// resp.setHeader(
|
|
274
|
+
// resp.setHeader(HttpHeaderCodes.Content_Type, 'multipart/mixed;boundary=' + boundary);
|
|
275
|
+
// resp.setHeader(HttpHeaderCodes.X_Opra_Version, OpraSchema.Version);
|
|
276
276
|
//
|
|
277
277
|
// const bodyBuilder = new HttpMultipartData();
|
|
278
278
|
//
|
|
@@ -352,16 +352,16 @@ class OpraHttpAdapter extends adapter_js_1.OpraAdapter {
|
|
|
352
352
|
const out = this.createOutput(requestContext);
|
|
353
353
|
const resp = executionContext.getResponse();
|
|
354
354
|
resp.setStatus(out.status);
|
|
355
|
-
resp.setHeader(common_1.
|
|
356
|
-
resp.setHeader(common_1.
|
|
357
|
-
resp.setHeader(common_1.
|
|
355
|
+
resp.setHeader(common_1.HttpHeaderCodes.Cache_Control, 'no-cache');
|
|
356
|
+
resp.setHeader(common_1.HttpHeaderCodes.Pragma, 'no-cache');
|
|
357
|
+
resp.setHeader(common_1.HttpHeaderCodes.Expires, '-1');
|
|
358
358
|
if (out.headers) {
|
|
359
359
|
for (const [k, v] of Object.entries(out.headers)) {
|
|
360
360
|
if (v)
|
|
361
361
|
resp.setHeader(k, v);
|
|
362
362
|
}
|
|
363
363
|
}
|
|
364
|
-
resp.setHeader(common_1.
|
|
364
|
+
resp.setHeader(common_1.HttpHeaderCodes.X_Opra_Version, common_1.OpraSchema.Version);
|
|
365
365
|
if (out.body)
|
|
366
366
|
resp.send(out.body);
|
|
367
367
|
resp.end();
|
|
@@ -379,10 +379,10 @@ class OpraHttpAdapter extends adapter_js_1.OpraAdapter {
|
|
|
379
379
|
return b.status - a.status;
|
|
380
380
|
return i;
|
|
381
381
|
});
|
|
382
|
-
if (!status || status < common_1.
|
|
382
|
+
if (!status || status < common_1.HttpStatusCodes.BAD_REQUEST) {
|
|
383
383
|
status = errors[0].status;
|
|
384
|
-
if (status < common_1.
|
|
385
|
-
status = common_1.
|
|
384
|
+
if (status < common_1.HttpStatusCodes.BAD_REQUEST)
|
|
385
|
+
status = common_1.HttpStatusCodes.INTERNAL_SERVER_ERROR;
|
|
386
386
|
}
|
|
387
387
|
body = this.i18n.deep({
|
|
388
388
|
operation: ctx.query.method,
|
|
@@ -397,7 +397,7 @@ class OpraHttpAdapter extends adapter_js_1.OpraAdapter {
|
|
|
397
397
|
body = JSON.stringify(body);
|
|
398
398
|
ctx.responseHeaders['content-type'] = 'application/json; charset=utf-8';
|
|
399
399
|
}
|
|
400
|
-
status = status || (query.operation === 'create' ? common_1.
|
|
400
|
+
status = status || (query.operation === 'create' ? common_1.HttpStatusCodes.CREATED : common_1.HttpStatusCodes.OK);
|
|
401
401
|
}
|
|
402
402
|
return {
|
|
403
403
|
status,
|
|
@@ -408,11 +408,11 @@ class OpraHttpAdapter extends adapter_js_1.OpraAdapter {
|
|
|
408
408
|
async sendError(executionContext, error) {
|
|
409
409
|
const resp = executionContext.getResponse();
|
|
410
410
|
resp.setStatus(error.status || 500);
|
|
411
|
-
resp.setHeader(common_1.
|
|
412
|
-
resp.setHeader(common_1.
|
|
413
|
-
resp.setHeader(common_1.
|
|
414
|
-
resp.setHeader(common_1.
|
|
415
|
-
resp.setHeader(common_1.
|
|
411
|
+
resp.setHeader(common_1.HttpHeaderCodes.Content_Type, 'application/json');
|
|
412
|
+
resp.setHeader(common_1.HttpHeaderCodes.Cache_Control, 'no-cache');
|
|
413
|
+
resp.setHeader(common_1.HttpHeaderCodes.Pragma, 'no-cache');
|
|
414
|
+
resp.setHeader(common_1.HttpHeaderCodes.Expires, '-1');
|
|
415
|
+
resp.setHeader(common_1.HttpHeaderCodes.X_Opra_Version, common_1.OpraSchema.Version);
|
|
416
416
|
const issue = this.i18n.deep(error.issue);
|
|
417
417
|
const body = {
|
|
418
418
|
operation: 'unknown',
|
package/cjs/index.js
CHANGED
|
@@ -11,5 +11,4 @@ tslib_1.__exportStar(require("./adapter/adapter.js"), exports);
|
|
|
11
11
|
tslib_1.__exportStar(require("./adapter/http-adapter.js"), exports);
|
|
12
12
|
tslib_1.__exportStar(require("./adapter/express-adapter.js"), exports);
|
|
13
13
|
tslib_1.__exportStar(require("./services/data-service.js"), exports);
|
|
14
|
-
tslib_1.__exportStar(require("./services/json-collection-service.js"), exports);
|
|
15
14
|
tslib_1.__exportStar(require("./services/json-singleton-service.js"), exports);
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.JsonSingletonService = void 0;
|
|
4
|
-
const
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const lodash_isnil_1 = tslib_1.__importDefault(require("lodash.isnil"));
|
|
6
|
+
const lodash_omitby_1 = tslib_1.__importDefault(require("lodash.omitby"));
|
|
5
7
|
const common_1 = require("@opra/common");
|
|
6
8
|
class JsonSingletonService {
|
|
7
9
|
constructor(dataType, options) {
|
|
@@ -28,11 +30,11 @@ class JsonSingletonService {
|
|
|
28
30
|
}
|
|
29
31
|
switch (query.method) {
|
|
30
32
|
case 'create': {
|
|
31
|
-
const options = (0,
|
|
33
|
+
const options = (0, lodash_omitby_1.default)({
|
|
32
34
|
pick: query.pick,
|
|
33
35
|
omit: query.omit,
|
|
34
36
|
include: query.include
|
|
35
|
-
},
|
|
37
|
+
}, lodash_isnil_1.default);
|
|
36
38
|
const { data } = query;
|
|
37
39
|
return {
|
|
38
40
|
method: query.method,
|
|
@@ -43,11 +45,11 @@ class JsonSingletonService {
|
|
|
43
45
|
}
|
|
44
46
|
case 'get': {
|
|
45
47
|
if (query.kind === 'CollectionGetQuery') {
|
|
46
|
-
const options = (0,
|
|
48
|
+
const options = (0, lodash_omitby_1.default)({
|
|
47
49
|
pick: query.pick,
|
|
48
50
|
omit: query.omit,
|
|
49
51
|
include: query.include
|
|
50
|
-
},
|
|
52
|
+
}, lodash_isnil_1.default);
|
|
51
53
|
const keyValue = query.keyValue;
|
|
52
54
|
return {
|
|
53
55
|
method: query.method,
|
|
@@ -62,11 +64,11 @@ class JsonSingletonService {
|
|
|
62
64
|
break;
|
|
63
65
|
}
|
|
64
66
|
case 'update': {
|
|
65
|
-
const options = (0,
|
|
67
|
+
const options = (0, lodash_omitby_1.default)({
|
|
66
68
|
pick: query.pick,
|
|
67
69
|
omit: query.omit,
|
|
68
70
|
include: query.include
|
|
69
|
-
},
|
|
71
|
+
}, lodash_isnil_1.default);
|
|
70
72
|
const { data } = query;
|
|
71
73
|
const keyValue = query.keyValue;
|
|
72
74
|
return {
|
package/esm/adapter/adapter.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { Task } from 'power-tasks';
|
|
|
2
2
|
import { CollectionGetQuery, CollectionResourceInfo, ComplexType, DataType, FieldGetQuery, I18n, OpraDocument, OpraException, ResourceInfo, ResponsiveMap, SingletonGetQuery, SingletonResourceInfo } from '@opra/common';
|
|
3
3
|
import { IExecutionContext } from '../interfaces/execution-context.interface.js';
|
|
4
4
|
import { I18nOptions } from '../interfaces/i18n-options.interface.js';
|
|
5
|
+
import { ILogger } from '../interfaces/logger.interface.js';
|
|
5
6
|
import { RequestContext } from './request-contexts/request-context.js';
|
|
6
7
|
import { SingleRequestContext } from './request-contexts/single-request-context.js';
|
|
7
8
|
export declare namespace OpraAdapter {
|
|
@@ -9,6 +10,7 @@ export declare namespace OpraAdapter {
|
|
|
9
10
|
interface Options {
|
|
10
11
|
i18n?: I18n | I18nOptions | (() => Promise<I18n>);
|
|
11
12
|
userContext?: UserContextResolver;
|
|
13
|
+
logger?: ILogger;
|
|
12
14
|
}
|
|
13
15
|
}
|
|
14
16
|
export declare abstract class OpraAdapter<TExecutionContext extends IExecutionContext> {
|
|
@@ -16,7 +18,9 @@ export declare abstract class OpraAdapter<TExecutionContext extends IExecutionCo
|
|
|
16
18
|
protected userContextResolver?: OpraAdapter.UserContextResolver;
|
|
17
19
|
protected _internalResources: ResponsiveMap<string, ResourceInfo>;
|
|
18
20
|
i18n: I18n;
|
|
21
|
+
logger?: ILogger;
|
|
19
22
|
constructor(document: OpraDocument);
|
|
23
|
+
close(): Promise<void>;
|
|
20
24
|
protected abstract parse(executionContext: TExecutionContext): Promise<RequestContext>;
|
|
21
25
|
protected abstract sendResponse(executionContext: TExecutionContext, requestContext: RequestContext): Promise<void>;
|
|
22
26
|
protected abstract sendError(executionContext: TExecutionContext, error: OpraException): Promise<void>;
|
package/esm/adapter/adapter.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Task } from 'power-tasks';
|
|
2
2
|
import { AsyncEventEmitter } from 'strict-typed-events';
|
|
3
|
-
import { CollectionCountQuery, CollectionResourceInfo, CollectionSearchQuery, ComplexType, FailedDependencyError, ForbiddenError,
|
|
3
|
+
import { CollectionCountQuery, CollectionResourceInfo, CollectionSearchQuery, ComplexType, FailedDependencyError, ForbiddenError, HttpHeaderCodes, I18n, InternalServerError, ResourceNotFoundError, ResponsiveMap, SingletonResourceInfo, translate, wrapException } from '@opra/common';
|
|
4
4
|
import { createI18n } from '../utils/create-i18n.js';
|
|
5
5
|
import { MetadataResource } from './classes/metadata.resource.js';
|
|
6
6
|
import { BatchRequestContext } from './request-contexts/batch-request-context.js';
|
|
@@ -11,6 +11,17 @@ export class OpraAdapter {
|
|
|
11
11
|
this.document = document;
|
|
12
12
|
this._internalResources = new ResponsiveMap();
|
|
13
13
|
}
|
|
14
|
+
async close() {
|
|
15
|
+
const promises = [];
|
|
16
|
+
for (const r of this.document.resources.values()) {
|
|
17
|
+
if (r.instance) {
|
|
18
|
+
const shutDown = r.instance.shutDown;
|
|
19
|
+
if (shutDown)
|
|
20
|
+
promises.push((async () => shutDown.call(r.instance))());
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
await Promise.allSettled(promises);
|
|
24
|
+
}
|
|
14
25
|
async handler(executionContext) {
|
|
15
26
|
let requestContext;
|
|
16
27
|
let failed = false;
|
|
@@ -26,6 +37,13 @@ export class OpraAdapter {
|
|
|
26
37
|
failed = true;
|
|
27
38
|
const error = wrapException(e);
|
|
28
39
|
await this.sendError(executionContext, error);
|
|
40
|
+
if (this.logger)
|
|
41
|
+
try {
|
|
42
|
+
this.logger.error(e);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// noop
|
|
46
|
+
}
|
|
29
47
|
}
|
|
30
48
|
finally {
|
|
31
49
|
if (executionContext instanceof AsyncEventEmitter) {
|
|
@@ -67,6 +85,13 @@ export class OpraAdapter {
|
|
|
67
85
|
requestContext.errors.push(new FailedDependencyError());
|
|
68
86
|
else
|
|
69
87
|
requestContext.errors.push(wrapException(task.error));
|
|
88
|
+
if (this.logger)
|
|
89
|
+
try {
|
|
90
|
+
this.logger.error(task.error);
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
// noop
|
|
94
|
+
}
|
|
70
95
|
return;
|
|
71
96
|
}
|
|
72
97
|
});
|
|
@@ -76,6 +101,7 @@ export class OpraAdapter {
|
|
|
76
101
|
throw new TypeError('Invalid request context instance');
|
|
77
102
|
}
|
|
78
103
|
async _init(options) {
|
|
104
|
+
this.logger = options?.logger;
|
|
79
105
|
if (options?.i18n instanceof I18n)
|
|
80
106
|
this.i18n = options.i18n;
|
|
81
107
|
else if (typeof options?.i18n === 'function')
|
|
@@ -121,7 +147,7 @@ export class OpraAdapter {
|
|
|
121
147
|
Object.setPrototypeOf(ctx, context);
|
|
122
148
|
promises.push(this._executeCollectionResource(document, resource, ctx)
|
|
123
149
|
.then(r => {
|
|
124
|
-
context.responseHeaders[
|
|
150
|
+
context.responseHeaders[HttpHeaderCodes.X_Opra_Count] = r;
|
|
125
151
|
}));
|
|
126
152
|
}
|
|
127
153
|
await Promise.all(promises);
|
|
@@ -139,8 +165,8 @@ export class OpraAdapter {
|
|
|
139
165
|
}
|
|
140
166
|
async _executeCollectionResource(document, resource, context) {
|
|
141
167
|
const method = context.query.method;
|
|
142
|
-
const
|
|
143
|
-
if (!
|
|
168
|
+
const handler = resource.getHandler(method);
|
|
169
|
+
if (!handler)
|
|
144
170
|
throw new ForbiddenError({
|
|
145
171
|
message: translate('RESOLVER_FORBIDDEN', { method }, `The resource endpoint does not accept '{{method}}' operations`),
|
|
146
172
|
severity: 'error',
|
|
@@ -150,21 +176,21 @@ export class OpraAdapter {
|
|
|
150
176
|
switch (method) {
|
|
151
177
|
case 'create': {
|
|
152
178
|
const query = context.query;
|
|
153
|
-
result = await
|
|
179
|
+
result = await handler(context, query.data, query);
|
|
154
180
|
result = Array.isArray(result) ? result[0] : result;
|
|
155
181
|
if (result)
|
|
156
182
|
context.status = 201;
|
|
157
|
-
context.responseHeaders[
|
|
183
|
+
context.responseHeaders[HttpHeaderCodes.X_Opra_DataType] = resource.dataType.name;
|
|
158
184
|
return result;
|
|
159
185
|
}
|
|
160
186
|
case 'count': {
|
|
161
187
|
const query = context.query;
|
|
162
|
-
result = await
|
|
188
|
+
result = await handler(context, query);
|
|
163
189
|
return result;
|
|
164
190
|
}
|
|
165
191
|
case 'get': {
|
|
166
192
|
const query = context.query;
|
|
167
|
-
result = await
|
|
193
|
+
result = await handler(context, query.keyValue, query);
|
|
168
194
|
result = Array.isArray(result) ? result[0] : result;
|
|
169
195
|
if (!result)
|
|
170
196
|
throw new ResourceNotFoundError(resource.name, query.keyValue);
|
|
@@ -172,23 +198,23 @@ export class OpraAdapter {
|
|
|
172
198
|
if (v.value === undefined)
|
|
173
199
|
throw new ResourceNotFoundError(v.path);
|
|
174
200
|
if (v.dataType)
|
|
175
|
-
context.responseHeaders[
|
|
201
|
+
context.responseHeaders[HttpHeaderCodes.X_Opra_DataType] = v.dataType.name;
|
|
176
202
|
return v.value;
|
|
177
203
|
}
|
|
178
204
|
case 'search': {
|
|
179
205
|
const query = context.query;
|
|
180
|
-
result = await
|
|
206
|
+
result = await handler(context, query);
|
|
181
207
|
const items = Array.isArray(result) ? result : (context.response ? [result] : []);
|
|
182
|
-
context.responseHeaders[
|
|
208
|
+
context.responseHeaders[HttpHeaderCodes.X_Opra_DataType] = resource.dataType.name;
|
|
183
209
|
return items;
|
|
184
210
|
}
|
|
185
211
|
case 'update': {
|
|
186
212
|
const query = context.query;
|
|
187
|
-
result = await
|
|
213
|
+
result = await handler(context, query.keyValue, query.data, query);
|
|
188
214
|
result = Array.isArray(result) ? result[0] : result;
|
|
189
215
|
if (!result)
|
|
190
216
|
throw new ResourceNotFoundError(resource.name, query.keyValue);
|
|
191
|
-
context.responseHeaders[
|
|
217
|
+
context.responseHeaders[HttpHeaderCodes.X_Opra_DataType] = resource.dataType.name;
|
|
192
218
|
return result;
|
|
193
219
|
}
|
|
194
220
|
case 'delete':
|
|
@@ -197,17 +223,17 @@ export class OpraAdapter {
|
|
|
197
223
|
switch (method) {
|
|
198
224
|
case 'delete': {
|
|
199
225
|
const query = context.query;
|
|
200
|
-
result = await
|
|
226
|
+
result = await handler(context, query.keyValue, query);
|
|
201
227
|
break;
|
|
202
228
|
}
|
|
203
229
|
case 'deleteMany': {
|
|
204
230
|
const query = context.query;
|
|
205
|
-
result = await
|
|
231
|
+
result = await handler(context, query);
|
|
206
232
|
break;
|
|
207
233
|
}
|
|
208
234
|
case 'updateMany': {
|
|
209
235
|
const query = context.query;
|
|
210
|
-
result = await
|
|
236
|
+
result = await handler(context, query.data, query);
|
|
211
237
|
break;
|
|
212
238
|
}
|
|
213
239
|
}
|
|
@@ -246,7 +272,7 @@ export class OpraAdapter {
|
|
|
246
272
|
if (v.value === undefined)
|
|
247
273
|
throw new ResourceNotFoundError(v.path);
|
|
248
274
|
if (v.dataType)
|
|
249
|
-
context.responseHeaders[
|
|
275
|
+
context.responseHeaders[HttpHeaderCodes.X_Opra_DataType] = v.dataType.name;
|
|
250
276
|
return v.value;
|
|
251
277
|
}
|
|
252
278
|
}
|
|
@@ -264,7 +290,7 @@ export class OpraAdapter {
|
|
|
264
290
|
}
|
|
265
291
|
if (method === 'create')
|
|
266
292
|
context.status = 201;
|
|
267
|
-
context.responseHeaders[
|
|
293
|
+
context.responseHeaders[HttpHeaderCodes.X_Opra_DataType] = resource.dataType.name;
|
|
268
294
|
return result;
|
|
269
295
|
}
|
|
270
296
|
async _pathWalkThrough(query, dataType, value, parentPath) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BadRequestError, CollectionCreateQuery, CollectionDeleteManyQuery, CollectionDeleteQuery, CollectionGetQuery, CollectionResourceInfo, CollectionSearchQuery, CollectionUpdateManyQuery, CollectionUpdateQuery, ComplexType, ContainerResourceInfo, FieldGetQuery,
|
|
1
|
+
import { BadRequestError, CollectionCreateQuery, CollectionDeleteManyQuery, CollectionDeleteQuery, CollectionGetQuery, CollectionResourceInfo, CollectionSearchQuery, CollectionUpdateManyQuery, CollectionUpdateQuery, ComplexType, ContainerResourceInfo, FieldGetQuery, HttpHeaderCodes, HttpStatusCodes, InternalServerError, isReadable, IssueSeverity, MethodNotAllowedError, normalizeHeaders, OpraException, OpraSchema, OpraURL, SingletonGetQuery, SingletonResourceInfo, UnionType, wrapException } from '@opra/common';
|
|
2
2
|
import { OpraAdapter } from './adapter.js';
|
|
3
3
|
import { SingleRequestContext } from './request-contexts/single-request-context.js';
|
|
4
4
|
export class OpraHttpAdapter extends OpraAdapter {
|
|
@@ -258,9 +258,9 @@ export class OpraHttpAdapter extends OpraAdapter {
|
|
|
258
258
|
// protected async sendBatchResponse(executionContext: TExecutionContext, requestContext: BatchRequestContext) {
|
|
259
259
|
// const resp = executionContext.getResponse();
|
|
260
260
|
// resp.setStatus(HttpStatus.OK);
|
|
261
|
-
// resp.setHeader(
|
|
262
|
-
// resp.setHeader(
|
|
263
|
-
// resp.setHeader(
|
|
261
|
+
// resp.setHeader(HttpHeaderCodes.Cache_Control, 'no-cache');
|
|
262
|
+
// resp.setHeader(HttpHeaderCodes.Pragma, 'no-cache');
|
|
263
|
+
// resp.setHeader(HttpHeaderCodes.Expires, '-1');
|
|
264
264
|
// if (requestContext.headers) {
|
|
265
265
|
// for (const [k, v] of Object.entries(requestContext.headers)) {
|
|
266
266
|
// if (v)
|
|
@@ -268,8 +268,8 @@ export class OpraHttpAdapter extends OpraAdapter {
|
|
|
268
268
|
// }
|
|
269
269
|
// }
|
|
270
270
|
// const boundary = 'batch_' + uuid();
|
|
271
|
-
// resp.setHeader(
|
|
272
|
-
// resp.setHeader(
|
|
271
|
+
// resp.setHeader(HttpHeaderCodes.Content_Type, 'multipart/mixed;boundary=' + boundary);
|
|
272
|
+
// resp.setHeader(HttpHeaderCodes.X_Opra_Version, OpraSchema.Version);
|
|
273
273
|
//
|
|
274
274
|
// const bodyBuilder = new HttpMultipartData();
|
|
275
275
|
//
|
|
@@ -349,16 +349,16 @@ export class OpraHttpAdapter extends OpraAdapter {
|
|
|
349
349
|
const out = this.createOutput(requestContext);
|
|
350
350
|
const resp = executionContext.getResponse();
|
|
351
351
|
resp.setStatus(out.status);
|
|
352
|
-
resp.setHeader(
|
|
353
|
-
resp.setHeader(
|
|
354
|
-
resp.setHeader(
|
|
352
|
+
resp.setHeader(HttpHeaderCodes.Cache_Control, 'no-cache');
|
|
353
|
+
resp.setHeader(HttpHeaderCodes.Pragma, 'no-cache');
|
|
354
|
+
resp.setHeader(HttpHeaderCodes.Expires, '-1');
|
|
355
355
|
if (out.headers) {
|
|
356
356
|
for (const [k, v] of Object.entries(out.headers)) {
|
|
357
357
|
if (v)
|
|
358
358
|
resp.setHeader(k, v);
|
|
359
359
|
}
|
|
360
360
|
}
|
|
361
|
-
resp.setHeader(
|
|
361
|
+
resp.setHeader(HttpHeaderCodes.X_Opra_Version, OpraSchema.Version);
|
|
362
362
|
if (out.body)
|
|
363
363
|
resp.send(out.body);
|
|
364
364
|
resp.end();
|
|
@@ -376,10 +376,10 @@ export class OpraHttpAdapter extends OpraAdapter {
|
|
|
376
376
|
return b.status - a.status;
|
|
377
377
|
return i;
|
|
378
378
|
});
|
|
379
|
-
if (!status || status <
|
|
379
|
+
if (!status || status < HttpStatusCodes.BAD_REQUEST) {
|
|
380
380
|
status = errors[0].status;
|
|
381
|
-
if (status <
|
|
382
|
-
status =
|
|
381
|
+
if (status < HttpStatusCodes.BAD_REQUEST)
|
|
382
|
+
status = HttpStatusCodes.INTERNAL_SERVER_ERROR;
|
|
383
383
|
}
|
|
384
384
|
body = this.i18n.deep({
|
|
385
385
|
operation: ctx.query.method,
|
|
@@ -394,7 +394,7 @@ export class OpraHttpAdapter extends OpraAdapter {
|
|
|
394
394
|
body = JSON.stringify(body);
|
|
395
395
|
ctx.responseHeaders['content-type'] = 'application/json; charset=utf-8';
|
|
396
396
|
}
|
|
397
|
-
status = status || (query.operation === 'create' ?
|
|
397
|
+
status = status || (query.operation === 'create' ? HttpStatusCodes.CREATED : HttpStatusCodes.OK);
|
|
398
398
|
}
|
|
399
399
|
return {
|
|
400
400
|
status,
|
|
@@ -405,11 +405,11 @@ export class OpraHttpAdapter extends OpraAdapter {
|
|
|
405
405
|
async sendError(executionContext, error) {
|
|
406
406
|
const resp = executionContext.getResponse();
|
|
407
407
|
resp.setStatus(error.status || 500);
|
|
408
|
-
resp.setHeader(
|
|
409
|
-
resp.setHeader(
|
|
410
|
-
resp.setHeader(
|
|
411
|
-
resp.setHeader(
|
|
412
|
-
resp.setHeader(
|
|
408
|
+
resp.setHeader(HttpHeaderCodes.Content_Type, 'application/json');
|
|
409
|
+
resp.setHeader(HttpHeaderCodes.Cache_Control, 'no-cache');
|
|
410
|
+
resp.setHeader(HttpHeaderCodes.Pragma, 'no-cache');
|
|
411
|
+
resp.setHeader(HttpHeaderCodes.Expires, '-1');
|
|
412
|
+
resp.setHeader(HttpHeaderCodes.X_Opra_Version, OpraSchema.Version);
|
|
413
413
|
const issue = this.i18n.deep(error.issue);
|
|
414
414
|
const body = {
|
|
415
415
|
operation: 'unknown',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { RequestContext, RequestContextArgs } from './request-context.js';
|
|
2
2
|
import { SingleRequestContext } from './single-request-context.js';
|
|
3
|
-
export
|
|
3
|
+
export type BatchContextArgs = RequestContextArgs & Pick<BatchRequestContext, 'queries'>;
|
|
4
4
|
export declare class BatchRequestContext extends RequestContext {
|
|
5
5
|
readonly queries: SingleRequestContext[];
|
|
6
6
|
constructor(args: BatchContextArgs);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import { IncomingHttpHeaders } from 'http';
|
|
3
|
-
import {
|
|
3
|
+
import { HttpStatusCodes, OpraDocument, OpraException, OpraURLSearchParams } from '@opra/common';
|
|
4
4
|
import { ContextType, IExecutionContext, IHttpExecutionContext } from '../../interfaces/execution-context.interface.js';
|
|
5
|
-
export
|
|
5
|
+
export type RequestContextArgs = Pick<RequestContext, 'contentId' | 'service' | 'executionContext' | 'params' | 'headers' | 'parentValue' | 'continueOnError'>;
|
|
6
6
|
export declare class RequestContext {
|
|
7
7
|
readonly service: OpraDocument;
|
|
8
8
|
readonly executionContext: IExecutionContext;
|
|
@@ -14,7 +14,7 @@ export declare class RequestContext {
|
|
|
14
14
|
readonly responseHeaders: IncomingHttpHeaders;
|
|
15
15
|
response?: any;
|
|
16
16
|
errors: OpraException[];
|
|
17
|
-
status?:
|
|
17
|
+
status?: HttpStatusCodes;
|
|
18
18
|
continueOnError?: boolean;
|
|
19
19
|
constructor(args: RequestContextArgs);
|
|
20
20
|
get type(): ContextType;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { OpraQuery } from '@opra/common';
|
|
2
2
|
import { RequestContext, RequestContextArgs } from './request-context.js';
|
|
3
|
-
export
|
|
3
|
+
export type QueryRequestContextArgs = RequestContextArgs & {
|
|
4
4
|
readonly query: OpraQuery;
|
|
5
5
|
};
|
|
6
6
|
export declare class SingleRequestContext extends RequestContext {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export type IssueSeverity = 'error' | 'fatal' | 'warning' | 'info';
|