@opra/core 0.22.0 → 0.23.1
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/execution-context.host.js +48 -0
- package/cjs/adapter/http/express-adapter.host.js +24 -0
- package/cjs/adapter/http/express-adapter.js +12 -45
- package/cjs/adapter/http/helpers/concat-readable.js +20 -0
- package/cjs/adapter/http/helpers/multipart-helper.js +96 -0
- package/cjs/adapter/http/helpers/query-parsers.js +16 -0
- package/cjs/adapter/http/http-adapter-base.js +127 -0
- package/cjs/adapter/http/http-adapter.host.js +57 -0
- package/cjs/adapter/http/http-adapter.js +11 -133
- package/cjs/adapter/http/{impl/http-server-request.js → http-server-request.js} +11 -5
- package/cjs/adapter/http/{impl/http-server-response.js → http-server-response.js} +4 -4
- package/cjs/adapter/http/impl/http-incoming-message.host.js +148 -0
- package/cjs/adapter/http/impl/{http-outgoing-message-host.js → http-outgoing-message.host.js} +26 -38
- package/cjs/adapter/http/request-handlers/entity-request-handler.js +405 -0
- package/cjs/adapter/http/request-handlers/request-handler-base.js +27 -0
- package/cjs/adapter/http/request-handlers/storage-request-handler.js +134 -0
- package/cjs/adapter/operation-context.js +16 -0
- package/cjs/adapter/platform-adapter.host.js +107 -0
- package/cjs/adapter/request.host.js +1 -2
- package/cjs/adapter/request.js +2 -0
- package/cjs/adapter/response.js +2 -0
- package/cjs/adapter/services/logger.js +36 -0
- package/cjs/augmentation/collection.augmentation.js +2 -0
- package/cjs/augmentation/singleton.augmentation.js +2 -0
- package/cjs/augmentation/storage.augmentation.js +2 -0
- package/cjs/index.js +15 -9
- package/esm/adapter/execution-context.host.js +44 -0
- package/esm/adapter/http/express-adapter.host.js +20 -0
- package/esm/adapter/http/express-adapter.js +11 -20
- package/esm/adapter/http/helpers/concat-readable.js +16 -0
- package/esm/adapter/http/helpers/multipart-helper.js +91 -0
- package/esm/adapter/http/helpers/query-parsers.js +12 -0
- package/esm/adapter/http/http-adapter-base.js +123 -0
- package/esm/adapter/http/http-adapter.host.js +52 -0
- package/esm/adapter/http/http-adapter.js +11 -132
- package/esm/adapter/http/{impl/http-server-request.js → http-server-request.js} +12 -6
- package/esm/adapter/http/{impl/http-server-response.js → http-server-response.js} +4 -4
- package/esm/adapter/http/impl/http-incoming-message.host.js +144 -0
- package/esm/adapter/http/impl/{http-outgoing-message-host.js → http-outgoing-message.host.js} +25 -36
- package/esm/adapter/http/request-handlers/entity-request-handler.js +400 -0
- package/esm/adapter/http/request-handlers/request-handler-base.js +23 -0
- package/esm/adapter/http/request-handlers/storage-request-handler.js +129 -0
- package/esm/adapter/operation-context.js +13 -0
- package/esm/adapter/platform-adapter.host.js +102 -0
- package/esm/adapter/request.host.js +1 -2
- package/esm/adapter/request.js +1 -0
- package/esm/adapter/response.js +1 -0
- package/esm/adapter/services/logger.js +32 -0
- package/esm/augmentation/collection.augmentation.js +1 -0
- package/esm/augmentation/singleton.augmentation.js +1 -0
- package/esm/augmentation/storage.augmentation.js +1 -0
- package/esm/index.js +15 -9
- package/i18n/en/error.json +7 -2
- package/package.json +7 -6
- package/types/adapter/execution-context.d.ts +31 -0
- package/types/adapter/execution-context.host.d.ts +27 -0
- package/types/adapter/http/express-adapter.d.ts +12 -8
- package/types/adapter/http/express-adapter.host.d.ts +11 -0
- package/types/adapter/http/helpers/concat-readable.d.ts +3 -0
- package/types/adapter/http/helpers/multipart-helper.d.ts +25 -0
- package/types/adapter/http/helpers/query-parsers.d.ts +1 -0
- package/types/adapter/http/http-adapter-base.d.ts +23 -0
- package/types/adapter/http/http-adapter.d.ts +13 -29
- package/types/adapter/http/http-adapter.host.d.ts +18 -0
- package/types/adapter/http/{impl/http-server-request.d.ts → http-server-request.d.ts} +7 -6
- package/types/adapter/http/{impl/http-server-response.d.ts → http-server-response.d.ts} +2 -2
- package/types/adapter/http/impl/{http-incoming-message-host.d.ts → http-incoming-message.host.d.ts} +16 -12
- package/types/adapter/http/impl/{http-outgoing-message-host.d.ts → http-outgoing-message.host.d.ts} +12 -16
- package/types/adapter/http/request-handlers/entity-request-handler.d.ts +24 -0
- package/types/adapter/http/request-handlers/request-handler-base.d.ts +15 -0
- package/types/adapter/http/request-handlers/storage-request-handler.d.ts +23 -0
- package/types/adapter/interfaces/logger.interface.d.ts +7 -6
- package/types/adapter/interfaces/request-handler.interface.d.ts +4 -0
- package/types/adapter/operation-context.d.ts +11 -0
- package/types/adapter/{adapter.d.ts → platform-adapter.d.ts} +18 -28
- package/types/adapter/platform-adapter.host.d.ts +31 -0
- package/types/adapter/request.d.ts +11 -0
- package/types/adapter/request.host.d.ts +12 -21
- package/types/adapter/{interfaces/response.interface.d.ts → response.d.ts} +2 -2
- package/types/adapter/response.host.d.ts +2 -2
- package/types/adapter/services/logger.d.ts +14 -0
- package/types/augmentation/collection.augmentation.d.ts +112 -0
- package/types/augmentation/singleton.augmentation.d.ts +64 -0
- package/types/augmentation/storage.augmentation.d.ts +39 -0
- package/types/index.d.ts +15 -9
- package/cjs/adapter/adapter.js +0 -118
- package/cjs/adapter/http/impl/http-incoming-message-host.js +0 -127
- package/cjs/adapter/http/request-parsers/parse-collection-request.js +0 -165
- package/cjs/adapter/http/request-parsers/parse-request.js +0 -24
- package/cjs/adapter/http/request-parsers/parse-singleton-request.js +0 -96
- package/cjs/adapter/internal/metadata.resource.js +0 -26
- package/cjs/adapter/request-context.host.js +0 -44
- package/cjs/shared/collection-resource-base.js +0 -20
- package/esm/adapter/adapter.js +0 -113
- package/esm/adapter/http/impl/http-incoming-message-host.js +0 -122
- package/esm/adapter/http/request-parsers/parse-collection-request.js +0 -161
- package/esm/adapter/http/request-parsers/parse-request.js +0 -20
- package/esm/adapter/http/request-parsers/parse-singleton-request.js +0 -92
- package/esm/adapter/internal/metadata.resource.js +0 -23
- package/esm/adapter/request-context.host.js +0 -40
- package/esm/shared/collection-resource-base.js +0 -16
- package/types/adapter/http/request-parsers/parse-collection-request.d.ts +0 -4
- package/types/adapter/http/request-parsers/parse-request.d.ts +0 -4
- package/types/adapter/http/request-parsers/parse-singleton-request.d.ts +0 -4
- package/types/adapter/interfaces/request-context.interface.d.ts +0 -31
- package/types/adapter/interfaces/request.interface.d.ts +0 -15
- package/types/adapter/internal/metadata.resource.d.ts +0 -7
- package/types/adapter/request-context.host.d.ts +0 -22
- package/types/shared/collection-resource-base.d.ts +0 -11
- /package/cjs/adapter/{interfaces/request-context.interface.js → execution-context.js} +0 -0
- /package/cjs/adapter/http/{request-parsers/batch-request-parser.js → request-handlers/parse-batch-request.js} +0 -0
- /package/cjs/adapter/interfaces/{request.interface.js → request-handler.interface.js} +0 -0
- /package/cjs/adapter/{interfaces/response.interface.js → platform-adapter.js} +0 -0
- /package/esm/adapter/{interfaces/request-context.interface.js → execution-context.js} +0 -0
- /package/esm/adapter/http/{request-parsers/batch-request-parser.js → request-handlers/parse-batch-request.js} +0 -0
- /package/esm/adapter/interfaces/{request.interface.js → request-handler.interface.js} +0 -0
- /package/esm/adapter/{interfaces/response.interface.js → platform-adapter.js} +0 -0
- /package/types/adapter/http/{request-parsers/batch-request-parser.d.ts → request-handlers/parse-batch-request.d.ts} +0 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
This file contains code blocks from open source NodeJs project
|
|
4
|
+
https://github.com/nodejs/
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.HttpIncomingMessageHost = exports.kTrailersDistinct = exports.kTrailers = exports.kHeadersDistinct = exports.kHeaders = exports.CRLF = void 0;
|
|
8
|
+
const stream_1 = require("stream");
|
|
9
|
+
const http_parser_1 = require("@browsery/http-parser");
|
|
10
|
+
const common_1 = require("@opra/common");
|
|
11
|
+
const concat_readable_js_1 = require("../helpers/concat-readable.js");
|
|
12
|
+
const convert_to_headers_js_1 = require("../helpers/convert-to-headers.js");
|
|
13
|
+
const convert_to_raw_headers_js_1 = require("../helpers/convert-to-raw-headers.js");
|
|
14
|
+
exports.CRLF = Buffer.from('\r\n');
|
|
15
|
+
exports.kHeaders = Symbol.for('kHeaders');
|
|
16
|
+
exports.kHeadersDistinct = Symbol.for('kHeadersDistinct');
|
|
17
|
+
exports.kTrailers = Symbol.for('kTrailers');
|
|
18
|
+
exports.kTrailersDistinct = Symbol.for('kTrailersDistinct');
|
|
19
|
+
/**
|
|
20
|
+
*
|
|
21
|
+
* @class HttpIncomingMessageHost
|
|
22
|
+
*/
|
|
23
|
+
class HttpIncomingMessageHost extends stream_1.Duplex {
|
|
24
|
+
constructor(init) {
|
|
25
|
+
super();
|
|
26
|
+
this.rawHeaders = [];
|
|
27
|
+
this.rawTrailers = [];
|
|
28
|
+
this.complete = false;
|
|
29
|
+
this.joinDuplicateHeaders = false;
|
|
30
|
+
if (init) {
|
|
31
|
+
this.complete = true;
|
|
32
|
+
this.httpVersionMajor = init.httpVersionMajor || 1;
|
|
33
|
+
this.httpVersionMinor = init.httpVersionMinor || 0;
|
|
34
|
+
this.method = (init.method || 'GET').toUpperCase();
|
|
35
|
+
this.url = init.url || '';
|
|
36
|
+
if (init.body != null) {
|
|
37
|
+
if (Buffer.isBuffer(init.body))
|
|
38
|
+
this.body = init.body;
|
|
39
|
+
else if (typeof init.body === 'string')
|
|
40
|
+
this.body = Buffer.from(init.body, 'utf-8');
|
|
41
|
+
else
|
|
42
|
+
this.body = Buffer.from(JSON.stringify(init.body), 'utf-8');
|
|
43
|
+
}
|
|
44
|
+
if (init.headers)
|
|
45
|
+
this.rawHeaders = Array.isArray(init.headers) ? init.headers : (0, convert_to_raw_headers_js_1.convertToRawHeaders)(init.headers);
|
|
46
|
+
if (init.trailers)
|
|
47
|
+
this.rawTrailers = Array.isArray(init.trailers) ? init.trailers : (0, convert_to_raw_headers_js_1.convertToRawHeaders)(init.trailers);
|
|
48
|
+
this.ip = init.ip || '';
|
|
49
|
+
this.ips = init.ips || (this.ip ? [this.ip] : []);
|
|
50
|
+
if (this.body && !this.headers['content-length'])
|
|
51
|
+
this.headers['content-length'] = String(this.body.length);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
get httpVersion() {
|
|
55
|
+
return this.httpVersionMajor
|
|
56
|
+
? this.httpVersionMajor + '.' + this.httpVersionMinor
|
|
57
|
+
: '';
|
|
58
|
+
}
|
|
59
|
+
get headers() {
|
|
60
|
+
if (!this[exports.kHeaders])
|
|
61
|
+
this[exports.kHeaders] = (0, convert_to_headers_js_1.convertToHeaders)(this.rawHeaders, {}, this.joinDuplicateHeaders);
|
|
62
|
+
return this[exports.kHeaders];
|
|
63
|
+
}
|
|
64
|
+
set headers(headers) {
|
|
65
|
+
this[exports.kHeaders] = headers;
|
|
66
|
+
}
|
|
67
|
+
get headersDistinct() {
|
|
68
|
+
if (!this[exports.kHeadersDistinct])
|
|
69
|
+
this[exports.kHeadersDistinct] = (0, convert_to_headers_js_1.convertToHeadersDistinct)(this.rawHeaders, {});
|
|
70
|
+
return this[exports.kHeadersDistinct];
|
|
71
|
+
}
|
|
72
|
+
get trailers() {
|
|
73
|
+
if (!this[exports.kTrailers])
|
|
74
|
+
this[exports.kTrailers] = (0, convert_to_headers_js_1.convertToHeaders)(this.rawTrailers, {}, this.joinDuplicateHeaders);
|
|
75
|
+
return this[exports.kTrailers];
|
|
76
|
+
}
|
|
77
|
+
set trailers(trailers) {
|
|
78
|
+
this[exports.kTrailers] = trailers;
|
|
79
|
+
}
|
|
80
|
+
get trailersDistinct() {
|
|
81
|
+
if (!this[exports.kTrailersDistinct])
|
|
82
|
+
this[exports.kTrailersDistinct] = (0, convert_to_headers_js_1.convertToHeadersDistinct)(this.rawTrailers, {});
|
|
83
|
+
return this[exports.kTrailersDistinct];
|
|
84
|
+
}
|
|
85
|
+
_read(size) {
|
|
86
|
+
if (!this.body) {
|
|
87
|
+
this.push(null);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
if (!this._readStream) {
|
|
91
|
+
if ((0, common_1.isIterable)(this.body) || (0, common_1.isAsyncIterable)(this.body))
|
|
92
|
+
this._readStream = stream_1.Readable.from(this.body);
|
|
93
|
+
else if (typeof this.body === 'string') {
|
|
94
|
+
this._readStream = stream_1.Readable.from(Buffer.from(this.body, 'utf-8'));
|
|
95
|
+
}
|
|
96
|
+
else
|
|
97
|
+
this._readStream = stream_1.Readable.from(Buffer.from(JSON.stringify(this.body), 'utf-8'));
|
|
98
|
+
}
|
|
99
|
+
const chunk = this._readStream.read(size);
|
|
100
|
+
this.push(chunk);
|
|
101
|
+
// this.push(null);s
|
|
102
|
+
}
|
|
103
|
+
_write(chunk, encoding, callback) {
|
|
104
|
+
const error = this._httpParser?.execute(chunk);
|
|
105
|
+
if (error && typeof error === 'object')
|
|
106
|
+
callback(error);
|
|
107
|
+
else
|
|
108
|
+
callback();
|
|
109
|
+
}
|
|
110
|
+
static from(iterable) {
|
|
111
|
+
if (typeof iterable === 'object' && !((0, common_1.isIterable)(iterable) || (0, common_1.isAsyncIterable)(iterable)))
|
|
112
|
+
return new HttpIncomingMessageHost(iterable);
|
|
113
|
+
const msg = new HttpIncomingMessageHost();
|
|
114
|
+
const parser = msg._httpParser = new http_parser_1.HTTPParser(http_parser_1.HTTPParser.REQUEST);
|
|
115
|
+
let bodyChunks;
|
|
116
|
+
parser[http_parser_1.HTTPParser.kOnHeadersComplete] = (info) => {
|
|
117
|
+
msg.httpVersionMajor = info.versionMajor;
|
|
118
|
+
msg.httpVersionMinor = info.versionMinor;
|
|
119
|
+
msg.rawHeaders = info.headers;
|
|
120
|
+
msg.method = http_parser_1.HTTPParser.methods[info.method];
|
|
121
|
+
msg.url = info.url;
|
|
122
|
+
};
|
|
123
|
+
parser[http_parser_1.HTTPParser.kOnHeaders] = (trailers) => {
|
|
124
|
+
msg.rawTrailers = trailers;
|
|
125
|
+
};
|
|
126
|
+
parser[http_parser_1.HTTPParser.kOnBody] = (chunk, offset, length) => {
|
|
127
|
+
bodyChunks = bodyChunks || [];
|
|
128
|
+
bodyChunks.push(chunk.subarray(offset, offset + length));
|
|
129
|
+
};
|
|
130
|
+
parser[http_parser_1.HTTPParser.kOnMessageComplete] = () => {
|
|
131
|
+
msg.complete = true;
|
|
132
|
+
if (bodyChunks)
|
|
133
|
+
msg.body = Buffer.concat(bodyChunks);
|
|
134
|
+
};
|
|
135
|
+
const readable = (0, concat_readable_js_1.concatReadable)(stream_1.Readable.from(iterable), stream_1.Readable.from(exports.CRLF));
|
|
136
|
+
msg.once('finish', () => parser.finish());
|
|
137
|
+
readable.pipe(msg);
|
|
138
|
+
return msg;
|
|
139
|
+
}
|
|
140
|
+
static async fromAsync(iterable) {
|
|
141
|
+
return new Promise((resolve, reject) => {
|
|
142
|
+
const msg = this.from(iterable);
|
|
143
|
+
msg.once('finish', () => resolve(msg));
|
|
144
|
+
msg.once('error', (error) => reject(error));
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
exports.HttpIncomingMessageHost = HttpIncomingMessageHost;
|
package/cjs/adapter/http/impl/{http-outgoing-message-host.js → http-outgoing-message.host.js}
RENAMED
|
@@ -3,36 +3,42 @@
|
|
|
3
3
|
This file contains code blocks from open source NodeJs project
|
|
4
4
|
https://github.com/nodejs/
|
|
5
5
|
*/
|
|
6
|
-
var _a;
|
|
7
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
-
exports.HttpOutgoingMessageHost = exports.
|
|
9
|
-
const
|
|
10
|
-
const stream = tslib_1.__importStar(require("stream"));
|
|
7
|
+
exports.HttpOutgoingMessageHost = exports.kOutTrailers = exports.kOutHeaders = void 0;
|
|
8
|
+
const stream_1 = require("stream");
|
|
11
9
|
const common_js_1 = require("../helpers/common.js");
|
|
12
|
-
exports.kOutHeaders = Symbol('kOutHeaders');
|
|
13
|
-
exports.kOutTrailers = Symbol('kOutTrailers');
|
|
14
|
-
exports.kErrored = Symbol('kErrored');
|
|
10
|
+
exports.kOutHeaders = Symbol.for('kOutHeaders');
|
|
11
|
+
exports.kOutTrailers = Symbol.for('kOutTrailers');
|
|
15
12
|
// noinspection JSUnusedLocalSymbols
|
|
16
13
|
/**
|
|
17
14
|
*
|
|
18
15
|
* @class HttpOutgoingMessageHost
|
|
19
16
|
*/
|
|
20
|
-
class HttpOutgoingMessageHost {
|
|
21
|
-
constructor() {
|
|
17
|
+
class HttpOutgoingMessageHost extends stream_1.Duplex {
|
|
18
|
+
constructor(init) {
|
|
19
|
+
super();
|
|
22
20
|
this._headersSent = false;
|
|
23
|
-
this._closed = false;
|
|
24
|
-
this[_a] = null;
|
|
25
21
|
this.finished = false;
|
|
26
|
-
|
|
22
|
+
if (init) {
|
|
23
|
+
this.req = init.req;
|
|
24
|
+
this.statusCode = init?.statusCode || 0;
|
|
25
|
+
this.statusMessage = init?.statusMessage || '';
|
|
26
|
+
this.chunkedEncoding = !!init?.chunkedEncoding;
|
|
27
|
+
this.sendDate = !!init?.sendDate;
|
|
28
|
+
this.strictContentLength = !!init?.strictContentLength;
|
|
29
|
+
if (init.headers)
|
|
30
|
+
this.setHeaders(Array.isArray(init.headers) ? new Map([init.headers]) : init.headers);
|
|
31
|
+
this.body = init.body;
|
|
32
|
+
}
|
|
27
33
|
}
|
|
28
|
-
get
|
|
29
|
-
return this.
|
|
34
|
+
get httpVersionMajor() {
|
|
35
|
+
return this.req?.httpVersionMajor || this._httpVersionMajor;
|
|
30
36
|
}
|
|
31
|
-
get
|
|
32
|
-
return this.
|
|
37
|
+
get httpVersionMinor() {
|
|
38
|
+
return this.req?.httpVersionMinor || this._httpVersionMinor;
|
|
33
39
|
}
|
|
34
|
-
get
|
|
35
|
-
return this
|
|
40
|
+
get headersSent() {
|
|
41
|
+
return this._headersSent;
|
|
36
42
|
}
|
|
37
43
|
appendHeader(name, value) {
|
|
38
44
|
if (this.headersSent)
|
|
@@ -185,26 +191,8 @@ class HttpOutgoingMessageHost {
|
|
|
185
191
|
//
|
|
186
192
|
return this;
|
|
187
193
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
this.statusCode = init?.statusCode || 0;
|
|
191
|
-
this.statusMessage = init?.statusMessage || '';
|
|
192
|
-
this.chunkedEncoding = !!init?.chunkedEncoding;
|
|
193
|
-
this.sendDate = !!init?.sendDate;
|
|
194
|
-
this.strictContentLength = !!init?.strictContentLength;
|
|
195
|
-
if (init.headers) {
|
|
196
|
-
this.setHeaders(Array.isArray(init.headers) ? new Map([init.headers]) : init.headers);
|
|
197
|
-
}
|
|
198
|
-
this.body = init.body;
|
|
199
|
-
}
|
|
200
|
-
static create(init) {
|
|
201
|
-
const msg = new HttpOutgoingMessageHost();
|
|
202
|
-
if (init)
|
|
203
|
-
msg._readConfig(init);
|
|
204
|
-
return msg;
|
|
194
|
+
static from(init) {
|
|
195
|
+
return new HttpOutgoingMessageHost(init);
|
|
205
196
|
}
|
|
206
197
|
}
|
|
207
198
|
exports.HttpOutgoingMessageHost = HttpOutgoingMessageHost;
|
|
208
|
-
_a = exports.kErrored;
|
|
209
|
-
// Apply mixins
|
|
210
|
-
Object.assign(HttpOutgoingMessageHost.prototype, stream.Stream.prototype);
|
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EntityRequestHandler = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const body_parser_1 = tslib_1.__importDefault(require("body-parser"));
|
|
6
|
+
const putil_varhelpers_1 = require("putil-varhelpers");
|
|
7
|
+
const valgen = tslib_1.__importStar(require("valgen"));
|
|
8
|
+
const common_1 = require("@opra/common");
|
|
9
|
+
const operation_context_js_1 = require("../../operation-context.js");
|
|
10
|
+
const request_host_js_1 = require("../../request.host.js");
|
|
11
|
+
const response_host_js_1 = require("../../response.host.js");
|
|
12
|
+
const query_parsers_js_1 = require("../helpers/query-parsers.js");
|
|
13
|
+
const request_handler_base_js_1 = require("./request-handler-base.js");
|
|
14
|
+
/**
|
|
15
|
+
* @class EntityRequestHandler
|
|
16
|
+
*/
|
|
17
|
+
class EntityRequestHandler extends request_handler_base_js_1.RequestHandlerBase {
|
|
18
|
+
constructor(adapter) {
|
|
19
|
+
super(adapter);
|
|
20
|
+
this.adapter = adapter;
|
|
21
|
+
this.bodyLoaders = new WeakMap();
|
|
22
|
+
}
|
|
23
|
+
async processRequest(executionContext) {
|
|
24
|
+
const { incoming, outgoing } = executionContext.switchToHttp();
|
|
25
|
+
// Parse incoming message and create Request object
|
|
26
|
+
const request = await this.parseRequest(incoming);
|
|
27
|
+
if (!request)
|
|
28
|
+
return;
|
|
29
|
+
const response = new response_host_js_1.ResponseHost({ http: outgoing });
|
|
30
|
+
const context = operation_context_js_1.OperationContext.from(executionContext, request, response);
|
|
31
|
+
// Execute operation
|
|
32
|
+
await this.executeOperation(context);
|
|
33
|
+
if (response.errors.length) {
|
|
34
|
+
context.errors.push(...response.errors);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
await this.sendResponse(context);
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
if (e instanceof common_1.OpraException)
|
|
42
|
+
throw e;
|
|
43
|
+
if (e instanceof valgen.ValidationError) {
|
|
44
|
+
throw new common_1.InternalServerError({
|
|
45
|
+
message: (0, common_1.translate)('error:RESPONSE_VALIDATION,', 'Response validation failed'),
|
|
46
|
+
code: 'RESPONSE_VALIDATION',
|
|
47
|
+
details: e.issues
|
|
48
|
+
}, e);
|
|
49
|
+
}
|
|
50
|
+
throw new common_1.InternalServerError(e);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async parseRequest(incoming) {
|
|
54
|
+
const p = incoming.parsedUrl.path[0];
|
|
55
|
+
const resource = this.adapter.api.getResource(p.resource);
|
|
56
|
+
try {
|
|
57
|
+
if (resource instanceof common_1.Collection)
|
|
58
|
+
return await this.parseCollectionRequest(resource, incoming);
|
|
59
|
+
if (resource instanceof common_1.Singleton)
|
|
60
|
+
return await this.parseSingletonRequest(resource, incoming);
|
|
61
|
+
}
|
|
62
|
+
catch (e) {
|
|
63
|
+
if (e instanceof common_1.OpraException)
|
|
64
|
+
throw e;
|
|
65
|
+
if (e instanceof valgen.ValidationError) {
|
|
66
|
+
throw new common_1.BadRequestError({
|
|
67
|
+
message: (0, common_1.translate)('error:REQUEST_VALIDATION,', 'Request validation failed'),
|
|
68
|
+
code: 'REQUEST_VALIDATION',
|
|
69
|
+
details: e.issues
|
|
70
|
+
}, e);
|
|
71
|
+
}
|
|
72
|
+
throw new common_1.BadRequestError(e);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async executeOperation(context) {
|
|
76
|
+
const request = context.request;
|
|
77
|
+
const { response } = context;
|
|
78
|
+
const resource = request.resource;
|
|
79
|
+
// Call operation handler method
|
|
80
|
+
let value;
|
|
81
|
+
try {
|
|
82
|
+
value = await request.controller[request.operation].call(request.controller, context);
|
|
83
|
+
if (value == null)
|
|
84
|
+
value = response.value;
|
|
85
|
+
const { operation } = request;
|
|
86
|
+
if (operation === 'delete' || operation === 'deleteMany' || operation === 'updateMany') {
|
|
87
|
+
let affected = 0;
|
|
88
|
+
if (typeof value === 'number')
|
|
89
|
+
affected = value;
|
|
90
|
+
if (typeof value === 'boolean')
|
|
91
|
+
affected = value ? 1 : 0;
|
|
92
|
+
if (typeof value === 'object')
|
|
93
|
+
affected = value.affected || value.affectedRows ||
|
|
94
|
+
(operation === 'updateMany' ? value.updated : value.deleted);
|
|
95
|
+
response.value = affected;
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
// "get" and "update" operations must return the entity instance, otherwise it means resource not found
|
|
99
|
+
if (value == null && (request.operation === 'get' || request.operation === 'update'))
|
|
100
|
+
throw new common_1.ResourceNotFoundError(resource.name, request.key);
|
|
101
|
+
// "findMany" operation should return array of entity instances
|
|
102
|
+
if (request.operation === 'findMany')
|
|
103
|
+
value = value == null ? [] : Array.isArray(value) ? value : [value];
|
|
104
|
+
else
|
|
105
|
+
value = value == null ? {} : Array.isArray(value) ? value[0] : value;
|
|
106
|
+
response.value = value;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
response.errors.push(error);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
async sendResponse(context) {
|
|
114
|
+
const { request, response } = context;
|
|
115
|
+
const resource = request.resource;
|
|
116
|
+
const outgoing = response.switchToHttp();
|
|
117
|
+
let responseObject;
|
|
118
|
+
if (request.operation === 'delete' || request.operation === 'deleteMany' || request.operation === 'updateMany') {
|
|
119
|
+
responseObject = {
|
|
120
|
+
resource: resource.name,
|
|
121
|
+
operation: request.operation,
|
|
122
|
+
affected: response.value
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
if (!response.value)
|
|
127
|
+
throw new common_1.InternalServerError(`"${request.operation}" operation should return value`);
|
|
128
|
+
const encode = resource.getEncoder(request.operation);
|
|
129
|
+
const data = encode(response.value, { coerce: true });
|
|
130
|
+
if (request.operation === 'create')
|
|
131
|
+
outgoing.statusCode = 201;
|
|
132
|
+
responseObject = {
|
|
133
|
+
resource: resource.name,
|
|
134
|
+
operation: request.operation,
|
|
135
|
+
data
|
|
136
|
+
};
|
|
137
|
+
if (request.operation === 'create' || request.operation === 'update')
|
|
138
|
+
responseObject.affected = 1;
|
|
139
|
+
if (request.operation === 'findMany' && response.count != null && response.count >= 0)
|
|
140
|
+
responseObject.totalCount = response.count;
|
|
141
|
+
}
|
|
142
|
+
outgoing.statusCode = outgoing.statusCode || common_1.HttpStatusCodes.OK;
|
|
143
|
+
const body = this.adapter._i18n.deep(responseObject);
|
|
144
|
+
outgoing.setHeader(common_1.HttpHeaderCodes.Content_Type, 'application/json; charset=utf-8');
|
|
145
|
+
outgoing.send(JSON.stringify(body));
|
|
146
|
+
outgoing.end();
|
|
147
|
+
}
|
|
148
|
+
async parseCollectionRequest(resource, incoming) {
|
|
149
|
+
if ((incoming.method === 'POST' || incoming.method === 'PATCH') &&
|
|
150
|
+
incoming.headers['content-type'] !== 'application/json')
|
|
151
|
+
throw new common_1.BadRequestError({ message: 'Unsupported Content-Type' });
|
|
152
|
+
const contentId = incoming.headers['content-id'];
|
|
153
|
+
const p = incoming.parsedUrl.path[0];
|
|
154
|
+
const params = incoming.parsedUrl.searchParams;
|
|
155
|
+
switch (incoming.method) {
|
|
156
|
+
case 'POST': {
|
|
157
|
+
if (p.key == null) {
|
|
158
|
+
const operationMeta = await this.assertOperation(resource, 'create');
|
|
159
|
+
const jsonReader = this.getBodyLoader(operationMeta);
|
|
160
|
+
const decode = resource.getDecoder('create');
|
|
161
|
+
let data = await jsonReader(incoming);
|
|
162
|
+
data = decode(data, { coerce: true });
|
|
163
|
+
const pick = (0, query_parsers_js_1.parseArrayParam)(params.get('$pick'));
|
|
164
|
+
const omit = (0, query_parsers_js_1.parseArrayParam)(params.get('$omit'));
|
|
165
|
+
const include = (0, query_parsers_js_1.parseArrayParam)(params.get('$include'));
|
|
166
|
+
return new request_host_js_1.RequestHost({
|
|
167
|
+
controller: operationMeta.controller,
|
|
168
|
+
http: incoming,
|
|
169
|
+
contentId,
|
|
170
|
+
resource,
|
|
171
|
+
operation: 'create',
|
|
172
|
+
data,
|
|
173
|
+
params: {
|
|
174
|
+
pick: pick && resource.normalizeFieldPath(pick),
|
|
175
|
+
omit: omit && resource.normalizeFieldPath(omit),
|
|
176
|
+
include: include && resource.normalizeFieldPath(include)
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
case 'DELETE': {
|
|
183
|
+
if (p.key != null) {
|
|
184
|
+
const operationMeta = await this.assertOperation(resource, 'delete');
|
|
185
|
+
return new request_host_js_1.RequestHost({
|
|
186
|
+
controller: operationMeta.controller,
|
|
187
|
+
http: incoming,
|
|
188
|
+
contentId,
|
|
189
|
+
resource,
|
|
190
|
+
operation: 'delete',
|
|
191
|
+
key: resource.parseKeyValue(p.key)
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
const operationMeta = await this.assertOperation(resource, 'deleteMany');
|
|
195
|
+
const filter = resource.normalizeFilter(params.get('$filter'));
|
|
196
|
+
return new request_host_js_1.RequestHost({
|
|
197
|
+
controller: operationMeta.controller,
|
|
198
|
+
http: incoming,
|
|
199
|
+
contentId,
|
|
200
|
+
resource,
|
|
201
|
+
operation: 'deleteMany',
|
|
202
|
+
params: {
|
|
203
|
+
filter
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
case 'GET': {
|
|
208
|
+
const pick = (0, query_parsers_js_1.parseArrayParam)(params.get('$pick'));
|
|
209
|
+
const omit = (0, query_parsers_js_1.parseArrayParam)(params.get('$omit'));
|
|
210
|
+
const include = (0, query_parsers_js_1.parseArrayParam)(params.get('$include'));
|
|
211
|
+
if (p.key != null) {
|
|
212
|
+
const operationMeta = await this.assertOperation(resource, 'get');
|
|
213
|
+
return new request_host_js_1.RequestHost({
|
|
214
|
+
controller: operationMeta.controller,
|
|
215
|
+
http: incoming,
|
|
216
|
+
contentId,
|
|
217
|
+
resource,
|
|
218
|
+
operation: 'get',
|
|
219
|
+
key: resource.parseKeyValue(p.key),
|
|
220
|
+
params: {
|
|
221
|
+
pick: pick && resource.normalizeFieldPath(pick),
|
|
222
|
+
omit: omit && resource.normalizeFieldPath(omit),
|
|
223
|
+
include: include && resource.normalizeFieldPath(include)
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
const operationMeta = await this.assertOperation(resource, 'findMany');
|
|
228
|
+
const filter = resource.normalizeFilter(params.get('$filter'));
|
|
229
|
+
const sort = (0, query_parsers_js_1.parseArrayParam)(params.get('$sort'));
|
|
230
|
+
return new request_host_js_1.RequestHost({
|
|
231
|
+
controller: operationMeta.controller,
|
|
232
|
+
http: incoming,
|
|
233
|
+
contentId,
|
|
234
|
+
resource,
|
|
235
|
+
operation: 'findMany',
|
|
236
|
+
params: {
|
|
237
|
+
pick: pick && resource.normalizeFieldPath(pick),
|
|
238
|
+
omit: omit && resource.normalizeFieldPath(omit),
|
|
239
|
+
include: include && resource.normalizeFieldPath(include),
|
|
240
|
+
sort: sort && resource.normalizeSortFields(sort),
|
|
241
|
+
filter,
|
|
242
|
+
limit: (0, putil_varhelpers_1.toInt)(params.get('$limit')),
|
|
243
|
+
skip: (0, putil_varhelpers_1.toInt)(params.get('$skip')),
|
|
244
|
+
distinct: (0, putil_varhelpers_1.toBoolean)(params.get('$distinct')),
|
|
245
|
+
count: (0, putil_varhelpers_1.toBoolean)(params.get('$count')),
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
case 'PATCH': {
|
|
250
|
+
if (p.key != null) {
|
|
251
|
+
const operationMeta = await this.assertOperation(resource, 'update');
|
|
252
|
+
const jsonReader = this.getBodyLoader(operationMeta);
|
|
253
|
+
const decode = resource.getDecoder('update');
|
|
254
|
+
let data = await jsonReader(incoming);
|
|
255
|
+
data = decode(data, { coerce: true });
|
|
256
|
+
const pick = (0, query_parsers_js_1.parseArrayParam)(params.get('$pick'));
|
|
257
|
+
const omit = (0, query_parsers_js_1.parseArrayParam)(params.get('$omit'));
|
|
258
|
+
const include = (0, query_parsers_js_1.parseArrayParam)(params.get('$include'));
|
|
259
|
+
return new request_host_js_1.RequestHost({
|
|
260
|
+
controller: operationMeta.controller,
|
|
261
|
+
http: incoming,
|
|
262
|
+
contentId,
|
|
263
|
+
resource,
|
|
264
|
+
operation: 'update',
|
|
265
|
+
key: resource.parseKeyValue(p.key),
|
|
266
|
+
data,
|
|
267
|
+
params: {
|
|
268
|
+
pick: pick && resource.normalizeFieldPath(pick),
|
|
269
|
+
omit: omit && resource.normalizeFieldPath(omit),
|
|
270
|
+
include: include && resource.normalizeFieldPath(include),
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
const operationMeta = await this.assertOperation(resource, 'updateMany');
|
|
275
|
+
const jsonReader = this.getBodyLoader(operationMeta);
|
|
276
|
+
const decode = resource.getDecoder('updateMany');
|
|
277
|
+
let data = await jsonReader(incoming);
|
|
278
|
+
data = decode(data, { coerce: true });
|
|
279
|
+
const filter = resource.normalizeFilter(params.get('$filter'));
|
|
280
|
+
return new request_host_js_1.RequestHost({
|
|
281
|
+
controller: operationMeta.controller,
|
|
282
|
+
http: incoming,
|
|
283
|
+
contentId,
|
|
284
|
+
resource,
|
|
285
|
+
operation: 'updateMany',
|
|
286
|
+
data,
|
|
287
|
+
params: {
|
|
288
|
+
filter,
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
throw new common_1.MethodNotAllowedError({
|
|
294
|
+
message: `Collection resources do not accept http "${incoming.method}" method`
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
async parseSingletonRequest(resource, incoming) {
|
|
298
|
+
if ((incoming.method === 'POST' || incoming.method === 'PATCH') &&
|
|
299
|
+
incoming.headers['content-type'] !== 'application/json')
|
|
300
|
+
throw new common_1.BadRequestError({ message: 'Unsupported Content-Type' });
|
|
301
|
+
const contentId = incoming.headers['content-id'];
|
|
302
|
+
const params = incoming.parsedUrl.searchParams;
|
|
303
|
+
switch (incoming.method) {
|
|
304
|
+
case 'POST': {
|
|
305
|
+
const operationMeta = await this.assertOperation(resource, 'create');
|
|
306
|
+
const jsonReader = this.getBodyLoader(operationMeta);
|
|
307
|
+
const decode = resource.getDecoder('create');
|
|
308
|
+
let data = await jsonReader(incoming);
|
|
309
|
+
data = decode(data, { coerce: true });
|
|
310
|
+
const pick = (0, query_parsers_js_1.parseArrayParam)(params.get('$pick'));
|
|
311
|
+
const omit = (0, query_parsers_js_1.parseArrayParam)(params.get('$omit'));
|
|
312
|
+
const include = (0, query_parsers_js_1.parseArrayParam)(params.get('$include'));
|
|
313
|
+
return new request_host_js_1.RequestHost({
|
|
314
|
+
controller: operationMeta.controller,
|
|
315
|
+
http: incoming,
|
|
316
|
+
contentId,
|
|
317
|
+
resource,
|
|
318
|
+
operation: 'create',
|
|
319
|
+
data,
|
|
320
|
+
params: {
|
|
321
|
+
pick: pick && resource.normalizeFieldPath(pick),
|
|
322
|
+
omit: omit && resource.normalizeFieldPath(omit),
|
|
323
|
+
include: include && resource.normalizeFieldPath(include)
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
case 'DELETE': {
|
|
328
|
+
const operationMeta = await this.assertOperation(resource, 'delete');
|
|
329
|
+
return new request_host_js_1.RequestHost({
|
|
330
|
+
controller: operationMeta.controller,
|
|
331
|
+
http: incoming,
|
|
332
|
+
contentId,
|
|
333
|
+
resource,
|
|
334
|
+
operation: 'delete',
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
case 'GET': {
|
|
338
|
+
const operationMeta = await this.assertOperation(resource, 'get');
|
|
339
|
+
const pick = (0, query_parsers_js_1.parseArrayParam)(params.get('$pick'));
|
|
340
|
+
const omit = (0, query_parsers_js_1.parseArrayParam)(params.get('$omit'));
|
|
341
|
+
const include = (0, query_parsers_js_1.parseArrayParam)(params.get('$include'));
|
|
342
|
+
return new request_host_js_1.RequestHost({
|
|
343
|
+
controller: operationMeta.controller,
|
|
344
|
+
http: incoming,
|
|
345
|
+
contentId,
|
|
346
|
+
resource,
|
|
347
|
+
operation: 'get',
|
|
348
|
+
params: {
|
|
349
|
+
pick: pick && resource.normalizeFieldPath(pick),
|
|
350
|
+
omit: omit && resource.normalizeFieldPath(omit),
|
|
351
|
+
include: include && resource.normalizeFieldPath(include)
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
case 'PATCH': {
|
|
356
|
+
const operationMeta = await this.assertOperation(resource, 'update');
|
|
357
|
+
const jsonReader = this.getBodyLoader(operationMeta);
|
|
358
|
+
const decode = resource.getDecoder('update');
|
|
359
|
+
let data = await jsonReader(incoming);
|
|
360
|
+
data = decode(data, { coerce: true });
|
|
361
|
+
const pick = (0, query_parsers_js_1.parseArrayParam)(params.get('$pick'));
|
|
362
|
+
const omit = (0, query_parsers_js_1.parseArrayParam)(params.get('$omit'));
|
|
363
|
+
const include = (0, query_parsers_js_1.parseArrayParam)(params.get('$include'));
|
|
364
|
+
return new request_host_js_1.RequestHost({
|
|
365
|
+
controller: operationMeta.controller,
|
|
366
|
+
http: incoming,
|
|
367
|
+
contentId,
|
|
368
|
+
resource,
|
|
369
|
+
operation: 'update',
|
|
370
|
+
data,
|
|
371
|
+
params: {
|
|
372
|
+
pick: pick && resource.normalizeFieldPath(pick),
|
|
373
|
+
omit: omit && resource.normalizeFieldPath(omit),
|
|
374
|
+
include: include && resource.normalizeFieldPath(include),
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
throw new common_1.MethodNotAllowedError({
|
|
380
|
+
message: `Singleton resources do not accept http "${incoming.method}" method`
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
getBodyLoader(operation) {
|
|
384
|
+
let bodyLoader = this.bodyLoaders.get(operation);
|
|
385
|
+
if (!bodyLoader) {
|
|
386
|
+
const parser = body_parser_1.default.json({
|
|
387
|
+
limit: operation.input?.maxContentSize,
|
|
388
|
+
type: 'json'
|
|
389
|
+
});
|
|
390
|
+
bodyLoader = (incoming) => {
|
|
391
|
+
return new Promise((resolve, reject) => {
|
|
392
|
+
const next = (error) => {
|
|
393
|
+
if (error)
|
|
394
|
+
return reject(error);
|
|
395
|
+
resolve(incoming.body);
|
|
396
|
+
};
|
|
397
|
+
parser(incoming, {}, next);
|
|
398
|
+
});
|
|
399
|
+
};
|
|
400
|
+
this.bodyLoaders.set(operation, bodyLoader);
|
|
401
|
+
}
|
|
402
|
+
return bodyLoader;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
exports.EntityRequestHandler = EntityRequestHandler;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RequestHandlerBase = void 0;
|
|
4
|
+
const common_1 = require("@opra/common");
|
|
5
|
+
/**
|
|
6
|
+
* @class RequestHandlerBase
|
|
7
|
+
*/
|
|
8
|
+
class RequestHandlerBase {
|
|
9
|
+
constructor(adapter) {
|
|
10
|
+
this.adapter = adapter;
|
|
11
|
+
}
|
|
12
|
+
async assertOperation(resource, operation) {
|
|
13
|
+
const controller = await this.adapter.getController(resource);
|
|
14
|
+
const operationMeta = (typeof controller?.[operation] === 'function') && resource.operations[operation];
|
|
15
|
+
if (operationMeta)
|
|
16
|
+
return {
|
|
17
|
+
...operationMeta,
|
|
18
|
+
controller
|
|
19
|
+
};
|
|
20
|
+
throw new common_1.ForbiddenError({
|
|
21
|
+
message: (0, common_1.translate)('RESOLVER_FORBIDDEN', { resource: resource.name, operation }, `'{{resource}}' endpoint does not accept '{{operation}}' operations`),
|
|
22
|
+
severity: 'error',
|
|
23
|
+
code: 'RESOLVER_FORBIDDEN'
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.RequestHandlerBase = RequestHandlerBase;
|