@opra/core 0.20.1 → 0.21.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 +2 -2
- package/cjs/adapter/http/express-adapter.js +29 -7
- package/cjs/adapter/http/helpers/common.js +66 -0
- package/cjs/adapter/http/helpers/convert-to-headers.js +65 -0
- package/cjs/adapter/http/helpers/convert-to-raw-headers.js +25 -0
- package/cjs/adapter/http/helpers/match-known-fields.js +47 -0
- package/cjs/adapter/http/http-adapter.js +76 -438
- package/cjs/adapter/http/impl/http-incoming-message-host.js +127 -0
- package/cjs/adapter/http/impl/http-outgoing-message-host.js +210 -0
- package/cjs/adapter/http/impl/http-server-request.js +125 -0
- package/cjs/adapter/http/impl/http-server-response.js +226 -0
- package/cjs/adapter/http/request-parsers/batch-request-parser.js +169 -0
- package/cjs/adapter/http/request-parsers/parse-collection-request.js +165 -0
- package/cjs/adapter/http/request-parsers/parse-request.js +24 -0
- package/cjs/adapter/http/request-parsers/parse-singleton-request.js +96 -0
- package/cjs/adapter/request-context.host.js +17 -3
- package/cjs/adapter/request.host.js +6 -3
- package/cjs/adapter/response.host.js +5 -3
- package/cjs/index.js +4 -2
- package/esm/adapter/adapter.js +2 -2
- package/esm/adapter/http/express-adapter.js +6 -6
- package/esm/adapter/http/helpers/common.js +60 -0
- package/esm/adapter/http/helpers/convert-to-headers.js +60 -0
- package/esm/adapter/http/helpers/convert-to-raw-headers.js +21 -0
- package/esm/adapter/http/helpers/match-known-fields.js +43 -0
- package/esm/adapter/http/http-adapter.js +77 -439
- package/esm/adapter/http/impl/http-incoming-message-host.js +122 -0
- package/esm/adapter/http/impl/http-outgoing-message-host.js +205 -0
- package/esm/adapter/http/impl/http-server-request.js +121 -0
- package/esm/adapter/http/impl/http-server-response.js +222 -0
- package/esm/adapter/http/request-parsers/batch-request-parser.js +169 -0
- package/esm/adapter/http/request-parsers/parse-collection-request.js +161 -0
- package/esm/adapter/http/request-parsers/parse-request.js +20 -0
- package/esm/adapter/http/request-parsers/parse-singleton-request.js +92 -0
- package/esm/adapter/request-context.host.js +17 -3
- package/esm/adapter/request.host.js +6 -3
- package/esm/adapter/response.host.js +5 -3
- package/esm/index.js +4 -2
- package/package.json +20 -16
- package/types/adapter/adapter.d.ts +1 -1
- package/types/adapter/http/helpers/common.d.ts +17 -0
- package/types/adapter/http/helpers/convert-to-headers.d.ts +2 -0
- package/types/adapter/http/helpers/convert-to-raw-headers.d.ts +3 -0
- package/types/adapter/http/helpers/match-known-fields.d.ts +6 -0
- package/types/adapter/http/http-adapter.d.ts +7 -12
- package/types/adapter/http/impl/http-incoming-message-host.d.ts +58 -0
- package/types/adapter/http/impl/http-outgoing-message-host.d.ts +72 -0
- package/types/adapter/http/{http-request-message.d.ts → impl/http-server-request.d.ts} +52 -85
- package/types/adapter/http/impl/http-server-response.d.ts +137 -0
- package/types/adapter/http/request-parsers/batch-request-parser.d.ts +0 -0
- package/types/adapter/http/request-parsers/parse-collection-request.d.ts +4 -0
- package/types/adapter/http/request-parsers/parse-request.d.ts +4 -0
- package/types/adapter/http/request-parsers/parse-singleton-request.d.ts +4 -0
- package/types/adapter/interfaces/request-context.interface.d.ts +14 -10
- package/types/adapter/interfaces/request.interface.d.ts +3 -2
- package/types/adapter/interfaces/response.interface.d.ts +2 -2
- package/types/adapter/request-context.host.d.ts +9 -6
- package/types/adapter/request.host.d.ts +8 -4
- package/types/adapter/response.host.d.ts +6 -4
- package/types/index.d.ts +4 -2
- package/cjs/adapter/http/http-message.host.js +0 -251
- package/cjs/adapter/http/http-request-context.host.js +0 -28
- package/cjs/adapter/http/http-request-message.js +0 -152
- package/cjs/adapter/http/http-request.host.js +0 -14
- package/cjs/adapter/http/http-response-message.js +0 -238
- package/cjs/adapter/http/http-response.host.js +0 -14
- package/esm/adapter/http/http-message.host.js +0 -246
- package/esm/adapter/http/http-request-context.host.js +0 -24
- package/esm/adapter/http/http-request-message.js +0 -148
- package/esm/adapter/http/http-request.host.js +0 -10
- package/esm/adapter/http/http-response-message.js +0 -233
- package/esm/adapter/http/http-response.host.js +0 -10
- package/types/adapter/http/http-message.host.d.ts +0 -122
- package/types/adapter/http/http-request-context.host.d.ts +0 -16
- package/types/adapter/http/http-request.host.d.ts +0 -7
- package/types/adapter/http/http-response-message.d.ts +0 -321
- package/types/adapter/http/http-response.host.d.ts +0 -7
|
@@ -4,9 +4,9 @@ exports.OpraHttpAdapter = void 0;
|
|
|
4
4
|
const power_tasks_1 = require("power-tasks");
|
|
5
5
|
const common_1 = require("@opra/common");
|
|
6
6
|
const adapter_js_1 = require("../adapter.js");
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
7
|
+
const request_context_host_js_1 = require("../request-context.host.js");
|
|
8
|
+
const response_host_js_1 = require("../response.host.js");
|
|
9
|
+
const parse_request_js_1 = require("./request-parsers/parse-request.js");
|
|
10
10
|
/**
|
|
11
11
|
*
|
|
12
12
|
* @class OpraHttpAdapter
|
|
@@ -20,43 +20,79 @@ class OpraHttpAdapter extends adapter_js_1.OpraAdapter {
|
|
|
20
20
|
*/
|
|
21
21
|
async handler(incoming, outgoing) {
|
|
22
22
|
try {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
try {
|
|
24
|
+
const request = await (0, parse_request_js_1.parseRequest)(this._apiRoot, incoming);
|
|
25
|
+
const task = this.createRequestTask(request, outgoing);
|
|
26
|
+
await task.toPromise();
|
|
26
27
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const task = new power_tasks_1.Task(async () => {
|
|
32
|
-
try {
|
|
33
|
-
await this.executeRequest(context);
|
|
34
|
-
if (request.operation === 'findMany' && request.args.count && response.count != null) {
|
|
35
|
-
response.switchToHttp().header(common_1.HttpHeaderCodes.X_Opra_Total_Matches, String(response.count));
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
catch (error) {
|
|
39
|
-
return this.errorHandler(incoming, outgoing, [error]);
|
|
40
|
-
}
|
|
41
|
-
await this.sendResponse(context);
|
|
42
|
-
}, {
|
|
43
|
-
id: incoming.get('content-id'),
|
|
44
|
-
exclusive: request.crud !== 'read'
|
|
45
|
-
});
|
|
46
|
-
await task.toPromise().catch(e => {
|
|
47
|
-
this.logger?.error?.(e);
|
|
48
|
-
outgoing.sendStatus(500);
|
|
49
|
-
});
|
|
50
|
-
await this.emitAsync('request-finish', context);
|
|
51
|
-
return;
|
|
28
|
+
catch (e) {
|
|
29
|
+
if (e instanceof common_1.OpraException)
|
|
30
|
+
throw e;
|
|
31
|
+
throw new common_1.BadRequestError(e);
|
|
52
32
|
}
|
|
53
|
-
throw new common_1.BadRequestError({ message: 'Unsupported Content-Type' });
|
|
54
33
|
}
|
|
55
34
|
catch (error) {
|
|
56
|
-
await this.
|
|
35
|
+
await this.handleError(incoming, outgoing, [error]);
|
|
57
36
|
}
|
|
58
37
|
}
|
|
59
|
-
|
|
38
|
+
createRequestTask(request, outgoing) {
|
|
39
|
+
return new power_tasks_1.Task(async () => {
|
|
40
|
+
const response = new response_host_js_1.ResponseHost({ http: outgoing });
|
|
41
|
+
const context = new request_context_host_js_1.RequestContextHost('http', this.platform, this.api, request, response);
|
|
42
|
+
return this.executeRequest(context);
|
|
43
|
+
}, {
|
|
44
|
+
id: request.contentId,
|
|
45
|
+
exclusive: request.crud !== 'read'
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
async executeRequest(context) {
|
|
49
|
+
const { request, response } = context;
|
|
50
|
+
await super.executeRequest(context)
|
|
51
|
+
.catch(e => {
|
|
52
|
+
response.errors = response.errors || [];
|
|
53
|
+
response.errors.push(e);
|
|
54
|
+
});
|
|
55
|
+
const outgoing = response.switchToHttp();
|
|
56
|
+
if (response.errors?.length) {
|
|
57
|
+
const errors = response.errors.map(e => (0, common_1.wrapException)(e));
|
|
58
|
+
await this.handleError(request.switchToHttp(), outgoing, errors);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (request.resource instanceof common_1.Singleton || request.resource instanceof common_1.Collection) {
|
|
62
|
+
outgoing.setHeader(common_1.HttpHeaderCodes.X_Opra_Data_Type, String(request.resource.type.name));
|
|
63
|
+
outgoing.setHeader(common_1.HttpHeaderCodes.X_Opra_Operation, request.operation);
|
|
64
|
+
}
|
|
65
|
+
if (request.crud === 'create') {
|
|
66
|
+
if (!response.value)
|
|
67
|
+
throw new common_1.InternalServerError();
|
|
68
|
+
// todo validate
|
|
69
|
+
outgoing.statusCode = 201;
|
|
70
|
+
}
|
|
71
|
+
if (request.resource instanceof common_1.Collection &&
|
|
72
|
+
request.crud === 'read' && request.many && request.args.count >= 0) {
|
|
73
|
+
outgoing.setHeader(common_1.HttpHeaderCodes.X_Opra_Total_Matches, String(response.count));
|
|
74
|
+
}
|
|
75
|
+
outgoing.statusCode = outgoing.statusCode || common_1.HttpStatusCodes.OK;
|
|
76
|
+
outgoing.setHeader(common_1.HttpHeaderCodes.Cache_Control, 'no-cache');
|
|
77
|
+
outgoing.setHeader(common_1.HttpHeaderCodes.Pragma, 'no-cache');
|
|
78
|
+
outgoing.setHeader(common_1.HttpHeaderCodes.Expires, '-1');
|
|
79
|
+
outgoing.setHeader(common_1.HttpHeaderCodes.X_Opra_Version, common_1.OpraSchema.SpecVersion);
|
|
80
|
+
if (response.value) {
|
|
81
|
+
if (typeof response.value === 'object') {
|
|
82
|
+
if ((0, common_1.isReadable)(response.value) || Buffer.isBuffer(response.value))
|
|
83
|
+
outgoing.send(response.value);
|
|
84
|
+
else {
|
|
85
|
+
const body = this.i18n.deep(response.value);
|
|
86
|
+
outgoing.setHeader(common_1.HttpHeaderCodes.Content_Type, 'application/json; charset=utf-8');
|
|
87
|
+
outgoing.send(JSON.stringify(body));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
else
|
|
91
|
+
outgoing.send(JSON.stringify(response.value));
|
|
92
|
+
}
|
|
93
|
+
outgoing.end();
|
|
94
|
+
}
|
|
95
|
+
async handleError(incoming, outgoing, errors) {
|
|
60
96
|
errors.forEach(e => {
|
|
61
97
|
this.log((e instanceof common_1.OpraException) ? 'error' : 'fatal', incoming, e); // todo. implement a better logger
|
|
62
98
|
});
|
|
@@ -77,12 +113,12 @@ class OpraHttpAdapter extends adapter_js_1.OpraAdapter {
|
|
|
77
113
|
const body = this.i18n.deep({
|
|
78
114
|
errors: errors.map(e => e.issue)
|
|
79
115
|
});
|
|
80
|
-
outgoing.
|
|
81
|
-
outgoing.
|
|
82
|
-
outgoing.
|
|
83
|
-
outgoing.
|
|
84
|
-
outgoing.
|
|
85
|
-
outgoing.
|
|
116
|
+
outgoing.statusCode = status;
|
|
117
|
+
outgoing.setHeader(common_1.HttpHeaderCodes.Content_Type, 'application/json; charset=utf-8');
|
|
118
|
+
outgoing.setHeader(common_1.HttpHeaderCodes.Cache_Control, 'no-cache');
|
|
119
|
+
outgoing.setHeader(common_1.HttpHeaderCodes.Pragma, 'no-cache');
|
|
120
|
+
outgoing.setHeader(common_1.HttpHeaderCodes.Expires, '-1');
|
|
121
|
+
outgoing.setHeader(common_1.HttpHeaderCodes.X_Opra_Version, common_1.OpraSchema.SpecVersion);
|
|
86
122
|
outgoing.send(JSON.stringify(body));
|
|
87
123
|
outgoing.end();
|
|
88
124
|
}
|
|
@@ -94,403 +130,5 @@ class OpraHttpAdapter extends adapter_js_1.OpraAdapter {
|
|
|
94
130
|
return;
|
|
95
131
|
logFn.apply(this.logger, [String(message), ...optionalParams]);
|
|
96
132
|
}
|
|
97
|
-
async executeRequest(context) {
|
|
98
|
-
await super.executeRequest(context);
|
|
99
|
-
const { request } = context;
|
|
100
|
-
const response = context.response;
|
|
101
|
-
const { crud } = request;
|
|
102
|
-
const httpResponse = response.switchToHttp();
|
|
103
|
-
if (request.resource instanceof common_1.Singleton || request.resource instanceof common_1.Collection) {
|
|
104
|
-
httpResponse.set(common_1.HttpHeaderCodes.X_Opra_Data_Type, request.resource.type.name);
|
|
105
|
-
httpResponse.set(common_1.HttpHeaderCodes.X_Opra_Operation, request.operation);
|
|
106
|
-
}
|
|
107
|
-
if (crud === 'create') {
|
|
108
|
-
if (!response.value)
|
|
109
|
-
throw new common_1.InternalServerError();
|
|
110
|
-
// todo validate
|
|
111
|
-
httpResponse.status(201);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
*
|
|
116
|
-
* @param incoming
|
|
117
|
-
* @protected
|
|
118
|
-
*/
|
|
119
|
-
async parseRequest(incoming) {
|
|
120
|
-
try {
|
|
121
|
-
const url = new common_1.OpraURL();
|
|
122
|
-
url.searchParams.define({
|
|
123
|
-
'$search': { codec: 'string' },
|
|
124
|
-
'$pick': { codec: 'string', array: 'strict' },
|
|
125
|
-
'$omit': { codec: 'string', array: 'strict' },
|
|
126
|
-
'$include': { codec: 'string', array: 'strict' },
|
|
127
|
-
'$sort': { codec: 'string', array: 'strict' },
|
|
128
|
-
'$filter': { codec: 'filter' },
|
|
129
|
-
'$limit': { codec: 'number' },
|
|
130
|
-
'$skip': { codec: 'number' },
|
|
131
|
-
'$distinct': { codec: 'boolean' },
|
|
132
|
-
'$count': { codec: 'boolean' },
|
|
133
|
-
});
|
|
134
|
-
url.parse(incoming.url);
|
|
135
|
-
// const {context, url, method, headers, body, contentId} = args;
|
|
136
|
-
if (!url.path.size)
|
|
137
|
-
throw new common_1.BadRequestError();
|
|
138
|
-
const method = incoming.method;
|
|
139
|
-
if (method !== 'GET' && url.path.size > 1)
|
|
140
|
-
throw new common_1.BadRequestError();
|
|
141
|
-
// const pathLen = url.path.size;
|
|
142
|
-
// let pathIndex = 0;
|
|
143
|
-
const params = url.searchParams;
|
|
144
|
-
const p = url.path.get(0);
|
|
145
|
-
const resource = this._internalDoc.getResource(p.resource);
|
|
146
|
-
// let container: IResourceContainer | undefined;
|
|
147
|
-
// while (resource && resource instanceof ContainerResourceInfo) {
|
|
148
|
-
// container = resource;
|
|
149
|
-
// p = url.path.get(++pathIndex);
|
|
150
|
-
// resource = container.getResource(p.resource);
|
|
151
|
-
// }
|
|
152
|
-
// const headers = incoming.headers;
|
|
153
|
-
/*
|
|
154
|
-
* Collection
|
|
155
|
-
*/
|
|
156
|
-
if (resource instanceof common_1.Collection) {
|
|
157
|
-
switch (method) {
|
|
158
|
-
case 'POST': {
|
|
159
|
-
if (!p.key) {
|
|
160
|
-
const pick = params.get('$pick');
|
|
161
|
-
const omit = params.get('$omit');
|
|
162
|
-
const include = params.get('$include');
|
|
163
|
-
return new http_request_host_js_1.HttpRequestHost({
|
|
164
|
-
kind: 'CollectionCreateRequest',
|
|
165
|
-
resource,
|
|
166
|
-
operation: 'create',
|
|
167
|
-
crud: 'create',
|
|
168
|
-
many: false,
|
|
169
|
-
args: {
|
|
170
|
-
data: incoming.body,
|
|
171
|
-
pick: pick && resource.normalizeFieldPath(pick),
|
|
172
|
-
omit: omit && resource.normalizeFieldPath(omit),
|
|
173
|
-
include: include && resource.normalizeFieldPath(include)
|
|
174
|
-
}
|
|
175
|
-
}, incoming);
|
|
176
|
-
}
|
|
177
|
-
break;
|
|
178
|
-
}
|
|
179
|
-
case 'DELETE': {
|
|
180
|
-
if (p.key) {
|
|
181
|
-
return new http_request_host_js_1.HttpRequestHost({
|
|
182
|
-
kind: 'CollectionDeleteRequest',
|
|
183
|
-
resource,
|
|
184
|
-
operation: 'delete',
|
|
185
|
-
crud: 'delete',
|
|
186
|
-
many: false,
|
|
187
|
-
args: {
|
|
188
|
-
key: resource.parseKeyValue(p.key)
|
|
189
|
-
}
|
|
190
|
-
}, incoming);
|
|
191
|
-
}
|
|
192
|
-
const filter = resource.normalizeFilter(params.get('$filter'));
|
|
193
|
-
return new http_request_host_js_1.HttpRequestHost({
|
|
194
|
-
kind: 'CollectionDeleteManyRequest',
|
|
195
|
-
resource,
|
|
196
|
-
operation: 'deleteMany',
|
|
197
|
-
crud: 'delete',
|
|
198
|
-
many: true,
|
|
199
|
-
args: {
|
|
200
|
-
filter
|
|
201
|
-
}
|
|
202
|
-
}, incoming);
|
|
203
|
-
}
|
|
204
|
-
case 'GET': {
|
|
205
|
-
const pick = params.get('$pick');
|
|
206
|
-
const omit = params.get('$omit');
|
|
207
|
-
const include = params.get('$include');
|
|
208
|
-
if (p.key) {
|
|
209
|
-
return new http_request_host_js_1.HttpRequestHost({
|
|
210
|
-
kind: 'CollectionGetRequest',
|
|
211
|
-
resource,
|
|
212
|
-
operation: 'get',
|
|
213
|
-
crud: 'read',
|
|
214
|
-
many: false,
|
|
215
|
-
args: {
|
|
216
|
-
key: resource.parseKeyValue(p.key),
|
|
217
|
-
pick: pick && resource.normalizeFieldPath(pick),
|
|
218
|
-
omit: omit && resource.normalizeFieldPath(omit),
|
|
219
|
-
include: include && resource.normalizeFieldPath(include)
|
|
220
|
-
}
|
|
221
|
-
}, incoming);
|
|
222
|
-
}
|
|
223
|
-
const filter = resource.normalizeFilter(params.get('$filter'));
|
|
224
|
-
const sort = params.get('$sort');
|
|
225
|
-
return new http_request_host_js_1.HttpRequestHost({
|
|
226
|
-
kind: 'CollectionFindManyRequest',
|
|
227
|
-
resource,
|
|
228
|
-
operation: 'findMany',
|
|
229
|
-
crud: 'read',
|
|
230
|
-
many: true,
|
|
231
|
-
args: {
|
|
232
|
-
pick: pick && resource.normalizeFieldPath(pick),
|
|
233
|
-
omit: omit && resource.normalizeFieldPath(omit),
|
|
234
|
-
include: include && resource.normalizeFieldPath(include),
|
|
235
|
-
sort: sort && resource.normalizeSortFields(sort),
|
|
236
|
-
filter,
|
|
237
|
-
limit: params.get('$limit'),
|
|
238
|
-
skip: params.get('$skip'),
|
|
239
|
-
distinct: params.get('$distinct'),
|
|
240
|
-
count: params.get('$count'),
|
|
241
|
-
}
|
|
242
|
-
}, incoming);
|
|
243
|
-
}
|
|
244
|
-
case 'PATCH': {
|
|
245
|
-
if (p.key) {
|
|
246
|
-
const pick = params.get('$pick');
|
|
247
|
-
const omit = params.get('$omit');
|
|
248
|
-
const include = params.get('$include');
|
|
249
|
-
return new http_request_host_js_1.HttpRequestHost({
|
|
250
|
-
kind: 'CollectionUpdateRequest',
|
|
251
|
-
resource,
|
|
252
|
-
operation: 'update',
|
|
253
|
-
crud: 'update',
|
|
254
|
-
many: false,
|
|
255
|
-
args: {
|
|
256
|
-
key: resource.parseKeyValue(p.key),
|
|
257
|
-
data: incoming.body,
|
|
258
|
-
pick: pick && resource.normalizeFieldPath(pick),
|
|
259
|
-
omit: omit && resource.normalizeFieldPath(omit),
|
|
260
|
-
include: include && resource.normalizeFieldPath(include),
|
|
261
|
-
}
|
|
262
|
-
}, incoming);
|
|
263
|
-
}
|
|
264
|
-
const filter = resource.normalizeFilter(params.get('$filter'));
|
|
265
|
-
return new http_request_host_js_1.HttpRequestHost({
|
|
266
|
-
kind: 'CollectionUpdateManyRequest',
|
|
267
|
-
resource,
|
|
268
|
-
operation: 'updateMany',
|
|
269
|
-
crud: 'update',
|
|
270
|
-
many: true,
|
|
271
|
-
args: {
|
|
272
|
-
data: incoming.body,
|
|
273
|
-
filter,
|
|
274
|
-
}
|
|
275
|
-
}, incoming);
|
|
276
|
-
}
|
|
277
|
-
default:
|
|
278
|
-
throw new common_1.BadRequestError();
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
else
|
|
282
|
-
/*
|
|
283
|
-
* Singleton
|
|
284
|
-
*/
|
|
285
|
-
if (resource instanceof common_1.Singleton) {
|
|
286
|
-
if (p.key)
|
|
287
|
-
throw new common_1.BadRequestError();
|
|
288
|
-
switch (method) {
|
|
289
|
-
case 'POST': {
|
|
290
|
-
const pick = params.get('$pick');
|
|
291
|
-
const omit = params.get('$omit');
|
|
292
|
-
const include = params.get('$include');
|
|
293
|
-
return new http_request_host_js_1.HttpRequestHost({
|
|
294
|
-
kind: 'SingletonCreateRequest',
|
|
295
|
-
resource,
|
|
296
|
-
operation: 'create',
|
|
297
|
-
crud: 'create',
|
|
298
|
-
many: false,
|
|
299
|
-
args: {
|
|
300
|
-
data: incoming.body,
|
|
301
|
-
pick: pick && resource.normalizeFieldPath(pick),
|
|
302
|
-
omit: omit && resource.normalizeFieldPath(omit),
|
|
303
|
-
include: include && resource.normalizeFieldPath(include),
|
|
304
|
-
}
|
|
305
|
-
}, incoming);
|
|
306
|
-
}
|
|
307
|
-
case 'DELETE': {
|
|
308
|
-
return new http_request_host_js_1.HttpRequestHost({
|
|
309
|
-
kind: 'SingletonDeleteRequest',
|
|
310
|
-
resource,
|
|
311
|
-
operation: 'delete',
|
|
312
|
-
crud: 'delete',
|
|
313
|
-
many: false,
|
|
314
|
-
args: {}
|
|
315
|
-
}, incoming);
|
|
316
|
-
}
|
|
317
|
-
case 'GET': {
|
|
318
|
-
const pick = params.get('$pick');
|
|
319
|
-
const omit = params.get('$omit');
|
|
320
|
-
const include = params.get('$include');
|
|
321
|
-
return new http_request_host_js_1.HttpRequestHost({
|
|
322
|
-
kind: 'SingletonGetRequest',
|
|
323
|
-
resource,
|
|
324
|
-
operation: 'get',
|
|
325
|
-
crud: 'read',
|
|
326
|
-
many: false,
|
|
327
|
-
args: {
|
|
328
|
-
pick: pick && resource.normalizeFieldPath(pick),
|
|
329
|
-
omit: omit && resource.normalizeFieldPath(omit),
|
|
330
|
-
include: include && resource.normalizeFieldPath(include),
|
|
331
|
-
}
|
|
332
|
-
}, incoming);
|
|
333
|
-
}
|
|
334
|
-
case 'PATCH': {
|
|
335
|
-
const pick = params.get('$pick');
|
|
336
|
-
const omit = params.get('$omit');
|
|
337
|
-
const include = params.get('$include');
|
|
338
|
-
return new http_request_host_js_1.HttpRequestHost({
|
|
339
|
-
kind: 'SingletonUpdateRequest',
|
|
340
|
-
resource,
|
|
341
|
-
operation: 'update',
|
|
342
|
-
crud: 'update',
|
|
343
|
-
many: false,
|
|
344
|
-
args: {
|
|
345
|
-
data: incoming.body,
|
|
346
|
-
pick: pick && resource.normalizeFieldPath(pick),
|
|
347
|
-
omit: omit && resource.normalizeFieldPath(omit),
|
|
348
|
-
include: include && resource.normalizeFieldPath(include),
|
|
349
|
-
}
|
|
350
|
-
}, incoming);
|
|
351
|
-
}
|
|
352
|
-
default:
|
|
353
|
-
throw new common_1.BadRequestError();
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
else
|
|
357
|
-
throw new common_1.InternalServerError();
|
|
358
|
-
// if (query instanceof SingletonGetQuery || query instanceof CollectionGetQuery || query instanceof ElementReadQuery) {
|
|
359
|
-
// // Move through properties
|
|
360
|
-
// let parentType: DataType;
|
|
361
|
-
// const curPath: string[] = [];
|
|
362
|
-
// let parent: SingletonGetQuery | CollectionGetQuery | ElementReadQuery = query;
|
|
363
|
-
// while (++pathIndex < pathLen) {
|
|
364
|
-
// p = url.path.get(pathIndex);
|
|
365
|
-
// parentType = parent.type;
|
|
366
|
-
// if (parent.type instanceof UnionType) {
|
|
367
|
-
// if (parent.type.name === 'any')
|
|
368
|
-
// parentType = this.document.getComplexType('object');
|
|
369
|
-
// else
|
|
370
|
-
// throw new TypeError(`"${resource.name}.${curPath.join()}" is a UnionType and needs type casting.`);
|
|
371
|
-
// }
|
|
372
|
-
// if (!(parentType instanceof ComplexType))
|
|
373
|
-
// throw new TypeError(`"${resource.name}.${curPath.join()}" is not a ComplexType and has no fields.`);
|
|
374
|
-
// curPath.push(p.resource);
|
|
375
|
-
// parent.child = new ElementReadQuery(parent, p.resource, {castingType: parentType});
|
|
376
|
-
// parent = parent.child;
|
|
377
|
-
// }
|
|
378
|
-
// }
|
|
379
|
-
throw new common_1.MethodNotAllowedError({
|
|
380
|
-
message: `Method "${method}" is not allowed by target endpoint`
|
|
381
|
-
});
|
|
382
|
-
}
|
|
383
|
-
catch (e) {
|
|
384
|
-
if (e instanceof common_1.OpraException)
|
|
385
|
-
throw e;
|
|
386
|
-
throw new common_1.BadRequestError(e);
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
// async parseMultiPart(
|
|
390
|
-
// context: TExecutionContext,
|
|
391
|
-
// url: OpraURL,
|
|
392
|
-
// headers: IncomingHttpHeaders,
|
|
393
|
-
// input: Readable,
|
|
394
|
-
// boundary: string
|
|
395
|
-
// ): Promise<BatchRequestContext> {
|
|
396
|
-
// return await new Promise((resolve, reject) => {
|
|
397
|
-
// let _resolved = false;
|
|
398
|
-
// const dicer = new Dicer({boundary});
|
|
399
|
-
// const doReject = (e) => {
|
|
400
|
-
// if (_resolved) return;
|
|
401
|
-
// _resolved = true;
|
|
402
|
-
// reject(e);
|
|
403
|
-
// taskQueue.clearQueue();
|
|
404
|
-
// dicer.destroy();
|
|
405
|
-
// }
|
|
406
|
-
// const taskQueue = new TaskQueue({concurrency: 1});
|
|
407
|
-
// taskQueue.on('error', doReject);
|
|
408
|
-
//
|
|
409
|
-
// const queries: SingleRequestContext[] = [];
|
|
410
|
-
// let partCounter = 0;
|
|
411
|
-
// dicer.on('error', doReject);
|
|
412
|
-
// dicer.on('part', part => {
|
|
413
|
-
// const partIndex = partCounter++;
|
|
414
|
-
// let header: any;
|
|
415
|
-
// const chunks: Buffer[] = [];
|
|
416
|
-
// part.on('error', doReject);
|
|
417
|
-
// part.on('header', (_header) => header = normalizeHeaders(_header));
|
|
418
|
-
// part.on('data', (chunk: Buffer) => chunks.push(chunk));
|
|
419
|
-
// part.on('end', () => {
|
|
420
|
-
// if (_resolved || !(header || chunks.length))
|
|
421
|
-
// return;
|
|
422
|
-
// const ct = header['content-type'];
|
|
423
|
-
// if (ct === 'application/http') {
|
|
424
|
-
// taskQueue.enqueue(async () => {
|
|
425
|
-
// const data = Buffer.concat(chunks);
|
|
426
|
-
// if (!(data && data.length))
|
|
427
|
-
// return;
|
|
428
|
-
// const r = HttpRequest.parse(data);
|
|
429
|
-
// await callMiddlewares(r, [jsonBodyParser]);
|
|
430
|
-
// const subUrl = new OpraURL(r.url);
|
|
431
|
-
// const contentId = header && header['content-id'];
|
|
432
|
-
// queries.push(this.parseSingleQuery({
|
|
433
|
-
// context,
|
|
434
|
-
// url: subUrl,
|
|
435
|
-
// method: r.method,
|
|
436
|
-
// headers: r.headers,
|
|
437
|
-
// body: r.body,
|
|
438
|
-
// contentId
|
|
439
|
-
// }));
|
|
440
|
-
// });
|
|
441
|
-
// } else doReject(new BadRequestError({
|
|
442
|
-
// message: 'Unaccepted "content-type" header in multipart data',
|
|
443
|
-
// details: {
|
|
444
|
-
// position: `${boundary}[${partIndex}]`
|
|
445
|
-
// }
|
|
446
|
-
// }))
|
|
447
|
-
// });
|
|
448
|
-
// });
|
|
449
|
-
// dicer.on('finish', () => {
|
|
450
|
-
// taskQueue.enqueue(() => {
|
|
451
|
-
// if (_resolved) return;
|
|
452
|
-
// _resolved = true;
|
|
453
|
-
// const batch = new BatchRequestContext({
|
|
454
|
-
// service: this.document,
|
|
455
|
-
// context,
|
|
456
|
-
// headers,
|
|
457
|
-
// queries,
|
|
458
|
-
// params: url.searchParams,
|
|
459
|
-
// continueOnError: false
|
|
460
|
-
// });
|
|
461
|
-
// resolve(batch);
|
|
462
|
-
// });
|
|
463
|
-
// });
|
|
464
|
-
// input.pipe(dicer);
|
|
465
|
-
// });
|
|
466
|
-
// }
|
|
467
|
-
async sendResponse(context) {
|
|
468
|
-
const { request, response } = context;
|
|
469
|
-
const outgoing = response.switchToHttp();
|
|
470
|
-
const errors = response.errors?.map(e => (0, common_1.wrapException)(e));
|
|
471
|
-
if (errors && errors.length) {
|
|
472
|
-
await this.errorHandler(request.switchToHttp(), outgoing, errors);
|
|
473
|
-
return;
|
|
474
|
-
}
|
|
475
|
-
outgoing.set(common_1.HttpHeaderCodes.Cache_Control, 'no-cache');
|
|
476
|
-
outgoing.set(common_1.HttpHeaderCodes.Pragma, 'no-cache');
|
|
477
|
-
outgoing.set(common_1.HttpHeaderCodes.Expires, '-1');
|
|
478
|
-
outgoing.set(common_1.HttpHeaderCodes.X_Opra_Version, common_1.OpraSchema.SpecVersion);
|
|
479
|
-
outgoing.status(outgoing.statusCode || common_1.HttpStatusCodes.OK);
|
|
480
|
-
if (response.value) {
|
|
481
|
-
if (typeof response.value === 'object') {
|
|
482
|
-
if ((0, common_1.isReadable)(response.value) || Buffer.isBuffer(response.value))
|
|
483
|
-
outgoing.send(response.value);
|
|
484
|
-
else {
|
|
485
|
-
const body = this.i18n.deep(response.value);
|
|
486
|
-
outgoing.set(common_1.HttpHeaderCodes.Content_Type, 'application/json; charset=utf-8');
|
|
487
|
-
outgoing.send(JSON.stringify(body));
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
else
|
|
491
|
-
outgoing.send(JSON.stringify(response.value));
|
|
492
|
-
}
|
|
493
|
-
outgoing.end();
|
|
494
|
-
}
|
|
495
133
|
}
|
|
496
134
|
exports.OpraHttpAdapter = OpraHttpAdapter;
|
|
@@ -0,0 +1,127 @@
|
|
|
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 tslib_1 = require("tslib");
|
|
9
|
+
const stream = tslib_1.__importStar(require("stream"));
|
|
10
|
+
const http_parser_1 = require("@browsery/http-parser");
|
|
11
|
+
const common_1 = require("@opra/common");
|
|
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('kHeaders');
|
|
16
|
+
exports.kHeadersDistinct = Symbol('kHeadersDistinct');
|
|
17
|
+
exports.kTrailers = Symbol('kTrailers');
|
|
18
|
+
exports.kTrailersDistinct = Symbol('kTrailersDistinct');
|
|
19
|
+
/**
|
|
20
|
+
*
|
|
21
|
+
* @class HttpIncomingMessageHost
|
|
22
|
+
*/
|
|
23
|
+
class HttpIncomingMessageHost {
|
|
24
|
+
constructor() {
|
|
25
|
+
this.rawHeaders = [];
|
|
26
|
+
this.rawTrailers = [];
|
|
27
|
+
this.complete = false;
|
|
28
|
+
this.joinDuplicateHeaders = false;
|
|
29
|
+
stream.Readable.apply(this);
|
|
30
|
+
}
|
|
31
|
+
get httpVersion() {
|
|
32
|
+
return this.httpVersionMajor
|
|
33
|
+
? this.httpVersionMajor + '.' + this.httpVersionMinor
|
|
34
|
+
: '';
|
|
35
|
+
}
|
|
36
|
+
get headers() {
|
|
37
|
+
if (!this[exports.kHeaders])
|
|
38
|
+
this[exports.kHeaders] = (0, convert_to_headers_js_1.convertToHeaders)(this.rawHeaders, {}, this.joinDuplicateHeaders);
|
|
39
|
+
return this[exports.kHeaders];
|
|
40
|
+
}
|
|
41
|
+
set headers(headers) {
|
|
42
|
+
this[exports.kHeaders] = headers;
|
|
43
|
+
}
|
|
44
|
+
get headersDistinct() {
|
|
45
|
+
if (!this[exports.kHeadersDistinct])
|
|
46
|
+
this[exports.kHeadersDistinct] = (0, convert_to_headers_js_1.convertToHeadersDistinct)(this.rawHeaders, {});
|
|
47
|
+
return this[exports.kHeadersDistinct];
|
|
48
|
+
}
|
|
49
|
+
get trailers() {
|
|
50
|
+
if (!this[exports.kTrailers])
|
|
51
|
+
this[exports.kTrailers] = (0, convert_to_headers_js_1.convertToHeaders)(this.rawTrailers, {}, this.joinDuplicateHeaders);
|
|
52
|
+
return this[exports.kTrailers];
|
|
53
|
+
}
|
|
54
|
+
set trailers(trailers) {
|
|
55
|
+
this[exports.kTrailers] = trailers;
|
|
56
|
+
}
|
|
57
|
+
get trailersDistinct() {
|
|
58
|
+
if (!this[exports.kTrailersDistinct])
|
|
59
|
+
this[exports.kTrailersDistinct] = (0, convert_to_headers_js_1.convertToHeadersDistinct)(this.rawTrailers, {});
|
|
60
|
+
return this[exports.kTrailersDistinct];
|
|
61
|
+
}
|
|
62
|
+
_readConfig(init) {
|
|
63
|
+
this.complete = true;
|
|
64
|
+
this.httpVersionMajor = init?.httpVersionMajor || 1;
|
|
65
|
+
this.httpVersionMinor = init?.httpVersionMinor || 0;
|
|
66
|
+
this.method = (init.method || 'GET').toUpperCase();
|
|
67
|
+
this.url = init.url || '';
|
|
68
|
+
this.body = init.body;
|
|
69
|
+
if (init.headers)
|
|
70
|
+
this.rawHeaders = Array.isArray(init.headers) ? init.headers : (0, convert_to_raw_headers_js_1.convertToRawHeaders)(init.headers);
|
|
71
|
+
if (init.trailers)
|
|
72
|
+
this.rawTrailers = Array.isArray(init.trailers) ? init.trailers : (0, convert_to_raw_headers_js_1.convertToRawHeaders)(init.trailers);
|
|
73
|
+
this.ip = init.ip || '';
|
|
74
|
+
this.ips = init.ips || (this.ip ? [this.ip] : []);
|
|
75
|
+
}
|
|
76
|
+
_readBuffer(buf) {
|
|
77
|
+
const parser = new http_parser_1.HTTPParser(http_parser_1.HTTPParser.REQUEST);
|
|
78
|
+
let bodyChunks;
|
|
79
|
+
parser[http_parser_1.HTTPParser.kOnHeadersComplete] = (info) => {
|
|
80
|
+
this.httpVersionMajor = info.versionMajor;
|
|
81
|
+
this.httpVersionMinor = info.versionMinor;
|
|
82
|
+
this.rawHeaders = info.headers;
|
|
83
|
+
this.method = http_parser_1.HTTPParser.methods[info.method];
|
|
84
|
+
this.url = info.url;
|
|
85
|
+
};
|
|
86
|
+
parser[http_parser_1.HTTPParser.kOnHeaders] = (trailers) => {
|
|
87
|
+
this.rawTrailers = trailers;
|
|
88
|
+
};
|
|
89
|
+
parser[http_parser_1.HTTPParser.kOnBody] = (chunk, offset, length) => {
|
|
90
|
+
bodyChunks = bodyChunks || [];
|
|
91
|
+
bodyChunks.push(chunk.subarray(offset, offset + length));
|
|
92
|
+
};
|
|
93
|
+
parser[http_parser_1.HTTPParser.kOnMessageComplete] = () => {
|
|
94
|
+
this.complete = true;
|
|
95
|
+
if (bodyChunks)
|
|
96
|
+
this.body = Buffer.concat(bodyChunks);
|
|
97
|
+
};
|
|
98
|
+
const buffer = Buffer.from(buf);
|
|
99
|
+
let x = parser.execute(buffer, 0, buffer.length);
|
|
100
|
+
if (typeof x === 'object')
|
|
101
|
+
throw x;
|
|
102
|
+
if (!this.complete) {
|
|
103
|
+
x = parser.execute(exports.CRLF);
|
|
104
|
+
if (typeof x === 'object')
|
|
105
|
+
throw x;
|
|
106
|
+
}
|
|
107
|
+
parser.finish();
|
|
108
|
+
}
|
|
109
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
110
|
+
_readStream(readable) {
|
|
111
|
+
throw new Error('_readStream is not implemented yet');
|
|
112
|
+
}
|
|
113
|
+
static create(init) {
|
|
114
|
+
const msg = new HttpIncomingMessageHost();
|
|
115
|
+
if (Buffer.isBuffer(init))
|
|
116
|
+
msg._readBuffer(init);
|
|
117
|
+
else if ((0, common_1.isReadable)(init)) {
|
|
118
|
+
throw new Error('fromStream is not implemented yet');
|
|
119
|
+
}
|
|
120
|
+
else if (init)
|
|
121
|
+
msg._readConfig(init);
|
|
122
|
+
return msg;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
exports.HttpIncomingMessageHost = HttpIncomingMessageHost;
|
|
126
|
+
// Apply mixins
|
|
127
|
+
Object.assign(HttpIncomingMessageHost.prototype, stream.Readable.prototype);
|