@forklaunch/core 0.7.4 → 0.8.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/lib/__test__/index.js +3 -5
- package/lib/__test__/index.js.map +1 -1
- package/lib/__test__/index.mjs +4 -6
- package/lib/__test__/index.mjs.map +1 -1
- package/lib/src/cache/index.js +30 -20
- package/lib/src/cache/index.js.map +1 -1
- package/lib/src/cache/index.mjs +37 -20
- package/lib/src/cache/index.mjs.map +1 -1
- package/lib/src/http/index.d.mts +201 -43
- package/lib/src/http/index.d.ts +201 -43
- package/lib/src/http/index.js +321 -37
- package/lib/src/http/index.js.map +1 -1
- package/lib/src/http/index.mjs +326 -37
- package/lib/src/http/index.mjs.map +1 -1
- package/lib/src/services/index.js +7 -1
- package/lib/src/services/index.js.map +1 -1
- package/lib/src/services/index.mjs +7 -1
- package/lib/src/services/index.mjs.map +1 -1
- package/package.json +31 -31
package/lib/src/http/index.js
CHANGED
@@ -41,6 +41,8 @@ __export(http_exports, {
|
|
41
41
|
HTTPStatuses: () => HTTPStatuses,
|
42
42
|
OpenTelemetryCollector: () => OpenTelemetryCollector,
|
43
43
|
delete_: () => delete_,
|
44
|
+
discriminateBody: () => discriminateBody,
|
45
|
+
discriminateResponseBodies: () => discriminateResponseBodies,
|
44
46
|
enrichExpressLikeSend: () => enrichExpressLikeSend,
|
45
47
|
evaluateTelemetryOptions: () => evaluateTelemetryOptions,
|
46
48
|
generateSwaggerDocument: () => generateSwaggerDocument,
|
@@ -278,6 +280,7 @@ async function parseRequestAuth(req, res, next) {
|
|
278
280
|
req
|
279
281
|
) ?? [];
|
280
282
|
if (error != null) {
|
283
|
+
res.type("text/plain");
|
281
284
|
res.status(error).send(message);
|
282
285
|
next?.(new Error(message));
|
283
286
|
}
|
@@ -548,7 +551,7 @@ function enrichDetails(path, contractDetails, requestSchema, responseSchemas, op
|
|
548
551
|
[ATTR_API_NAME]: req.contractDetails?.name || "unknown",
|
549
552
|
[import_semantic_conventions.ATTR_HTTP_REQUEST_METHOD]: req.method,
|
550
553
|
[import_semantic_conventions.ATTR_HTTP_ROUTE]: req.originalPath || "unknown",
|
551
|
-
[import_semantic_conventions.ATTR_HTTP_RESPONSE_STATUS_CODE]: res.statusCode
|
554
|
+
[import_semantic_conventions.ATTR_HTTP_RESPONSE_STATUS_CODE]: Number(res.statusCode)
|
552
555
|
});
|
553
556
|
});
|
554
557
|
next?.();
|
@@ -563,9 +566,9 @@ function hasSend(res) {
|
|
563
566
|
return typeof res === "object" && res !== null && "send" in res;
|
564
567
|
}
|
565
568
|
|
566
|
-
// src/http/guards/
|
567
|
-
function
|
568
|
-
return maybeResponseShape != null && "body" in maybeResponseShape
|
569
|
+
// src/http/guards/isRequestShape.ts
|
570
|
+
function isRequestShape(maybeResponseShape) {
|
571
|
+
return maybeResponseShape != null && ("body" in maybeResponseShape || "query" in maybeResponseShape || "params" in maybeResponseShape || "headers" in maybeResponseShape);
|
569
572
|
}
|
570
573
|
|
571
574
|
// src/http/middleware/request/parse.middleware.ts
|
@@ -580,7 +583,7 @@ function parse(req, res, next) {
|
|
580
583
|
req.requestSchema,
|
581
584
|
request
|
582
585
|
);
|
583
|
-
if (parsedRequest.ok &&
|
586
|
+
if (parsedRequest.ok && isRequestShape(parsedRequest.value)) {
|
584
587
|
req.body = parsedRequest.value.body;
|
585
588
|
req.params = parsedRequest.value.params;
|
586
589
|
Object.defineProperty(req, "query", {
|
@@ -589,7 +592,7 @@ function parse(req, res, next) {
|
|
589
592
|
enumerable: true,
|
590
593
|
configurable: false
|
591
594
|
});
|
592
|
-
req.headers = parsedRequest.value.headers;
|
595
|
+
req.headers = parsedRequest.value.headers ?? {};
|
593
596
|
}
|
594
597
|
if (!parsedRequest.ok) {
|
595
598
|
switch (req.contractDetails.options?.requestValidation) {
|
@@ -622,11 +625,132 @@ Correlation id: ${req.context.correlationId ?? "No correlation ID"}`
|
|
622
625
|
next?.();
|
623
626
|
}
|
624
627
|
|
628
|
+
// src/http/router/discriminateBody.ts
|
629
|
+
function discriminateBody(schemaValidator, body) {
|
630
|
+
if (body == null) {
|
631
|
+
return void 0;
|
632
|
+
}
|
633
|
+
const maybeTypedBody = body;
|
634
|
+
if ("text" in maybeTypedBody && maybeTypedBody.text != null) {
|
635
|
+
return {
|
636
|
+
contentType: maybeTypedBody.contentType ?? "text/plain",
|
637
|
+
parserType: "text",
|
638
|
+
schema: maybeTypedBody.text
|
639
|
+
};
|
640
|
+
} else if ("json" in maybeTypedBody && maybeTypedBody.json != null) {
|
641
|
+
return {
|
642
|
+
contentType: maybeTypedBody.contentType ?? "application/json",
|
643
|
+
parserType: "json",
|
644
|
+
schema: maybeTypedBody.json
|
645
|
+
};
|
646
|
+
} else if ("file" in maybeTypedBody && maybeTypedBody.file != null) {
|
647
|
+
return {
|
648
|
+
contentType: maybeTypedBody.contentType ?? "application/octet-stream",
|
649
|
+
parserType: "file",
|
650
|
+
schema: maybeTypedBody.file
|
651
|
+
};
|
652
|
+
} else if ("multipartForm" in maybeTypedBody && maybeTypedBody.multipartForm != null) {
|
653
|
+
return {
|
654
|
+
contentType: maybeTypedBody.contentType ?? "multipart/form-data",
|
655
|
+
parserType: "multipart",
|
656
|
+
schema: maybeTypedBody.multipartForm
|
657
|
+
};
|
658
|
+
} else if ("urlEncodedForm" in maybeTypedBody && maybeTypedBody.urlEncodedForm != null) {
|
659
|
+
return {
|
660
|
+
contentType: maybeTypedBody.contentType ?? "application/x-www-form-urlencoded",
|
661
|
+
parserType: "urlEncoded",
|
662
|
+
schema: maybeTypedBody.urlEncodedForm
|
663
|
+
};
|
664
|
+
} else if ("schema" in maybeTypedBody && maybeTypedBody.schema != null) {
|
665
|
+
return {
|
666
|
+
contentType: maybeTypedBody.contentType ?? "application/json",
|
667
|
+
parserType: "text",
|
668
|
+
schema: maybeTypedBody.schema
|
669
|
+
};
|
670
|
+
} else if (schemaValidator.isInstanceOf(
|
671
|
+
maybeTypedBody,
|
672
|
+
schemaValidator.string
|
673
|
+
)) {
|
674
|
+
return {
|
675
|
+
contentType: "text/plain",
|
676
|
+
parserType: "text",
|
677
|
+
schema: maybeTypedBody
|
678
|
+
};
|
679
|
+
} else {
|
680
|
+
return {
|
681
|
+
contentType: "application/json",
|
682
|
+
parserType: "json",
|
683
|
+
schema: maybeTypedBody
|
684
|
+
};
|
685
|
+
}
|
686
|
+
}
|
687
|
+
function discriminateResponseBodies(schemaValidator, responses) {
|
688
|
+
const discriminatedResponses = {};
|
689
|
+
for (const [statusCode, response] of Object.entries(responses)) {
|
690
|
+
if (response != null && typeof response === "object") {
|
691
|
+
if ("json" in response && response.json != null) {
|
692
|
+
discriminatedResponses[Number(statusCode)] = {
|
693
|
+
contentType: ("contentType" in response && typeof response.contentType === "string" ? response.contentType : "application/json") ?? "application/json",
|
694
|
+
parserType: "json",
|
695
|
+
schema: response.json
|
696
|
+
};
|
697
|
+
} else if ("schema" in response && response.schema != null) {
|
698
|
+
discriminatedResponses[Number(statusCode)] = {
|
699
|
+
contentType: ("contentType" in response && typeof response.contentType === "string" ? response.contentType : "application/json") ?? "application/json",
|
700
|
+
parserType: "text",
|
701
|
+
schema: response.schema
|
702
|
+
};
|
703
|
+
} else if ("text" in response && response.text != null) {
|
704
|
+
discriminatedResponses[Number(statusCode)] = {
|
705
|
+
contentType: ("contentType" in response && typeof response.contentType === "string" ? response.contentType : "text/plain") ?? "text/plain",
|
706
|
+
parserType: "text",
|
707
|
+
schema: response.text
|
708
|
+
};
|
709
|
+
} else if ("file" in response && response.file != null) {
|
710
|
+
discriminatedResponses[Number(statusCode)] = {
|
711
|
+
contentType: ("contentType" in response && typeof response.contentType === "string" ? response.contentType : "application/octet-stream") ?? "application/octet-stream",
|
712
|
+
parserType: "file",
|
713
|
+
schema: response.file
|
714
|
+
};
|
715
|
+
} else if ("event" in response && response.event != null) {
|
716
|
+
discriminatedResponses[Number(statusCode)] = {
|
717
|
+
contentType: ("contentType" in response && typeof response.contentType === "string" ? response.contentType : "text/event-stream") ?? "text/event-stream",
|
718
|
+
parserType: "serverSentEvent",
|
719
|
+
schema: response.event
|
720
|
+
};
|
721
|
+
} else if (schemaValidator.isInstanceOf(
|
722
|
+
response,
|
723
|
+
schemaValidator.string
|
724
|
+
)) {
|
725
|
+
discriminatedResponses[Number(statusCode)] = {
|
726
|
+
contentType: "text/plain",
|
727
|
+
parserType: "text",
|
728
|
+
schema: response
|
729
|
+
};
|
730
|
+
} else {
|
731
|
+
discriminatedResponses[Number(statusCode)] = {
|
732
|
+
contentType: "application/json",
|
733
|
+
parserType: "json",
|
734
|
+
schema: response
|
735
|
+
};
|
736
|
+
}
|
737
|
+
} else {
|
738
|
+
discriminatedResponses[Number(statusCode)] = {
|
739
|
+
contentType: "application/json",
|
740
|
+
parserType: "json",
|
741
|
+
schema: response
|
742
|
+
};
|
743
|
+
}
|
744
|
+
}
|
745
|
+
return discriminatedResponses;
|
746
|
+
}
|
747
|
+
|
625
748
|
// src/http/router/expressLikeRouter.ts
|
626
749
|
var ForklaunchExpressLikeRouter = class {
|
627
|
-
constructor(basePath, schemaValidator, internal, openTelemetryCollector) {
|
750
|
+
constructor(basePath, schemaValidator, internal, postEnrichMiddleware, openTelemetryCollector) {
|
628
751
|
this.schemaValidator = schemaValidator;
|
629
752
|
this.internal = internal;
|
753
|
+
this.postEnrichMiddleware = postEnrichMiddleware;
|
630
754
|
this.openTelemetryCollector = openTelemetryCollector;
|
631
755
|
this.basePath = basePath;
|
632
756
|
}
|
@@ -649,6 +773,7 @@ var ForklaunchExpressLikeRouter = class {
|
|
649
773
|
responseSchemas,
|
650
774
|
this.openTelemetryCollector
|
651
775
|
),
|
776
|
+
...this.postEnrichMiddleware,
|
652
777
|
parse,
|
653
778
|
parseRequestAuth
|
654
779
|
];
|
@@ -704,12 +829,16 @@ var ForklaunchExpressLikeRouter = class {
|
|
704
829
|
}
|
705
830
|
#compile(contractDetails) {
|
706
831
|
const schemaValidator = this.schemaValidator;
|
832
|
+
let body = null;
|
833
|
+
if (isHttpContractDetails(contractDetails)) {
|
834
|
+
body = discriminateBody(this.schemaValidator, contractDetails.body);
|
835
|
+
}
|
707
836
|
const requestSchema = schemaValidator.compile(
|
708
837
|
schemaValidator.schemify({
|
709
838
|
...contractDetails.params ? { params: contractDetails.params } : {},
|
710
839
|
...contractDetails.requestHeaders ? { headers: contractDetails.requestHeaders } : {},
|
711
840
|
...contractDetails.query ? { query: contractDetails.query } : {},
|
712
|
-
...
|
841
|
+
...body != null ? { body: body.schema } : {}
|
713
842
|
})
|
714
843
|
);
|
715
844
|
const responseEntries = {
|
@@ -718,9 +847,16 @@ var ForklaunchExpressLikeRouter = class {
|
|
718
847
|
403: schemaValidator.string,
|
719
848
|
404: schemaValidator.string,
|
720
849
|
500: schemaValidator.string,
|
721
|
-
...isPathParamHttpContractDetails(contractDetails) || isHttpContractDetails(contractDetails) ?
|
722
|
-
|
723
|
-
|
850
|
+
...isPathParamHttpContractDetails(contractDetails) || isHttpContractDetails(contractDetails) ? Object.fromEntries(
|
851
|
+
Object.entries(
|
852
|
+
discriminateResponseBodies(
|
853
|
+
this.schemaValidator,
|
854
|
+
contractDetails.responses
|
855
|
+
)
|
856
|
+
).map(([key, value]) => {
|
857
|
+
return [key, value.schema];
|
858
|
+
})
|
859
|
+
) : {}
|
724
860
|
};
|
725
861
|
const responseSchemas = {
|
726
862
|
responses: {},
|
@@ -756,7 +892,7 @@ var ForklaunchExpressLikeRouter = class {
|
|
756
892
|
params: request?.params ?? {},
|
757
893
|
query: request?.query ?? {},
|
758
894
|
headers: request?.headers ?? {},
|
759
|
-
body: request?.body ?? {},
|
895
|
+
body: discriminateBody(this.schemaValidator, request?.body)?.schema ?? {},
|
760
896
|
path: route
|
761
897
|
};
|
762
898
|
const res = {
|
@@ -775,6 +911,9 @@ var ForklaunchExpressLikeRouter = class {
|
|
775
911
|
},
|
776
912
|
setHeader: (key, value) => {
|
777
913
|
responseHeaders[key] = value;
|
914
|
+
},
|
915
|
+
sseEmiter: (generator) => {
|
916
|
+
responseMessage = generator;
|
778
917
|
}
|
779
918
|
};
|
780
919
|
let cursor = handlers.shift();
|
@@ -800,7 +939,7 @@ var ForklaunchExpressLikeRouter = class {
|
|
800
939
|
}
|
801
940
|
});
|
802
941
|
return {
|
803
|
-
code: statusCode,
|
942
|
+
code: Number(statusCode),
|
804
943
|
response: responseMessage,
|
805
944
|
headers: responseHeaders
|
806
945
|
};
|
@@ -1208,10 +1347,17 @@ var ForklaunchExpressLikeApplication = class extends ForklaunchExpressLikeRouter
|
|
1208
1347
|
*
|
1209
1348
|
* @param {SV} schemaValidator - The schema validator.
|
1210
1349
|
*/
|
1211
|
-
constructor(schemaValidator, internal, openTelemetryCollector) {
|
1212
|
-
super(
|
1350
|
+
constructor(schemaValidator, internal, postEnrichMiddleware, openTelemetryCollector) {
|
1351
|
+
super(
|
1352
|
+
"/",
|
1353
|
+
schemaValidator,
|
1354
|
+
internal,
|
1355
|
+
postEnrichMiddleware,
|
1356
|
+
openTelemetryCollector
|
1357
|
+
);
|
1213
1358
|
this.schemaValidator = schemaValidator;
|
1214
1359
|
this.internal = internal;
|
1360
|
+
this.postEnrichMiddleware = postEnrichMiddleware;
|
1215
1361
|
this.openTelemetryCollector = openTelemetryCollector;
|
1216
1362
|
this.internal.use(createContext(this.schemaValidator));
|
1217
1363
|
this.internal.use(cors);
|
@@ -2282,8 +2428,9 @@ var httpStatusCodes_default = HTTPStatuses;
|
|
2282
2428
|
var import_validator2 = require("@forklaunch/validator");
|
2283
2429
|
function parse2(req, res, next) {
|
2284
2430
|
const { headers, responses } = res.responseSchemas;
|
2431
|
+
const statusCode = Number(res.statusCode);
|
2285
2432
|
const parsedResponse = req.schemaValidator.parse(
|
2286
|
-
responses?.[
|
2433
|
+
responses?.[statusCode],
|
2287
2434
|
res.bodyData
|
2288
2435
|
);
|
2289
2436
|
const parsedHeaders = req.schemaValidator.parse(
|
@@ -2311,7 +2458,7 @@ function parse2(req, res, next) {
|
|
2311
2458
|
default:
|
2312
2459
|
case "error":
|
2313
2460
|
res.type("text/plain");
|
2314
|
-
res.status(
|
2461
|
+
res.status(500);
|
2315
2462
|
if (hasSend(res)) {
|
2316
2463
|
res.send(
|
2317
2464
|
`Invalid response:
|
@@ -2338,6 +2485,10 @@ ${parseErrors.join("\n\n")}`
|
|
2338
2485
|
next?.();
|
2339
2486
|
}
|
2340
2487
|
|
2488
|
+
// src/http/middleware/response/enrichExpressLikeSend.middleware.ts
|
2489
|
+
var import_common4 = require("@forklaunch/common");
|
2490
|
+
var import_stream = require("stream");
|
2491
|
+
|
2341
2492
|
// src/http/telemetry/recordMetric.ts
|
2342
2493
|
var import_semantic_conventions3 = require("@opentelemetry/semantic-conventions");
|
2343
2494
|
|
@@ -2356,21 +2507,121 @@ function recordMetric(req, res) {
|
|
2356
2507
|
[ATTR_CORRELATION_ID]: req.context.correlationId,
|
2357
2508
|
[import_semantic_conventions3.ATTR_HTTP_REQUEST_METHOD]: req.method,
|
2358
2509
|
[import_semantic_conventions3.ATTR_HTTP_ROUTE]: req.originalPath,
|
2359
|
-
[import_semantic_conventions3.ATTR_HTTP_RESPONSE_STATUS_CODE]: res.statusCode || 0
|
2510
|
+
[import_semantic_conventions3.ATTR_HTTP_RESPONSE_STATUS_CODE]: Number(res.statusCode) || 0
|
2360
2511
|
});
|
2361
2512
|
res.metricRecorded = true;
|
2362
2513
|
}
|
2363
2514
|
|
2364
2515
|
// src/http/middleware/response/enrichExpressLikeSend.middleware.ts
|
2365
|
-
function enrichExpressLikeSend(instance, req, res, originalSend, data, shouldEnrich) {
|
2366
|
-
let
|
2367
|
-
if (
|
2368
|
-
|
2369
|
-
|
2516
|
+
function enrichExpressLikeSend(instance, req, res, originalOperation, originalSend, data, shouldEnrich) {
|
2517
|
+
let errorSent = false;
|
2518
|
+
if (data == null) {
|
2519
|
+
originalSend.call(instance, data);
|
2520
|
+
return;
|
2521
|
+
}
|
2522
|
+
if (res.statusCode === 404) {
|
2523
|
+
res.type("text/plain");
|
2524
|
+
res.status(404);
|
2525
|
+
logger("error").error("Not Found");
|
2526
|
+
originalSend.call(instance, "Not Found");
|
2527
|
+
errorSent = true;
|
2528
|
+
}
|
2529
|
+
const responseBodies = discriminateResponseBodies(
|
2530
|
+
req.schemaValidator,
|
2531
|
+
req.contractDetails.responses
|
2532
|
+
);
|
2533
|
+
if (responseBodies != null && responseBodies[Number(res.statusCode)] != null) {
|
2534
|
+
res.type(responseBodies[Number(res.statusCode)].contentType);
|
2535
|
+
}
|
2536
|
+
if (data instanceof File || data instanceof Blob) {
|
2537
|
+
if (data instanceof File) {
|
2538
|
+
res.setHeader(
|
2539
|
+
"Content-Disposition",
|
2540
|
+
`attachment; filename="${data.name}"`
|
2541
|
+
);
|
2542
|
+
}
|
2543
|
+
if ((0, import_common4.isNodeJsWriteableStream)(res)) {
|
2544
|
+
import_stream.Readable.from((0, import_common4.readableStreamToAsyncIterable)(data.stream())).pipe(
|
2545
|
+
res
|
2546
|
+
);
|
2547
|
+
} else {
|
2548
|
+
res.type("text/plain");
|
2549
|
+
res.status(500);
|
2550
|
+
originalSend.call(instance, "Not a NodeJS WritableStream");
|
2551
|
+
errorSent = true;
|
2552
|
+
}
|
2553
|
+
} else if ((0, import_common4.isAsyncGenerator)(data)) {
|
2554
|
+
let firstPass = true;
|
2555
|
+
const transformer = new import_stream.Transform({
|
2556
|
+
objectMode: true,
|
2557
|
+
transform(chunk, _encoding, callback) {
|
2558
|
+
if (firstPass) {
|
2559
|
+
res.bodyData = chunk;
|
2560
|
+
parse2(req, res, (err) => {
|
2561
|
+
if (err) {
|
2562
|
+
let errorString = err.message;
|
2563
|
+
if (res.locals.errorMessage) {
|
2564
|
+
errorString += `
|
2565
|
+
------------------
|
2566
|
+
${res.locals.errorMessage}`;
|
2567
|
+
}
|
2568
|
+
logger("error").error(errorString);
|
2569
|
+
res.type("text/plain");
|
2570
|
+
res.status(500);
|
2571
|
+
originalSend.call(instance, errorString);
|
2572
|
+
errorSent = true;
|
2573
|
+
callback(new Error(errorString));
|
2574
|
+
}
|
2575
|
+
});
|
2576
|
+
firstPass = false;
|
2577
|
+
}
|
2578
|
+
if (!errorSent) {
|
2579
|
+
let data2 = "";
|
2580
|
+
for (const [key, value] of Object.entries(chunk)) {
|
2581
|
+
data2 += `${key}: ${typeof value === "string" ? value : (0, import_common4.safeStringify)(value)}
|
2582
|
+
`;
|
2583
|
+
}
|
2584
|
+
data2 += "\n";
|
2585
|
+
callback(null, data2);
|
2586
|
+
}
|
2587
|
+
}
|
2588
|
+
});
|
2589
|
+
if ((0, import_common4.isNodeJsWriteableStream)(res)) {
|
2590
|
+
import_stream.Readable.from(data).pipe(transformer).pipe(res);
|
2591
|
+
} else {
|
2370
2592
|
res.type("text/plain");
|
2371
|
-
res.status(
|
2372
|
-
|
2373
|
-
|
2593
|
+
res.status(500);
|
2594
|
+
originalSend.call(instance, "Not a NodeJS WritableStream");
|
2595
|
+
errorSent = true;
|
2596
|
+
}
|
2597
|
+
} else {
|
2598
|
+
const parserType = responseBodies?.[Number(res.statusCode)]?.parserType;
|
2599
|
+
res.bodyData = data;
|
2600
|
+
if ((0, import_common4.isRecord)(data)) {
|
2601
|
+
switch (parserType) {
|
2602
|
+
case "json":
|
2603
|
+
res.bodyData = "json" in data ? data.json : data;
|
2604
|
+
break;
|
2605
|
+
case "text":
|
2606
|
+
res.bodyData = "text" in data ? data.text : data;
|
2607
|
+
break;
|
2608
|
+
case "file":
|
2609
|
+
res.bodyData = "file" in data ? data.file : data;
|
2610
|
+
break;
|
2611
|
+
case "serverSentEvent":
|
2612
|
+
res.bodyData = "event" in data ? data.event : data;
|
2613
|
+
break;
|
2614
|
+
case "multipart":
|
2615
|
+
res.bodyData = "multipart" in data ? data.multipart : data;
|
2616
|
+
break;
|
2617
|
+
case void 0:
|
2618
|
+
res.bodyData = data;
|
2619
|
+
break;
|
2620
|
+
default:
|
2621
|
+
(0, import_common4.isNever)(parserType);
|
2622
|
+
res.bodyData = data;
|
2623
|
+
break;
|
2624
|
+
}
|
2374
2625
|
}
|
2375
2626
|
parse2(req, res, (err) => {
|
2376
2627
|
if (err) {
|
@@ -2384,12 +2635,21 @@ ${res.locals.errorMessage}`;
|
|
2384
2635
|
res.type("text/plain");
|
2385
2636
|
res.status(500);
|
2386
2637
|
originalSend.call(instance, errorString);
|
2387
|
-
|
2638
|
+
errorSent = true;
|
2388
2639
|
}
|
2389
2640
|
});
|
2641
|
+
if (!errorSent) {
|
2642
|
+
if (typeof data === "string") {
|
2643
|
+
res.type("text/plain");
|
2644
|
+
originalSend.call(instance, data);
|
2645
|
+
} else if (!(data instanceof File)) {
|
2646
|
+
res.sent = true;
|
2647
|
+
originalOperation.call(instance, data);
|
2648
|
+
}
|
2649
|
+
}
|
2390
2650
|
}
|
2391
|
-
if (
|
2392
|
-
|
2651
|
+
if (shouldEnrich) {
|
2652
|
+
recordMetric(req, res);
|
2393
2653
|
}
|
2394
2654
|
}
|
2395
2655
|
|
@@ -2428,10 +2688,14 @@ function generateOpenApiDocument(port, tags, paths) {
|
|
2428
2688
|
paths
|
2429
2689
|
};
|
2430
2690
|
}
|
2431
|
-
function contentResolver(schemaValidator, body) {
|
2691
|
+
function contentResolver(schemaValidator, body, contentType) {
|
2432
2692
|
const bodySpec = schemaValidator.openapi(body);
|
2433
|
-
return
|
2434
|
-
|
2693
|
+
return contentType != null ? {
|
2694
|
+
[contentType]: {
|
2695
|
+
schema: bodySpec
|
2696
|
+
}
|
2697
|
+
} : body === schemaValidator.string ? {
|
2698
|
+
"text/plain": {
|
2435
2699
|
schema: bodySpec
|
2436
2700
|
}
|
2437
2701
|
} : {
|
@@ -2456,15 +2720,29 @@ function generateSwaggerDocument(schemaValidator, port, routers) {
|
|
2456
2720
|
}
|
2457
2721
|
const { name, summary, query, requestHeaders } = route.contractDetails;
|
2458
2722
|
const responses = {};
|
2459
|
-
|
2723
|
+
const discriminatedResponseBodiesResult = discriminateResponseBodies(
|
2724
|
+
schemaValidator,
|
2725
|
+
route.contractDetails.responses
|
2726
|
+
);
|
2727
|
+
for (const key in discriminatedResponseBodiesResult) {
|
2460
2728
|
responses[key] = {
|
2461
2729
|
description: httpStatusCodes_default[key],
|
2462
2730
|
content: contentResolver(
|
2463
2731
|
schemaValidator,
|
2464
|
-
|
2732
|
+
discriminatedResponseBodiesResult[key].schema,
|
2733
|
+
discriminatedResponseBodiesResult[key].contentType
|
2465
2734
|
)
|
2466
2735
|
};
|
2467
2736
|
}
|
2737
|
+
const commonErrors = [400, 404, 500];
|
2738
|
+
for (const error of commonErrors) {
|
2739
|
+
if (!(error in responses)) {
|
2740
|
+
responses[error] = {
|
2741
|
+
description: httpStatusCodes_default[error],
|
2742
|
+
content: contentResolver(schemaValidator, schemaValidator.string)
|
2743
|
+
};
|
2744
|
+
}
|
2745
|
+
}
|
2468
2746
|
const pathItemObject = {
|
2469
2747
|
tags: [controllerName],
|
2470
2748
|
summary: `${name}: ${summary}`,
|
@@ -2482,11 +2760,15 @@ function generateSwaggerDocument(schemaValidator, port, routers) {
|
|
2482
2760
|
});
|
2483
2761
|
}
|
2484
2762
|
}
|
2485
|
-
const
|
2486
|
-
if (
|
2763
|
+
const discriminatedBodyResult = "body" in route.contractDetails ? discriminateBody(schemaValidator, route.contractDetails.body) : null;
|
2764
|
+
if (discriminatedBodyResult) {
|
2487
2765
|
pathItemObject.requestBody = {
|
2488
2766
|
required: true,
|
2489
|
-
content: contentResolver(
|
2767
|
+
content: contentResolver(
|
2768
|
+
schemaValidator,
|
2769
|
+
discriminatedBodyResult.schema,
|
2770
|
+
discriminatedBodyResult.contentType
|
2771
|
+
)
|
2490
2772
|
};
|
2491
2773
|
}
|
2492
2774
|
if (requestHeaders) {
|
@@ -2569,6 +2851,8 @@ function metricsDefinitions(metrics2) {
|
|
2569
2851
|
HTTPStatuses,
|
2570
2852
|
OpenTelemetryCollector,
|
2571
2853
|
delete_,
|
2854
|
+
discriminateBody,
|
2855
|
+
discriminateResponseBodies,
|
2572
2856
|
enrichExpressLikeSend,
|
2573
2857
|
evaluateTelemetryOptions,
|
2574
2858
|
generateSwaggerDocument,
|