@congruent-stack/congruent-api 0.9.0-rc.0 → 0.10.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/dist/index.cjs +387 -88
- package/dist/index.d.cts +230 -52
- package/dist/index.d.mts +230 -52
- package/dist/index.mjs +385 -89
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -19,6 +19,11 @@ class HttpMethodEndpoint {
|
|
|
19
19
|
}
|
|
20
20
|
return this._cachedGenericPath;
|
|
21
21
|
}
|
|
22
|
+
createPath(pathParams) {
|
|
23
|
+
return `/${this.pathSegments.map(
|
|
24
|
+
(segment) => segment.startsWith(":") ? pathParams[segment.slice(1)] ?? "?" : segment
|
|
25
|
+
).join("/")}`;
|
|
26
|
+
}
|
|
22
27
|
_method = null;
|
|
23
28
|
get method() {
|
|
24
29
|
return this._method;
|
|
@@ -112,6 +117,7 @@ var HttpStatusCode = /* @__PURE__ */ ((HttpStatusCode2) => {
|
|
|
112
117
|
HttpStatusCode2[HttpStatusCode2["Forbidden_403"] = 403] = "Forbidden_403";
|
|
113
118
|
HttpStatusCode2[HttpStatusCode2["NotFound_404"] = 404] = "NotFound_404";
|
|
114
119
|
HttpStatusCode2[HttpStatusCode2["Conflict_409"] = 409] = "Conflict_409";
|
|
120
|
+
HttpStatusCode2[HttpStatusCode2["UnprocessableEntity_422"] = 422] = "UnprocessableEntity_422";
|
|
115
121
|
HttpStatusCode2[HttpStatusCode2["InternalServerError_500"] = 500] = "InternalServerError_500";
|
|
116
122
|
HttpStatusCode2[HttpStatusCode2["NotImplemented_501"] = 501] = "NotImplemented_501";
|
|
117
123
|
HttpStatusCode2[HttpStatusCode2["BadGateway_502"] = 502] = "BadGateway_502";
|
|
@@ -140,6 +146,9 @@ class MethodEndpointHandlerRegistryEntry {
|
|
|
140
146
|
this._methodEndpoint = methodEndpoint;
|
|
141
147
|
this._dicontainer = dicontainer;
|
|
142
148
|
}
|
|
149
|
+
get genericPath() {
|
|
150
|
+
return this._methodEndpoint.genericPath;
|
|
151
|
+
}
|
|
143
152
|
_handler = null;
|
|
144
153
|
get handler() {
|
|
145
154
|
return this._handler;
|
|
@@ -149,6 +158,7 @@ class MethodEndpointHandlerRegistryEntry {
|
|
|
149
158
|
if (this._onHandlerRegisteredCallback) {
|
|
150
159
|
this._onHandlerRegisteredCallback(this);
|
|
151
160
|
}
|
|
161
|
+
return this;
|
|
152
162
|
}
|
|
153
163
|
_onHandlerRegisteredCallback = null;
|
|
154
164
|
_onHandlerRegistered(callback) {
|
|
@@ -166,7 +176,55 @@ class MethodEndpointHandlerRegistryEntry {
|
|
|
166
176
|
this._injection = injection;
|
|
167
177
|
return this;
|
|
168
178
|
}
|
|
179
|
+
_decoratorFactories = [];
|
|
180
|
+
get decoratorFactories() {
|
|
181
|
+
return this._decoratorFactories;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
*
|
|
185
|
+
* @param decoratorFactory must be a function that takes exactly the DI scope type and returns an instance of a class that implements IEndpointHandlerDecorator
|
|
186
|
+
* @returns this
|
|
187
|
+
*
|
|
188
|
+
* Initially, the method was defined as:
|
|
189
|
+
* ```ts
|
|
190
|
+
* decorate<
|
|
191
|
+
* TDecoratorSchemas extends IDecoratorHandlerSchemas,
|
|
192
|
+
* TDecorator extends IEndpointHandlerDecorator<TDecoratorSchemas>
|
|
193
|
+
* > (
|
|
194
|
+
* decoratorFactory: IEndpointHandlerDecoratorFactory<TDecoratorSchemas, TDIContainer, TDecorator>
|
|
195
|
+
* ): this {
|
|
196
|
+
* this._decoratorFactories.push(decoratorFactory);
|
|
197
|
+
* return this;
|
|
198
|
+
* }
|
|
199
|
+
* ```
|
|
200
|
+
*
|
|
201
|
+
* and the type IEndpointHandlerDecoratorFactory was defined as:
|
|
202
|
+
* ```ts
|
|
203
|
+
* type IEndpointHandlerDecoratorFactory<
|
|
204
|
+
* TDecoratorSchemas extends IDecoratorHandlerSchemas,
|
|
205
|
+
* TDIContainer extends DIContainer,
|
|
206
|
+
* TDecorator extends IEndpointHandlerDecorator<TDecoratorSchemas>
|
|
207
|
+
* > = (diScope: ReturnType<TDIContainer['createScope']>) => TDecorator;
|
|
208
|
+
* ```
|
|
209
|
+
*
|
|
210
|
+
* However, TypeScript was incorrectly inferring the types when using the 'decorate' method.
|
|
211
|
+
* The end developer would have had to explicitly provide the generic types, which is not ideal.
|
|
212
|
+
*
|
|
213
|
+
* With the current definition, TypeScript can infer the types from the decoratorFactory parameter,
|
|
214
|
+
* making it easier to use.
|
|
215
|
+
*/
|
|
216
|
+
decorate(decoratorFactory, decoratingArgs) {
|
|
217
|
+
this._decoratorFactories.push((scope) => decoratorFactory(scope, decoratingArgs));
|
|
218
|
+
return this;
|
|
219
|
+
}
|
|
220
|
+
decorateWith(decoratorStaticMethodFactory, decoratingArgs) {
|
|
221
|
+
this._decoratorFactories.push((scope) => decoratorStaticMethodFactory.create(scope, decoratingArgs));
|
|
222
|
+
return this;
|
|
223
|
+
}
|
|
169
224
|
async trigger(diScope, requestObject) {
|
|
225
|
+
return this.triggerNoStaticTypeCheck(diScope, requestObject, { originalRequest: requestObject });
|
|
226
|
+
}
|
|
227
|
+
async exec(injected, requestObject) {
|
|
170
228
|
if (!this._handler) {
|
|
171
229
|
throw new Error("Handler not set for this endpoint");
|
|
172
230
|
}
|
|
@@ -186,9 +244,7 @@ class MethodEndpointHandlerRegistryEntry {
|
|
|
186
244
|
badRequestResponse = body;
|
|
187
245
|
return badRequestResponse;
|
|
188
246
|
}
|
|
189
|
-
const path =
|
|
190
|
-
(segment) => segment.startsWith(":") ? requestObject.pathParams[segment.slice(1)] ?? "?" : segment
|
|
191
|
-
).join("/")}`;
|
|
247
|
+
const path = this._methodEndpoint.createPath(requestObject.pathParams);
|
|
192
248
|
return await this._handler({
|
|
193
249
|
method: this._methodEndpoint.method,
|
|
194
250
|
path,
|
|
@@ -197,9 +253,41 @@ class MethodEndpointHandlerRegistryEntry {
|
|
|
197
253
|
headers,
|
|
198
254
|
pathParams: requestObject.pathParams,
|
|
199
255
|
query,
|
|
200
|
-
body
|
|
201
|
-
|
|
202
|
-
|
|
256
|
+
body
|
|
257
|
+
}, injected);
|
|
258
|
+
}
|
|
259
|
+
async triggerNoStaticTypeCheck(diScope, requestObject, context) {
|
|
260
|
+
if (!this._handler) {
|
|
261
|
+
throw new Error("Handler not set for this endpoint");
|
|
262
|
+
}
|
|
263
|
+
let badRequestResponse = null;
|
|
264
|
+
const headers = parseRequestDefinitionField(this._methodEndpoint.definition, "headers", requestObject);
|
|
265
|
+
if (isHttpResponseObject(headers)) {
|
|
266
|
+
badRequestResponse = headers;
|
|
267
|
+
return badRequestResponse;
|
|
268
|
+
}
|
|
269
|
+
const query = parseRequestDefinitionField(this._methodEndpoint.definition, "query", requestObject);
|
|
270
|
+
if (isHttpResponseObject(query)) {
|
|
271
|
+
badRequestResponse = query;
|
|
272
|
+
return badRequestResponse;
|
|
273
|
+
}
|
|
274
|
+
const body = parseRequestDefinitionField(this._methodEndpoint.definition, "body", requestObject);
|
|
275
|
+
if (isHttpResponseObject(body)) {
|
|
276
|
+
badRequestResponse = body;
|
|
277
|
+
return badRequestResponse;
|
|
278
|
+
}
|
|
279
|
+
const path = this._methodEndpoint.createPath(requestObject.pathParams);
|
|
280
|
+
Object.assign(context, this._injection(diScope));
|
|
281
|
+
return await this._handler({
|
|
282
|
+
method: this._methodEndpoint.method,
|
|
283
|
+
path,
|
|
284
|
+
genericPath: this._methodEndpoint.genericPath,
|
|
285
|
+
pathSegments: this._methodEndpoint.pathSegments,
|
|
286
|
+
headers,
|
|
287
|
+
pathParams: requestObject.pathParams,
|
|
288
|
+
query,
|
|
289
|
+
body
|
|
290
|
+
}, context);
|
|
203
291
|
}
|
|
204
292
|
}
|
|
205
293
|
function parseRequestDefinitionField(definition, key, requestObject) {
|
|
@@ -255,7 +343,26 @@ class MiddlewareHandlersRegistryEntryInternal {
|
|
|
255
343
|
get genericPath() {
|
|
256
344
|
return this._middlewareGenericPath;
|
|
257
345
|
}
|
|
258
|
-
|
|
346
|
+
_middlewarePathSegments;
|
|
347
|
+
get pathSegments() {
|
|
348
|
+
return this._middlewarePathSegments;
|
|
349
|
+
}
|
|
350
|
+
_middlewareMethod;
|
|
351
|
+
get method() {
|
|
352
|
+
return this._middlewareMethod;
|
|
353
|
+
}
|
|
354
|
+
_middlewareSchemas;
|
|
355
|
+
get middlewareSchemas() {
|
|
356
|
+
return this._middlewareSchemas;
|
|
357
|
+
}
|
|
358
|
+
_handler;
|
|
359
|
+
constructor(diContainer, middlewarePath, middlewareSchemas, decoratorFactories, injection, handler) {
|
|
360
|
+
this._dicontainer = diContainer;
|
|
361
|
+
this._middlewareGenericPath = middlewarePath;
|
|
362
|
+
this._middlewareSchemas = middlewareSchemas;
|
|
363
|
+
this._decoratorFactories.push(...decoratorFactories);
|
|
364
|
+
this._injection = injection;
|
|
365
|
+
this._handler = handler;
|
|
259
366
|
const splitResult = this._middlewareGenericPath.split(" ");
|
|
260
367
|
let method = "";
|
|
261
368
|
let pathSegments = [];
|
|
@@ -266,58 +373,57 @@ class MiddlewareHandlersRegistryEntryInternal {
|
|
|
266
373
|
}
|
|
267
374
|
pathSegments = splitResult[1].split("/").map((segment) => segment.trim()).filter((segment) => segment !== "");
|
|
268
375
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
pathSegments
|
|
272
|
-
};
|
|
376
|
+
this._middlewareMethod = method;
|
|
377
|
+
this._middlewarePathSegments = pathSegments;
|
|
273
378
|
}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
this._dicontainer = diContainer;
|
|
278
|
-
this._middlewareGenericPath = middlewarePath;
|
|
279
|
-
this._inputSchemas = inputSchemas;
|
|
280
|
-
this._injection = injection;
|
|
281
|
-
this._handler = handler;
|
|
379
|
+
_decoratorFactories = [];
|
|
380
|
+
get decoratorFactories() {
|
|
381
|
+
return this._decoratorFactories;
|
|
282
382
|
}
|
|
283
383
|
_injection = (_dicontainer) => ({});
|
|
284
|
-
async trigger(diScope, requestObject,
|
|
384
|
+
async trigger(diScope, requestObject, context) {
|
|
385
|
+
return this.triggerNoStaticTypeCheck(diScope, requestObject, context);
|
|
386
|
+
}
|
|
387
|
+
async triggerNoStaticTypeCheck(diScope, requestObject, context) {
|
|
285
388
|
let badRequestResponse = null;
|
|
286
|
-
const headers = middlewareParseRequestDefinitionField(this.
|
|
389
|
+
const headers = middlewareParseRequestDefinitionField(this._middlewareSchemas, "headers", requestObject);
|
|
287
390
|
if (isHttpResponseObject(headers)) {
|
|
288
391
|
badRequestResponse = headers;
|
|
289
392
|
return badRequestResponse;
|
|
290
393
|
}
|
|
291
|
-
const query = middlewareParseRequestDefinitionField(this.
|
|
394
|
+
const query = middlewareParseRequestDefinitionField(this._middlewareSchemas, "query", requestObject);
|
|
292
395
|
if (isHttpResponseObject(query)) {
|
|
293
396
|
badRequestResponse = query;
|
|
294
397
|
return badRequestResponse;
|
|
295
398
|
}
|
|
296
|
-
const body = middlewareParseRequestDefinitionField(this.
|
|
399
|
+
const body = middlewareParseRequestDefinitionField(this._middlewareSchemas, "body", requestObject);
|
|
297
400
|
if (isHttpResponseObject(body)) {
|
|
298
401
|
badRequestResponse = body;
|
|
299
402
|
return badRequestResponse;
|
|
300
403
|
}
|
|
301
|
-
const
|
|
302
|
-
|
|
303
|
-
(segment) => segment.startsWith(":") ? requestObject.pathParams[segment.slice(1)] ?? "?" : segment
|
|
304
|
-
).join("/")}`;
|
|
404
|
+
const path = this.createPath(requestObject.pathParams);
|
|
405
|
+
Object.assign(context, this._injection(diScope));
|
|
305
406
|
return await this._handler(
|
|
306
407
|
{
|
|
307
|
-
method,
|
|
308
|
-
// TODO: might be empty, as middleware can be registered with path only, without method
|
|
408
|
+
method: this.method,
|
|
409
|
+
// TODO: might be empty, as middleware can be registered with path only, without method
|
|
309
410
|
path,
|
|
310
411
|
genericPath: this.genericPath,
|
|
311
|
-
pathSegments,
|
|
412
|
+
pathSegments: this.pathSegments,
|
|
312
413
|
headers,
|
|
313
414
|
pathParams: requestObject.pathParams,
|
|
314
415
|
query,
|
|
315
|
-
body
|
|
316
|
-
injected: this._injection(diScope)
|
|
416
|
+
body
|
|
317
417
|
},
|
|
318
|
-
|
|
418
|
+
context
|
|
319
419
|
);
|
|
320
420
|
}
|
|
421
|
+
createPath(pathParams) {
|
|
422
|
+
const path = `/${this.pathSegments.map(
|
|
423
|
+
(segment) => segment.startsWith(":") ? pathParams[segment.slice(1)] ?? "?" : segment
|
|
424
|
+
).join("/")}`;
|
|
425
|
+
return path;
|
|
426
|
+
}
|
|
321
427
|
}
|
|
322
428
|
class MiddlewareHandlersRegistryEntry {
|
|
323
429
|
_registry;
|
|
@@ -334,16 +440,62 @@ class MiddlewareHandlersRegistryEntry {
|
|
|
334
440
|
this._injection = injection;
|
|
335
441
|
return this;
|
|
336
442
|
}
|
|
337
|
-
register(
|
|
443
|
+
register(middlewareSchemas, handler) {
|
|
338
444
|
const internalEntry = new MiddlewareHandlersRegistryEntryInternal(
|
|
339
445
|
this._registry.dicontainer,
|
|
340
446
|
this._path,
|
|
341
|
-
|
|
447
|
+
middlewareSchemas,
|
|
448
|
+
this._decoratorFactories,
|
|
342
449
|
this._injection,
|
|
343
450
|
handler
|
|
344
451
|
);
|
|
345
452
|
this._registry.register(internalEntry);
|
|
346
453
|
}
|
|
454
|
+
_decoratorFactories = [];
|
|
455
|
+
get decoratorFactories() {
|
|
456
|
+
return this._decoratorFactories;
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
*
|
|
460
|
+
* @param decoratorFactory must be a function that takes exactly the DI scope type and returns an instance of a class that implements IEndpointHandlerDecorator
|
|
461
|
+
* @returns this
|
|
462
|
+
*
|
|
463
|
+
* Initially, the method was defined as:
|
|
464
|
+
* ```ts
|
|
465
|
+
* decorate<
|
|
466
|
+
* TDecoratorSchemas extends IDecoratorHandlerSchemas,
|
|
467
|
+
* TDecorator extends IEndpointHandlerDecorator<TDecoratorSchemas>
|
|
468
|
+
* > (
|
|
469
|
+
* decoratorFactory: IEndpointHandlerDecoratorFactory<TDecoratorSchemas, TDIContainer, TDecorator>
|
|
470
|
+
* ): this {
|
|
471
|
+
* this._decoratorFactories.push(decoratorFactory);
|
|
472
|
+
* return this;
|
|
473
|
+
* }
|
|
474
|
+
* ```
|
|
475
|
+
*
|
|
476
|
+
* and the type IEndpointHandlerDecoratorFactory was defined as:
|
|
477
|
+
* ```ts
|
|
478
|
+
* type IEndpointHandlerDecoratorFactory<
|
|
479
|
+
* TDecoratorSchemas extends IDecoratorHandlerSchemas,
|
|
480
|
+
* TDIContainer extends DIContainer,
|
|
481
|
+
* TDecorator extends IEndpointHandlerDecorator<TDecoratorSchemas>
|
|
482
|
+
* > = (diScope: ReturnType<TDIContainer['createScope']>) => TDecorator;
|
|
483
|
+
* ```
|
|
484
|
+
*
|
|
485
|
+
* However, TypeScript was incorrectly inferring the types when using the 'decorate' method.
|
|
486
|
+
* The end developer would have had to explicitly provide the generic types, which is not ideal.
|
|
487
|
+
*
|
|
488
|
+
* With the current definition, TypeScript can infer the types from the decoratorFactory parameter,
|
|
489
|
+
* making it easier to use.
|
|
490
|
+
*/
|
|
491
|
+
decorate(decoratorFactory) {
|
|
492
|
+
this._decoratorFactories.push(decoratorFactory);
|
|
493
|
+
return this;
|
|
494
|
+
}
|
|
495
|
+
decorateWith(decoratorStaticMethodFactory) {
|
|
496
|
+
this._decoratorFactories.push(decoratorStaticMethodFactory.create);
|
|
497
|
+
return this;
|
|
498
|
+
}
|
|
347
499
|
}
|
|
348
500
|
class MiddlewareHandlersRegistry {
|
|
349
501
|
dicontainer;
|
|
@@ -366,12 +518,12 @@ class MiddlewareHandlersRegistry {
|
|
|
366
518
|
this._onHandlerRegisteredCallback = callback;
|
|
367
519
|
}
|
|
368
520
|
}
|
|
369
|
-
function middlewareParseRequestDefinitionField(
|
|
370
|
-
if (
|
|
521
|
+
function middlewareParseRequestDefinitionField(middlewareSchemas, key, requestObject) {
|
|
522
|
+
if (middlewareSchemas[key]) {
|
|
371
523
|
if (!(key in requestObject) || requestObject[key] === null || requestObject[key] === void 0) {
|
|
372
|
-
const result2 =
|
|
524
|
+
const result2 = middlewareSchemas[key].safeParse(requestObject[key]);
|
|
373
525
|
if (!result2.success) {
|
|
374
|
-
switch (
|
|
526
|
+
switch (middlewareSchemas[key].type) {
|
|
375
527
|
case "optional":
|
|
376
528
|
if (requestObject[key] === null) {
|
|
377
529
|
return void 0;
|
|
@@ -390,7 +542,7 @@ function middlewareParseRequestDefinitionField(inputSchemas, key, requestObject)
|
|
|
390
542
|
}
|
|
391
543
|
return result2.data;
|
|
392
544
|
}
|
|
393
|
-
const result =
|
|
545
|
+
const result = middlewareSchemas[key].safeParse(requestObject[key]);
|
|
394
546
|
if (!result.success) {
|
|
395
547
|
return {
|
|
396
548
|
code: HttpStatusCode.BadRequest_400,
|
|
@@ -521,6 +673,41 @@ function registerEntryHandler(endpointEntry, handler) {
|
|
|
521
673
|
endpointEntry.register(handler);
|
|
522
674
|
}
|
|
523
675
|
|
|
676
|
+
async function execHandlerChain(diScope, allHandlerEntries, input) {
|
|
677
|
+
const queue = [...allHandlerEntries];
|
|
678
|
+
let response = void 0;
|
|
679
|
+
const next = async () => {
|
|
680
|
+
if (response) {
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
683
|
+
const current = queue.shift();
|
|
684
|
+
if (!current) {
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
if (!input.genericPath.startsWith(current.genericPath)) {
|
|
688
|
+
await next();
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
const currResponse = await current.triggerNoStaticTypeCheck(
|
|
692
|
+
diScope,
|
|
693
|
+
input,
|
|
694
|
+
{
|
|
695
|
+
next
|
|
696
|
+
}
|
|
697
|
+
);
|
|
698
|
+
if (response) {
|
|
699
|
+
return;
|
|
700
|
+
}
|
|
701
|
+
if (currResponse) {
|
|
702
|
+
response = currResponse;
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
705
|
+
await next();
|
|
706
|
+
};
|
|
707
|
+
await next();
|
|
708
|
+
return response;
|
|
709
|
+
}
|
|
710
|
+
|
|
524
711
|
function partialPathString(_apiReg, path) {
|
|
525
712
|
return path;
|
|
526
713
|
}
|
|
@@ -714,66 +901,175 @@ class DIContainerTestClone extends DIContainerBase {
|
|
|
714
901
|
}
|
|
715
902
|
}
|
|
716
903
|
|
|
717
|
-
function
|
|
718
|
-
|
|
719
|
-
const
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
904
|
+
async function triggerEndpointDecoratorNoStaticTypeCheck(endpoint, decorator, requestObject, context) {
|
|
905
|
+
let badRequestResponse = null;
|
|
906
|
+
const headers = decoratorParseRequestDefinitionField(endpoint.definition, "headers", requestObject);
|
|
907
|
+
if (isHttpResponseObject(headers)) {
|
|
908
|
+
badRequestResponse = headers;
|
|
909
|
+
return badRequestResponse;
|
|
910
|
+
}
|
|
911
|
+
const query = decoratorParseRequestDefinitionField(endpoint.definition, "query", requestObject);
|
|
912
|
+
if (isHttpResponseObject(query)) {
|
|
913
|
+
badRequestResponse = query;
|
|
914
|
+
return badRequestResponse;
|
|
915
|
+
}
|
|
916
|
+
const body = decoratorParseRequestDefinitionField(endpoint.definition, "body", requestObject);
|
|
917
|
+
if (isHttpResponseObject(body)) {
|
|
918
|
+
badRequestResponse = body;
|
|
919
|
+
return badRequestResponse;
|
|
920
|
+
}
|
|
921
|
+
const path = endpoint.createPath(requestObject.pathParams);
|
|
922
|
+
return decorator.handle({
|
|
923
|
+
...requestObject,
|
|
924
|
+
method: endpoint.method,
|
|
925
|
+
genericPath: endpoint.genericPath,
|
|
926
|
+
path,
|
|
927
|
+
pathSegments: endpoint.pathSegments
|
|
928
|
+
}, context);
|
|
929
|
+
}
|
|
930
|
+
async function triggerMiddlewareDecoratorNoStaticTypeCheck(middlewareEntry, decorator, requestObject, context) {
|
|
931
|
+
let badRequestResponse = null;
|
|
932
|
+
const headers = decoratorParseRequestDefinitionField(middlewareEntry.middlewareSchemas, "headers", requestObject);
|
|
933
|
+
if (isHttpResponseObject(headers)) {
|
|
934
|
+
badRequestResponse = headers;
|
|
935
|
+
return badRequestResponse;
|
|
936
|
+
}
|
|
937
|
+
const query = decoratorParseRequestDefinitionField(middlewareEntry.middlewareSchemas, "query", requestObject);
|
|
938
|
+
if (isHttpResponseObject(query)) {
|
|
939
|
+
badRequestResponse = query;
|
|
940
|
+
return badRequestResponse;
|
|
941
|
+
}
|
|
942
|
+
const body = decoratorParseRequestDefinitionField(middlewareEntry.middlewareSchemas, "body", requestObject);
|
|
943
|
+
if (isHttpResponseObject(body)) {
|
|
944
|
+
badRequestResponse = body;
|
|
945
|
+
return badRequestResponse;
|
|
946
|
+
}
|
|
947
|
+
const path = middlewareEntry.createPath(requestObject.pathParams);
|
|
948
|
+
return decorator.handle({
|
|
949
|
+
...requestObject,
|
|
950
|
+
method: middlewareEntry.method,
|
|
951
|
+
// TODO: might be empty, as middleware can be registered with path only, without method
|
|
952
|
+
genericPath: middlewareEntry.genericPath,
|
|
953
|
+
path,
|
|
954
|
+
pathSegments: middlewareEntry.pathSegments
|
|
955
|
+
}, context);
|
|
956
|
+
}
|
|
957
|
+
function decoratorParseRequestDefinitionField(decoratorSchemas, key, requestObject) {
|
|
958
|
+
if (decoratorSchemas[key]) {
|
|
959
|
+
if (!(key in requestObject) || requestObject[key] === null || requestObject[key] === void 0) {
|
|
960
|
+
const result2 = decoratorSchemas[key].safeParse(requestObject[key]);
|
|
961
|
+
if (!result2.success) {
|
|
962
|
+
switch (decoratorSchemas[key].type) {
|
|
963
|
+
case "optional":
|
|
964
|
+
if (requestObject[key] === null) {
|
|
965
|
+
return void 0;
|
|
966
|
+
}
|
|
967
|
+
break;
|
|
968
|
+
case "nullable":
|
|
969
|
+
if (requestObject[key] === void 0) {
|
|
970
|
+
return null;
|
|
971
|
+
}
|
|
972
|
+
break;
|
|
973
|
+
}
|
|
974
|
+
return {
|
|
975
|
+
code: HttpStatusCode.BadRequest_400,
|
|
976
|
+
body: `'${key}' is required for this endpoint` + (key === "body" ? ", { 'Content-Type': 'application/json' } header might be missing" : "")
|
|
977
|
+
};
|
|
978
|
+
}
|
|
979
|
+
return result2.data;
|
|
723
980
|
}
|
|
724
|
-
const result =
|
|
725
|
-
|
|
726
|
-
{
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
query: input.query
|
|
731
|
-
},
|
|
732
|
-
next
|
|
733
|
-
);
|
|
734
|
-
if (result) {
|
|
735
|
-
return result;
|
|
981
|
+
const result = decoratorSchemas[key].safeParse(requestObject[key]);
|
|
982
|
+
if (!result.success) {
|
|
983
|
+
return {
|
|
984
|
+
code: HttpStatusCode.BadRequest_400,
|
|
985
|
+
body: result.error.issues
|
|
986
|
+
};
|
|
736
987
|
}
|
|
737
|
-
return
|
|
738
|
-
}
|
|
739
|
-
return
|
|
988
|
+
return result.data;
|
|
989
|
+
}
|
|
990
|
+
return null;
|
|
740
991
|
}
|
|
741
992
|
|
|
742
|
-
function createInProcApiClient(contract, testContainer, registry) {
|
|
743
|
-
const
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
993
|
+
function createInProcApiClient(contract, testContainer, registry, options) {
|
|
994
|
+
const mwHandlers = [];
|
|
995
|
+
const mwReg = registry._middlewareRegistry;
|
|
996
|
+
const mwNdx = 0;
|
|
997
|
+
for (const mwEntry of mwReg.list) {
|
|
998
|
+
if (!options?.filterMiddleware) {
|
|
999
|
+
addMiddlewareDecorators(mwHandlers, mwEntry);
|
|
1000
|
+
mwHandlers.push(mwEntry);
|
|
1001
|
+
continue;
|
|
747
1002
|
}
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
});
|
|
752
|
-
const mwReg = testApiReg._middlewareRegistry;
|
|
753
|
-
flatListAllRegistryEntries(registry).forEach((entry) => {
|
|
754
|
-
if (!entry.handler) {
|
|
755
|
-
return;
|
|
1003
|
+
const isIncluded = options.filterMiddleware(mwEntry.genericPath, mwNdx);
|
|
1004
|
+
if (!isIncluded) {
|
|
1005
|
+
continue;
|
|
756
1006
|
}
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
}
|
|
1007
|
+
addMiddlewareDecorators(mwHandlers, mwEntry);
|
|
1008
|
+
mwHandlers.push(mwEntry);
|
|
1009
|
+
}
|
|
760
1010
|
const client = createClient(contract, async (input) => {
|
|
1011
|
+
if (options?.enhanceRequest) {
|
|
1012
|
+
input = options.enhanceRequest(input);
|
|
1013
|
+
}
|
|
761
1014
|
const diScope = testContainer.createScope();
|
|
762
|
-
const
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
1015
|
+
const allHandlerEntries = [...mwHandlers];
|
|
1016
|
+
const endpointHandlerEntry = route(registry, `${input.method} ${input.genericPath}`);
|
|
1017
|
+
if (!endpointHandlerEntry.handler) {
|
|
1018
|
+
throw new Error(`No handler registered for ${input.method} ${input.genericPath}`);
|
|
1019
|
+
}
|
|
1020
|
+
endpointHandlerEntry.decoratorFactories.forEach((decoratorFactory) => {
|
|
1021
|
+
allHandlerEntries.push({
|
|
1022
|
+
genericPath: endpointHandlerEntry.genericPath,
|
|
1023
|
+
triggerNoStaticTypeCheck: async (diScope2, requestObject, context) => {
|
|
1024
|
+
const decorator = decoratorFactory(diScope2);
|
|
1025
|
+
return await triggerEndpointDecoratorNoStaticTypeCheck(
|
|
1026
|
+
endpointHandlerEntry.methodEndpoint,
|
|
1027
|
+
decorator,
|
|
1028
|
+
requestObject,
|
|
1029
|
+
context
|
|
1030
|
+
);
|
|
1031
|
+
}
|
|
1032
|
+
});
|
|
772
1033
|
});
|
|
773
|
-
|
|
1034
|
+
if (options?.mockEndpointResponse) {
|
|
1035
|
+
const mockResponse = options.mockEndpointResponse(input.genericPath, input.method, diScope);
|
|
1036
|
+
if (mockResponse) {
|
|
1037
|
+
allHandlerEntries.push({
|
|
1038
|
+
genericPath: endpointHandlerEntry.genericPath,
|
|
1039
|
+
triggerNoStaticTypeCheck: async (_diScope, _requestObject, _next) => {
|
|
1040
|
+
return mockResponse;
|
|
1041
|
+
}
|
|
1042
|
+
});
|
|
1043
|
+
} else {
|
|
1044
|
+
allHandlerEntries.push(endpointHandlerEntry);
|
|
1045
|
+
}
|
|
1046
|
+
} else {
|
|
1047
|
+
allHandlerEntries.push(endpointHandlerEntry);
|
|
1048
|
+
}
|
|
1049
|
+
const response = await execHandlerChain(diScope, allHandlerEntries, input);
|
|
1050
|
+
if (!response) {
|
|
1051
|
+
throw new Error(`No response from ${input.method} ${input.genericPath}`);
|
|
1052
|
+
}
|
|
1053
|
+
return response;
|
|
774
1054
|
});
|
|
775
1055
|
return client;
|
|
776
1056
|
}
|
|
1057
|
+
function addMiddlewareDecorators(mwHandlers, mwEntry) {
|
|
1058
|
+
mwEntry.decoratorFactories.forEach((decoratorFactory) => {
|
|
1059
|
+
mwHandlers.push({
|
|
1060
|
+
genericPath: mwEntry.genericPath,
|
|
1061
|
+
triggerNoStaticTypeCheck: async (diScope, requestObject, context) => {
|
|
1062
|
+
const decorator = decoratorFactory(diScope);
|
|
1063
|
+
return await triggerMiddlewareDecoratorNoStaticTypeCheck(
|
|
1064
|
+
mwEntry,
|
|
1065
|
+
decorator,
|
|
1066
|
+
requestObject,
|
|
1067
|
+
context
|
|
1068
|
+
);
|
|
1069
|
+
}
|
|
1070
|
+
});
|
|
1071
|
+
});
|
|
1072
|
+
}
|
|
777
1073
|
|
|
778
1074
|
exports.ApiClient = ApiClient;
|
|
779
1075
|
exports.ApiContract = ApiContract;
|
|
@@ -793,6 +1089,7 @@ exports.createClient = createClient;
|
|
|
793
1089
|
exports.createInProcApiClient = createInProcApiClient;
|
|
794
1090
|
exports.createRegistry = createRegistry;
|
|
795
1091
|
exports.endpoint = endpoint;
|
|
1092
|
+
exports.execHandlerChain = execHandlerChain;
|
|
796
1093
|
exports.flatListAllRegistryEntries = flatListAllRegistryEntries;
|
|
797
1094
|
exports.isHttpResponseObject = isHttpResponseObject;
|
|
798
1095
|
exports.isHttpStatusCode = isHttpStatusCode;
|
|
@@ -802,3 +1099,5 @@ exports.partialPathString = partialPathString;
|
|
|
802
1099
|
exports.register = register;
|
|
803
1100
|
exports.response = response;
|
|
804
1101
|
exports.route = route;
|
|
1102
|
+
exports.triggerEndpointDecoratorNoStaticTypeCheck = triggerEndpointDecoratorNoStaticTypeCheck;
|
|
1103
|
+
exports.triggerMiddlewareDecoratorNoStaticTypeCheck = triggerMiddlewareDecoratorNoStaticTypeCheck;
|