@postman-cse/onboarding-bootstrap 2.4.0 → 2.6.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/README.md +1 -1
- package/dist/action.cjs +281 -5
- package/dist/cli.cjs +281 -5
- package/dist/index.cjs +281 -5
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -296971,12 +296971,36 @@ function buildOperationScript(operation, index, warnings) {
|
|
|
296971
296971
|
' if (mediaType === "application/json" && gqlBody.data !== undefined && gqlBody.data !== null && pm.response.code !== 200) { pm.expect.fail("a GraphQL response carrying data over application/json must be HTTP 200; got " + pm.response.code); }',
|
|
296972
296972
|
"});"
|
|
296973
296973
|
);
|
|
296974
|
+
lines.push(
|
|
296975
|
+
`pm.test(${JSON.stringify(`[${label}] GraphQL response map follows the spec response format`)}, function () {`,
|
|
296976
|
+
' var extraKeys = Object.keys(gqlBody).filter(function (key) { return key !== "data" && key !== "errors" && key !== "extensions"; });',
|
|
296977
|
+
' if (extraKeys.length > 0) pm.expect.fail("the GraphQL response map must not contain entries other than data, errors, and extensions (GraphQL spec 7.1): " + extraKeys.join(", "));',
|
|
296978
|
+
' if (Object.prototype.hasOwnProperty.call(gqlBody, "extensions") && (typeof gqlBody.extensions !== "object" || gqlBody.extensions === null || Array.isArray(gqlBody.extensions))) pm.expect.fail("the GraphQL extensions entry must be a map (GraphQL spec 7.1)");',
|
|
296979
|
+
' if (!Object.prototype.hasOwnProperty.call(gqlBody, "data") && (!Array.isArray(gqlBody.errors) || gqlBody.errors.length === 0)) pm.expect.fail("a GraphQL request-error result (no data entry) must carry a non-empty errors list (GraphQL spec 7.1)");',
|
|
296980
|
+
"});"
|
|
296981
|
+
);
|
|
296974
296982
|
lines.push(
|
|
296975
296983
|
`pm.test(${JSON.stringify(`[${label}] GraphQL errors are well-formed and not a total failure`)}, function () {`,
|
|
296976
296984
|
" var errors = gqlBody.errors;",
|
|
296977
296985
|
" if (errors === undefined || errors === null) return;",
|
|
296978
296986
|
` pm.expect(errors, "GraphQL 'errors' must be an array when present").to.be.an("array");`,
|
|
296979
|
-
|
|
296987
|
+
" errors.forEach(function (err, errIndex) {",
|
|
296988
|
+
' pm.expect(err, "each GraphQL error must carry a message").to.be.an("object").that.has.property("message");',
|
|
296989
|
+
' if (typeof err.message !== "string") pm.expect.fail("errors[" + errIndex + "].message must be a string (GraphQL spec 7.1.2)");',
|
|
296990
|
+
" if (err.locations !== undefined && err.locations !== null) {",
|
|
296991
|
+
' if (!Array.isArray(err.locations)) { pm.expect.fail("errors[" + errIndex + "].locations must be a list (GraphQL spec 7.1.2)"); return; }',
|
|
296992
|
+
" err.locations.forEach(function (loc, locIndex) {",
|
|
296993
|
+
' var locLabel = "errors[" + errIndex + "].locations[" + locIndex + "]";',
|
|
296994
|
+
' if (!loc || typeof loc !== "object" || Array.isArray(loc)) { pm.expect.fail(locLabel + " must be a map with line and column (GraphQL spec 7.1.2)"); return; }',
|
|
296995
|
+
' if (!Number.isInteger(loc.line) || loc.line < 1 || !Number.isInteger(loc.column) || loc.column < 1) pm.expect.fail(locLabel + " line and column must be positive integers starting from 1 (GraphQL spec 7.1.2)");',
|
|
296996
|
+
" });",
|
|
296997
|
+
" }",
|
|
296998
|
+
" if (err.path !== undefined && err.path !== null) {",
|
|
296999
|
+
' if (!Array.isArray(err.path)) { pm.expect.fail("errors[" + errIndex + "].path must be a list of path segments (GraphQL spec 7.1.2)"); return; }',
|
|
297000
|
+
' err.path.forEach(function (segment, segIndex) { if (typeof segment !== "string" && !Number.isInteger(segment)) pm.expect.fail("errors[" + errIndex + "].path[" + segIndex + "] must be a string field name or an integer list index (GraphQL spec 7.1.2)"); });',
|
|
297001
|
+
" }",
|
|
297002
|
+
' if (err.extensions !== undefined && err.extensions !== null && (typeof err.extensions !== "object" || Array.isArray(err.extensions))) pm.expect.fail("errors[" + errIndex + "].extensions must be a map (GraphQL spec 7.1.2)");',
|
|
297003
|
+
" });",
|
|
296980
297004
|
" var hasData = gqlBody.data !== undefined && gqlBody.data !== null;",
|
|
296981
297005
|
' if (!hasData && errors.length > 0) { pm.expect.fail("GraphQL request returned errors and no data: " + JSON.stringify(errors)); }',
|
|
296982
297006
|
"});"
|
|
@@ -297635,7 +297659,17 @@ function createGrpcScript(spec) {
|
|
|
297635
297659
|
// reporters/cli/modules/grpc.ts:15-21.
|
|
297636
297660
|
'var GRPC_STATUS = { 0: "OK", 1: "CANCELLED", 2: "UNKNOWN", 3: "INVALID_ARGUMENT", 4: "DEADLINE_EXCEEDED", 5: "NOT_FOUND", 6: "ALREADY_EXISTS", 7: "PERMISSION_DENIED", 8: "RESOURCE_EXHAUSTED", 9: "FAILED_PRECONDITION", 10: "ABORTED", 11: "OUT_OF_RANGE", 12: "UNIMPLEMENTED", 13: "INTERNAL", 14: "UNAVAILABLE", 15: "DATA_LOSS", 16: "UNAUTHENTICATED" };',
|
|
297637
297661
|
'function grpcStatusName() { if (typeof pm.response.status === "string" && pm.response.status) return pm.response.status; return GRPC_STATUS[pm.response.code] || ("UNKNOWN(" + pm.response.code + ")"); }',
|
|
297638
|
-
|
|
297662
|
+
// Terminal message: pm.response.json() where the sandbox provides it,
|
|
297663
|
+
// falling back to the last received message in pm.response.messages (the
|
|
297664
|
+
// unified runtime's GRPCResponse exposes messages but no json()).
|
|
297665
|
+
"function grpcMessage() {",
|
|
297666
|
+
' try { if (pm.response && typeof pm.response.json === "function") { var viaJson = pm.response.json(); if (viaJson !== undefined && viaJson !== null) return viaJson; } } catch (error) { /* fall back to messages */ }',
|
|
297667
|
+
" try {",
|
|
297668
|
+
" var list = pm.response && pm.response.messages;",
|
|
297669
|
+
' if (list && typeof list.each === "function") { var last; list.each(function (member) { last = member && member.data !== undefined ? member.data : member; }); return last; }',
|
|
297670
|
+
" } catch (error) { return undefined; }",
|
|
297671
|
+
" return undefined;",
|
|
297672
|
+
"}",
|
|
297639
297673
|
"function jsonTypeOf(value) {",
|
|
297640
297674
|
' if (value === null || value === undefined) return "null";',
|
|
297641
297675
|
' if (Array.isArray(value)) return "array";',
|
|
@@ -297786,9 +297820,104 @@ function createGrpcScript(spec) {
|
|
|
297786
297820
|
' if (set.length > 1) pm.expect.fail("gRPC response at " + path + " sets multiple members of a oneof: " + set.join(", "));',
|
|
297787
297821
|
" });",
|
|
297788
297822
|
"}",
|
|
297823
|
+
// Wire-conformance helpers for response metadata/trailers ({ key, value }
|
|
297824
|
+
// PropertyLists; each/has/one guarded so the script stays inert on runtimes
|
|
297825
|
+
// or harnesses that do not surface them).
|
|
297826
|
+
'function grpcEachMeta(list, cb) { if (list && typeof list.each === "function") list.each(cb); }',
|
|
297827
|
+
'function grpcMetaOne(list, key) { try { if (list && typeof list.has === "function" && list.has(key) && typeof list.one === "function") return list.one(key); } catch (error) { /* absent */ } return null; }',
|
|
297828
|
+
"function grpcCheckMetaPairs(list, label) {",
|
|
297829
|
+
" grpcEachMeta(list, function (entry) {",
|
|
297830
|
+
" if (!entry || entry.disabled) return;",
|
|
297831
|
+
' var key = entry.key === undefined || entry.key === null ? "" : String(entry.key);',
|
|
297832
|
+
' var value = entry.value === undefined || entry.value === null ? "" : String(entry.value);',
|
|
297833
|
+
' if (!/^[0-9a-z_.-]+$/.test(key)) pm.expect.fail(label + " key " + JSON.stringify(key) + " must be a lowercase gRPC metadata key ([0-9a-z_.-])");',
|
|
297834
|
+
' if (/-bin$/.test(key)) { if (!/^[A-Za-z0-9+/=]*\\s*$/.test(value)) pm.expect.fail(label + " " + key + " must carry base64 data but was " + JSON.stringify(value)); }',
|
|
297835
|
+
' else if (!/^[\\x20-\\x7e]*$/.test(value)) pm.expect.fail(label + " " + key + " value must be printable ASCII (gRPC ASCII-Value)");',
|
|
297836
|
+
" });",
|
|
297837
|
+
"}",
|
|
297838
|
+
// grpc-status-details-bin carries a base64-encoded google.rpc.Status protobuf
|
|
297839
|
+
// (the gRPC richer-error model). The sandbox has no protobuf runtime, so the
|
|
297840
|
+
// script walks the protobuf wire format directly: base64 -> bytes -> tagged
|
|
297841
|
+
// fields (varint / 64-bit / length-delimited / 32-bit). Groups (wire types
|
|
297842
|
+
// 3/4) are proto2-deprecated and never emitted by google.rpc.Status.
|
|
297843
|
+
"function grpcB64Bytes(value) {",
|
|
297844
|
+
' var clean = String(value).replace(/\\s+/g, "").replace(/-/g, "+").replace(/_/g, "/");',
|
|
297845
|
+
" if (clean.length % 4 === 1 || !/^[A-Za-z0-9+/]*={0,2}$/.test(clean)) return null;",
|
|
297846
|
+
' var alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";',
|
|
297847
|
+
' var unpadded = clean.replace(/=+$/, ""); var bytes = []; var buffer = 0; var bits = 0;',
|
|
297848
|
+
" for (var i = 0; i < unpadded.length; i++) {",
|
|
297849
|
+
" var idx = alphabet.indexOf(unpadded.charAt(i));",
|
|
297850
|
+
" if (idx === -1) return null;",
|
|
297851
|
+
" buffer = (buffer << 6) | idx; bits += 6;",
|
|
297852
|
+
" if (bits >= 8) { bits -= 8; bytes.push((buffer >> bits) & 0xff); }",
|
|
297853
|
+
" }",
|
|
297854
|
+
" return bytes;",
|
|
297855
|
+
"}",
|
|
297856
|
+
"function grpcPbVarint(bytes, pos) {",
|
|
297857
|
+
" var value = 0; var shift = 0;",
|
|
297858
|
+
" while (pos < bytes.length) {",
|
|
297859
|
+
" var b = bytes[pos]; pos += 1;",
|
|
297860
|
+
" value += (b & 0x7f) * Math.pow(2, shift); shift += 7;",
|
|
297861
|
+
" if ((b & 0x80) === 0) return value > 9007199254740991 ? null : { value: value, pos: pos };",
|
|
297862
|
+
" if (shift > 63) return null;",
|
|
297863
|
+
" }",
|
|
297864
|
+
" return null;",
|
|
297865
|
+
"}",
|
|
297866
|
+
"function grpcPbFields(bytes) {",
|
|
297867
|
+
" var pos = 0; var fields = [];",
|
|
297868
|
+
" while (pos < bytes.length) {",
|
|
297869
|
+
" var tag = grpcPbVarint(bytes, pos); if (!tag) return null;",
|
|
297870
|
+
" pos = tag.pos;",
|
|
297871
|
+
" var fieldNumber = Math.floor(tag.value / 8); var wire = tag.value % 8;",
|
|
297872
|
+
" if (fieldNumber < 1) return null;",
|
|
297873
|
+
" if (wire === 0) { var varint = grpcPbVarint(bytes, pos); if (!varint) return null; fields.push({ field: fieldNumber, wire: 0, value: varint.value }); pos = varint.pos; }",
|
|
297874
|
+
" else if (wire === 1) { if (pos + 8 > bytes.length) return null; fields.push({ field: fieldNumber, wire: 1 }); pos += 8; }",
|
|
297875
|
+
" else if (wire === 2) { var len = grpcPbVarint(bytes, pos); if (!len) return null; pos = len.pos; if (pos + len.value > bytes.length) return null; fields.push({ field: fieldNumber, wire: 2, bytes: bytes.slice(pos, pos + len.value) }); pos += len.value; }",
|
|
297876
|
+
" else if (wire === 5) { if (pos + 4 > bytes.length) return null; fields.push({ field: fieldNumber, wire: 5 }); pos += 4; }",
|
|
297877
|
+
" else return null;",
|
|
297878
|
+
" }",
|
|
297879
|
+
" return fields;",
|
|
297880
|
+
"}",
|
|
297881
|
+
"function grpcCheckStatusDetailsBin(entry) {",
|
|
297882
|
+
' if (!entry || entry.value === undefined || entry.value === null || String(entry.value) === "") return;',
|
|
297883
|
+
" var detailBytes = grpcB64Bytes(entry.value);",
|
|
297884
|
+
' if (!detailBytes) { pm.expect.fail("grpc-status-details-bin trailer must carry base64-encoded bytes but was " + JSON.stringify(String(entry.value))); return; }',
|
|
297885
|
+
" var statusFields = grpcPbFields(detailBytes);",
|
|
297886
|
+
' if (!statusFields) { pm.expect.fail("grpc-status-details-bin does not decode as a protobuf wire message (google.rpc.Status)"); return; }',
|
|
297887
|
+
" statusFields.forEach(function (statusField) {",
|
|
297888
|
+
" if (statusField.field === 1) {",
|
|
297889
|
+
' if (statusField.wire !== 0) { pm.expect.fail("google.rpc.Status.code (field 1) in grpc-status-details-bin must be varint-encoded"); return; }',
|
|
297890
|
+
' if (statusField.value < 0 || statusField.value > 16) pm.expect.fail("google.rpc.Status.code in grpc-status-details-bin must be a canonical gRPC code 0-16 but was " + statusField.value);',
|
|
297891
|
+
' else if (statusField.value !== pm.response.code) pm.expect.fail("google.rpc.Status.code in grpc-status-details-bin (" + statusField.value + ") disagrees with the reported status code (" + pm.response.code + ")");',
|
|
297892
|
+
" } else if (statusField.field === 2) {",
|
|
297893
|
+
' if (statusField.wire !== 2) pm.expect.fail("google.rpc.Status.message (field 2) in grpc-status-details-bin must be length-delimited");',
|
|
297894
|
+
" } else if (statusField.field === 3) {",
|
|
297895
|
+
' if (statusField.wire !== 2) { pm.expect.fail("google.rpc.Status.details (field 3) in grpc-status-details-bin must be length-delimited google.protobuf.Any messages"); return; }',
|
|
297896
|
+
" var anyFields = grpcPbFields(statusField.bytes);",
|
|
297897
|
+
' if (!anyFields) { pm.expect.fail("google.rpc.Status.details entry in grpc-status-details-bin is not a decodable google.protobuf.Any message"); return; }',
|
|
297898
|
+
" var hasTypeUrl = anyFields.some(function (anyField) { return anyField.field === 1 && anyField.wire === 2 && anyField.bytes.length > 0; });",
|
|
297899
|
+
' if (!hasTypeUrl) pm.expect.fail("google.protobuf.Any in grpc-status-details-bin details must carry a non-empty type_url (field 1)");',
|
|
297900
|
+
" }",
|
|
297901
|
+
" });",
|
|
297902
|
+
"}",
|
|
297789
297903
|
`pm.test('gRPC status is OK for ' + grpcSpec.methodPath, function () {`,
|
|
297790
297904
|
' pm.expect(pm.response.code, "gRPC call for " + grpcSpec.methodPath + " returned " + grpcStatusName() + " (" + pm.response.code + ")").to.eql(0);',
|
|
297791
297905
|
"});",
|
|
297906
|
+
// Metadata/trailer wire conformance runs on every terminal status (error
|
|
297907
|
+
// statuses carry trailers too, so this is not gated on code === 0).
|
|
297908
|
+
`pm.test('gRPC response metadata and trailers conform to gRPC wire rules for ' + grpcSpec.methodPath, function () {`,
|
|
297909
|
+
' grpcCheckMetaPairs(pm.response.metadata, "gRPC response metadata");',
|
|
297910
|
+
' grpcCheckMetaPairs(pm.response.trailers, "gRPC response trailer");',
|
|
297911
|
+
' var contentType = grpcMetaOne(pm.response.metadata, "content-type");',
|
|
297912
|
+
' if (contentType && contentType.value !== undefined && contentType.value !== null && !/^application\\/grpc/.test(String(contentType.value))) pm.expect.fail("gRPC response content-type metadata must be application/grpc[+proto|+json] but was " + contentType.value);',
|
|
297913
|
+
' var echoedStatus = grpcMetaOne(pm.response.trailers, "grpc-status");',
|
|
297914
|
+
" if (echoedStatus) {",
|
|
297915
|
+
" var echoedCode = Number(echoedStatus.value);",
|
|
297916
|
+
' if (!Number.isInteger(echoedCode) || echoedCode < 0 || echoedCode > 16) pm.expect.fail("grpc-status trailer must be a canonical gRPC code 0-16 but was " + JSON.stringify(echoedStatus.value));',
|
|
297917
|
+
' else if (echoedCode !== pm.response.code) pm.expect.fail("grpc-status trailer (" + echoedCode + ") disagrees with the reported status code (" + pm.response.code + ")");',
|
|
297918
|
+
" }",
|
|
297919
|
+
' grpcCheckStatusDetailsBin(grpcMetaOne(pm.response.trailers, "grpc-status-details-bin"));',
|
|
297920
|
+
"});",
|
|
297792
297921
|
// Terminal-response-message expectation. unary and client-streaming RPCs
|
|
297793
297922
|
// return exactly one terminal response message; server/bidi streaming may
|
|
297794
297923
|
// legitimately return ZERO messages on an OK stream, so no minimum is
|
|
@@ -297810,6 +297939,26 @@ function createGrpcScript(spec) {
|
|
|
297810
297939
|
' if (grpcSpec.responseShape) grpcCheckShape(message, grpcSpec.responseShape, grpcSpec.responseType + ".");',
|
|
297811
297940
|
" if (grpcSpec.responseIsRpcStatus) grpcCheckRpcStatus(message, grpcSpec.responseType);",
|
|
297812
297941
|
"});"
|
|
297942
|
+
] : [],
|
|
297943
|
+
// Every streamed message is validated, not only the terminal one that
|
|
297944
|
+
// pm.response.json() returns; pm.response.messages is the full received
|
|
297945
|
+
// stream. Server/bidi only: for unary/client the single terminal message
|
|
297946
|
+
// is already covered above.
|
|
297947
|
+
...(spec.stream === "server" || spec.stream === "bidi") && (spec.hasResponseShape || spec.responseIsRpcStatus) ? [
|
|
297948
|
+
`pm.test('gRPC streamed response messages each match ' + grpcSpec.responseType, function () {`,
|
|
297949
|
+
" if (pm.response.code !== 0) return;",
|
|
297950
|
+
" var list = pm.response.messages;",
|
|
297951
|
+
' if (!list || typeof list.each !== "function") return;',
|
|
297952
|
+
" var messageIndex = 0;",
|
|
297953
|
+
" list.each(function (member) {",
|
|
297954
|
+
" var data = member && member.data !== undefined ? member.data : member;",
|
|
297955
|
+
' var label = grpcSpec.responseType + "[message " + messageIndex + "]";',
|
|
297956
|
+
" messageIndex += 1;",
|
|
297957
|
+
' if (jsonTypeOf(data) !== "object") { pm.expect.fail("gRPC streamed response " + label + " is not a decodable message object but " + jsonTypeOf(data)); return; }',
|
|
297958
|
+
' if (grpcSpec.responseShape) grpcCheckShape(data, grpcSpec.responseShape, label + ".");',
|
|
297959
|
+
" if (grpcSpec.responseIsRpcStatus) grpcCheckRpcStatus(data, label);",
|
|
297960
|
+
" });",
|
|
297961
|
+
"});"
|
|
297813
297962
|
] : []
|
|
297814
297963
|
];
|
|
297815
297964
|
}
|
|
@@ -303606,6 +303755,7 @@ function createSoapScript(operation, warnings = []) {
|
|
|
303606
303755
|
};
|
|
303607
303756
|
const responseRegex = operation.expectedResponseElement ? elementPresenceRegex(operation.expectedResponseElement) : "";
|
|
303608
303757
|
const mediaType = operation.soapVersion === "1.2" ? "application/soap+xml" : "text/xml";
|
|
303758
|
+
const envelopeNs = operation.soapVersion === "1.2" ? "http://www.w3.org/2003/05/soap-envelope" : "http://schemas.xmlsoap.org/soap/envelope/";
|
|
303609
303759
|
const faultStatusLine = operation.soapVersion === "1.2" ? ' if (faulted && code !== 500 && code !== 400) pm.expect.fail("SOAP 1.2 Faults ride HTTP 500, or 400 for env:Sender faults (SOAP 1.2 Part 2 HTTP binding); got HTTP " + code);' : ' if (faulted && code !== 500) pm.expect.fail("SOAP 1.1 Faults must ride HTTP 500 (WS-I Basic Profile R1126); got HTTP " + code);';
|
|
303610
303760
|
const lines = [
|
|
303611
303761
|
`var soap = JSON.parse(${JSON.stringify(JSON.stringify(meta))});`,
|
|
@@ -303628,6 +303778,11 @@ function createSoapScript(operation, warnings = []) {
|
|
|
303628
303778
|
' pm.expect(bodyText, "response body is not a SOAP envelope").to.match(matchTag("Envelope"));',
|
|
303629
303779
|
"});",
|
|
303630
303780
|
"",
|
|
303781
|
+
`pm.test('SOAP Envelope namespace matches SOAP ${operation.soapVersion}', function () {`,
|
|
303782
|
+
' if (!matchTag("Envelope").test(bodyText)) return;',
|
|
303783
|
+
` pm.expect(bodyText.indexOf(${JSON.stringify(envelopeNs)}) !== -1, "SOAP ${operation.soapVersion} envelopes must declare the ${envelopeNs} namespace").to.equal(true);`,
|
|
303784
|
+
"});",
|
|
303785
|
+
"",
|
|
303631
303786
|
"pm.test('SOAP Body element is present', function () {",
|
|
303632
303787
|
' pm.expect(bodyText, "SOAP envelope has no Body element").to.match(matchTag("Body"));',
|
|
303633
303788
|
"});",
|
|
@@ -303645,7 +303800,33 @@ function createSoapScript(operation, warnings = []) {
|
|
|
303645
303800
|
" var code = pm.response.code;",
|
|
303646
303801
|
faultStatusLine,
|
|
303647
303802
|
' if (!faulted && code === 500) pm.expect.fail("HTTP 500 from a SOAP endpoint must carry a SOAP Fault in the body");',
|
|
303648
|
-
"});"
|
|
303803
|
+
"});",
|
|
303804
|
+
// Fault well-formedness (diagnostic companion to the Fault-absence test: a
|
|
303805
|
+
// faulted run already fails, this pinpoints a malformed Fault). SOAP 1.1
|
|
303806
|
+
// section 4.4 requires faultcode + faultstring children; SOAP 1.2 Part 1
|
|
303807
|
+
// section 5.4 requires Code/Value + Reason/Text and pins the top-level
|
|
303808
|
+
// Value QName to the five defined fault codes.
|
|
303809
|
+
...operation.soapVersion === "1.2" ? [
|
|
303810
|
+
"",
|
|
303811
|
+
"pm.test('SOAP Fault is well-formed for SOAP 1.2', function () {",
|
|
303812
|
+
' if (!matchTag("Fault").test(bodyText)) return;',
|
|
303813
|
+
' if (!matchTag("Code").test(bodyText)) { pm.expect.fail("a SOAP 1.2 Fault must carry an env:Code child (SOAP 1.2 Part 1 section 5.4)"); return; }',
|
|
303814
|
+
' if (!matchTag("Value").test(bodyText)) { pm.expect.fail("a SOAP 1.2 Fault Code must carry an env:Value child (SOAP 1.2 Part 1 section 5.4.1)"); return; }',
|
|
303815
|
+
' if (!matchTag("Reason").test(bodyText)) pm.expect.fail("a SOAP 1.2 Fault must carry an env:Reason child (SOAP 1.2 Part 1 section 5.4)");',
|
|
303816
|
+
' else if (!matchTag("Text").test(bodyText)) pm.expect.fail("a SOAP 1.2 Fault Reason must carry an env:Text child (SOAP 1.2 Part 1 section 5.4.2)");',
|
|
303817
|
+
' var faultValue = (bodyText.match(/<(?:[A-Za-z_][\\w.-]*:)?Value[^>]*>([\\s\\S]*?)<\\//) || [])[1] || "";',
|
|
303818
|
+
' var faultLocal = faultValue.trim().split(":").pop();',
|
|
303819
|
+
' var faultCodes = ["VersionMismatch", "MustUnderstand", "DataEncodingUnknown", "Sender", "Receiver"];',
|
|
303820
|
+
' if (faultLocal && faultCodes.indexOf(faultLocal) === -1) pm.expect.fail("the SOAP 1.2 Fault Code Value must be one of " + faultCodes.join(", ") + " (SOAP 1.2 Part 1 table 4); got " + faultValue.trim());',
|
|
303821
|
+
"});"
|
|
303822
|
+
] : [
|
|
303823
|
+
"",
|
|
303824
|
+
"pm.test('SOAP Fault is well-formed for SOAP 1.1', function () {",
|
|
303825
|
+
' if (!matchTag("Fault").test(bodyText)) return;',
|
|
303826
|
+
' if (!matchTag("faultcode").test(bodyText)) pm.expect.fail("a SOAP 1.1 Fault must carry a faultcode child (SOAP 1.1 section 4.4)");',
|
|
303827
|
+
' if (!matchTag("faultstring").test(bodyText)) pm.expect.fail("a SOAP 1.1 Fault must carry a faultstring child (SOAP 1.1 section 4.4)");',
|
|
303828
|
+
"});"
|
|
303829
|
+
]
|
|
303649
303830
|
];
|
|
303650
303831
|
if (responseRegex) {
|
|
303651
303832
|
lines.push(
|
|
@@ -303822,7 +304003,8 @@ function wsBindingKeyValues(channel) {
|
|
|
303822
304003
|
const value = asRecord15(wsBinding.value()) ?? {};
|
|
303823
304004
|
return {
|
|
303824
304005
|
headers: bindingKeyValues(value.headers),
|
|
303825
|
-
queryParams: bindingKeyValues(value.query)
|
|
304006
|
+
queryParams: bindingKeyValues(value.query),
|
|
304007
|
+
binding: value
|
|
303826
304008
|
};
|
|
303827
304009
|
}
|
|
303828
304010
|
function collectMqttInfo(channel, servers, messagesRaw, documentJson) {
|
|
@@ -303896,6 +304078,8 @@ function messageDescriptor2(message, warnings, channelId, defaultContentType, re
|
|
|
303896
304078
|
const replySchema = replyByMessageId.get(message.id());
|
|
303897
304079
|
const ackSchema = xAckSchema ?? replySchema;
|
|
303898
304080
|
const ackSource = xAckSchema ? "x-ack" : replySchema ? "reply" : void 0;
|
|
304081
|
+
const correlationRaw = asRecord15(rawMessage.correlationId)?.location;
|
|
304082
|
+
const correlationLocation = typeof correlationRaw === "string" ? correlationRaw : void 0;
|
|
303899
304083
|
const examples = message.examples().all().filter((example) => example.hasPayload());
|
|
303900
304084
|
const hasExample = examples.length > 0;
|
|
303901
304085
|
let sample;
|
|
@@ -303917,6 +304101,7 @@ function messageDescriptor2(message, warnings, channelId, defaultContentType, re
|
|
|
303917
304101
|
payloadSchema,
|
|
303918
304102
|
ackSchema,
|
|
303919
304103
|
ackSource,
|
|
304104
|
+
correlationLocation,
|
|
303920
304105
|
sample,
|
|
303921
304106
|
hasExample,
|
|
303922
304107
|
contentKind,
|
|
@@ -303929,6 +304114,7 @@ function channelDescriptor(channel, document2, documentJson, defaultContentType,
|
|
|
303929
304114
|
const servers = resolveServers(channel, document2);
|
|
303930
304115
|
const messagesRaw = channel.messages().all();
|
|
303931
304116
|
const transport = detectTransport(channel, servers, messagesRaw, documentJson, warnings);
|
|
304117
|
+
const parameterNames = Object.keys(asRecord15(channel.json().parameters) ?? {}).sort();
|
|
303932
304118
|
const serverUrl = servers.find((server) => server.url())?.url() || options.endpointUrl?.trim() || "";
|
|
303933
304119
|
if (!serverUrl) {
|
|
303934
304120
|
warnings.push(`ASYNCAPI_NO_SERVER_URL: channel ${channel.id()} has no resolvable server url; the generated request url is derived from the channel address only and must be completed before use`);
|
|
@@ -303948,6 +304134,7 @@ function channelDescriptor(channel, document2, documentJson, defaultContentType,
|
|
|
303948
304134
|
headers: [],
|
|
303949
304135
|
queryParams: [],
|
|
303950
304136
|
mqtt: collectMqttInfo(channel, servers, messagesRaw, documentJson),
|
|
304137
|
+
parameterNames,
|
|
303951
304138
|
messages,
|
|
303952
304139
|
warnings
|
|
303953
304140
|
};
|
|
@@ -303962,11 +304149,12 @@ function channelDescriptor(channel, document2, documentJson, defaultContentType,
|
|
|
303962
304149
|
queryParams: [],
|
|
303963
304150
|
socketioNamespace: address.startsWith("/") ? address : `/${address}`,
|
|
303964
304151
|
socketioPath: DEFAULT_SOCKETIO_PATH,
|
|
304152
|
+
parameterNames,
|
|
303965
304153
|
messages,
|
|
303966
304154
|
warnings
|
|
303967
304155
|
};
|
|
303968
304156
|
}
|
|
303969
|
-
const { headers, queryParams } = wsBindingKeyValues(channel);
|
|
304157
|
+
const { headers, queryParams, binding } = wsBindingKeyValues(channel);
|
|
303970
304158
|
return {
|
|
303971
304159
|
id: channel.id(),
|
|
303972
304160
|
address,
|
|
@@ -303974,6 +304162,8 @@ function channelDescriptor(channel, document2, documentJson, defaultContentType,
|
|
|
303974
304162
|
url: joinUrl(serverUrl, address),
|
|
303975
304163
|
headers,
|
|
303976
304164
|
queryParams,
|
|
304165
|
+
parameterNames,
|
|
304166
|
+
wsBinding: binding,
|
|
303977
304167
|
messages,
|
|
303978
304168
|
warnings
|
|
303979
304169
|
};
|
|
@@ -304411,14 +304601,68 @@ function collectMessageNodeIds(node, ids, path10) {
|
|
|
304411
304601
|
if (record) collectMessageNodeIds(record, ids, `${path10}/${i}`);
|
|
304412
304602
|
});
|
|
304413
304603
|
}
|
|
304604
|
+
function validateChannelParameters(channel, warnings) {
|
|
304605
|
+
const declared = new Set(channel.parameterNames ?? []);
|
|
304606
|
+
const used = /* @__PURE__ */ new Set();
|
|
304607
|
+
for (const match of channel.address.matchAll(/\{([^{}]*)\}/g)) {
|
|
304608
|
+
const name = match[1] ?? "";
|
|
304609
|
+
if (!name) {
|
|
304610
|
+
warnings.push(`ASYNCAPI_CHANNEL_PARAMETER_INVALID: channel ${channel.id} address ${channel.address} contains an empty {} parameter expression`);
|
|
304611
|
+
continue;
|
|
304612
|
+
}
|
|
304613
|
+
used.add(name);
|
|
304614
|
+
if (!declared.has(name)) {
|
|
304615
|
+
warnings.push(`ASYNCAPI_CHANNEL_PARAMETER_UNDECLARED: channel ${channel.id} address parameter {${name}} has no entry in the channel parameters object; AsyncAPI requires every address parameter to be declared`);
|
|
304616
|
+
}
|
|
304617
|
+
}
|
|
304618
|
+
for (const name of declared) {
|
|
304619
|
+
if (!used.has(name)) {
|
|
304620
|
+
warnings.push(`ASYNCAPI_CHANNEL_PARAMETER_UNUSED: channel ${channel.id} declares parameter ${name} that never appears in the channel address ${channel.address}`);
|
|
304621
|
+
}
|
|
304622
|
+
}
|
|
304623
|
+
}
|
|
304624
|
+
var CORRELATION_LOCATION_GRAMMAR = /^\$message\.(header|payload)#(\/(?:[^~/]|~[01])*)+$/;
|
|
304625
|
+
function validateCorrelationLocation(channelId, message, warnings) {
|
|
304626
|
+
if (message.correlationLocation === void 0) return;
|
|
304627
|
+
if (!CORRELATION_LOCATION_GRAMMAR.test(message.correlationLocation)) {
|
|
304628
|
+
warnings.push(
|
|
304629
|
+
`ASYNCAPI_CORRELATION_LOCATION_INVALID: message ${message.id} on channel ${channelId} correlationId location ${JSON.stringify(message.correlationLocation)} is not a valid AsyncAPI runtime expression ($message.header#/<pointer> or $message.payload#/<pointer>)`
|
|
304630
|
+
);
|
|
304631
|
+
}
|
|
304632
|
+
}
|
|
304633
|
+
var SOCKETIO_RESERVED_EVENTS = /* @__PURE__ */ new Set(["connect", "connect_error", "disconnect", "disconnecting", "newListener", "removeListener"]);
|
|
304634
|
+
function validateWsBinding(channel, warnings) {
|
|
304635
|
+
const binding = channel.wsBinding;
|
|
304636
|
+
if (!binding) return;
|
|
304637
|
+
const method = binding.method;
|
|
304638
|
+
if (method !== void 0 && (typeof method !== "string" || method !== "GET" && method !== "POST")) {
|
|
304639
|
+
warnings.push(`ASYNCAPI_WS_BINDING_INVALID: channel ${channel.id} ws binding method must be "GET" or "POST" but was ${JSON.stringify(method)}`);
|
|
304640
|
+
}
|
|
304641
|
+
for (const key of ["query", "headers"]) {
|
|
304642
|
+
const schema = binding[key];
|
|
304643
|
+
if (schema === void 0) continue;
|
|
304644
|
+
const record = asRecord16(schema);
|
|
304645
|
+
if (!record || record.type !== void 0 && record.type !== "object") {
|
|
304646
|
+
warnings.push(`ASYNCAPI_WS_BINDING_INVALID: channel ${channel.id} ws binding ${key} must be a Schema Object of type object`);
|
|
304647
|
+
}
|
|
304648
|
+
}
|
|
304649
|
+
}
|
|
304414
304650
|
function instrumentAsyncApiCollection(collection, index) {
|
|
304415
304651
|
const warnings = [
|
|
304416
304652
|
...index.warnings,
|
|
304417
304653
|
...index.channels.flatMap((channel) => channel.warnings)
|
|
304418
304654
|
];
|
|
304419
304655
|
for (const channel of index.channels) {
|
|
304656
|
+
validateChannelParameters(channel, warnings);
|
|
304420
304657
|
if (channel.transport === "mqtt") validateMqttChannel(channel, warnings);
|
|
304658
|
+
if (channel.transport === "ws-raw") validateWsBinding(channel, warnings);
|
|
304421
304659
|
for (const message of channel.messages) {
|
|
304660
|
+
if (channel.transport === "socketio" && SOCKETIO_RESERVED_EVENTS.has(message.eventName)) {
|
|
304661
|
+
warnings.push(
|
|
304662
|
+
`ASYNCAPI_SOCKETIO_RESERVED_EVENT: message ${message.id} on channel ${channel.id} uses the reserved Socket.IO event name "${message.eventName}"; reserved lifecycle events cannot be emitted or received as application events`
|
|
304663
|
+
);
|
|
304664
|
+
}
|
|
304665
|
+
validateCorrelationLocation(channel.id, message, warnings);
|
|
304422
304666
|
validateMessage(index, channel.id, message, warnings);
|
|
304423
304667
|
}
|
|
304424
304668
|
}
|
|
@@ -304596,6 +304840,7 @@ function toolDescriptor(tool, warnings) {
|
|
|
304596
304840
|
description: typeof tool.description === "string" ? tool.description : void 0,
|
|
304597
304841
|
inputSchema,
|
|
304598
304842
|
outputSchema: asRecord17(tool.outputSchema) ?? void 0,
|
|
304843
|
+
annotations: asRecord17(tool.annotations) ?? void 0,
|
|
304599
304844
|
sampleArguments: inputSchema ? sampleFromSchema2(inputSchema, 0) : {},
|
|
304600
304845
|
warnings: toolWarnings
|
|
304601
304846
|
};
|
|
@@ -304796,10 +305041,41 @@ function validateServer(server, warnings) {
|
|
|
304796
305041
|
}
|
|
304797
305042
|
}
|
|
304798
305043
|
}
|
|
305044
|
+
var TOOL_ANNOTATION_BOOLEAN_HINTS = ["readOnlyHint", "destructiveHint", "idempotentHint", "openWorldHint"];
|
|
305045
|
+
function validateToolAnnotations(tool, warnings) {
|
|
305046
|
+
if (!tool.annotations) return;
|
|
305047
|
+
for (const hint of TOOL_ANNOTATION_BOOLEAN_HINTS) {
|
|
305048
|
+
const value = tool.annotations[hint];
|
|
305049
|
+
if (value !== void 0 && typeof value !== "boolean") {
|
|
305050
|
+
warnings.push(`MCP_TOOL_ANNOTATION_INVALID: tool ${tool.name} annotations.${hint} must be a boolean (MCP ToolAnnotations); got ${JSON.stringify(value)}`);
|
|
305051
|
+
}
|
|
305052
|
+
}
|
|
305053
|
+
if (tool.annotations.title !== void 0 && typeof tool.annotations.title !== "string") {
|
|
305054
|
+
warnings.push(`MCP_TOOL_ANNOTATION_INVALID: tool ${tool.name} annotations.title must be a string (MCP ToolAnnotations); got ${JSON.stringify(tool.annotations.title)}`);
|
|
305055
|
+
}
|
|
305056
|
+
}
|
|
305057
|
+
function validateToolOutputSchema(index, tool, warnings) {
|
|
305058
|
+
if (!tool.outputSchema) return;
|
|
305059
|
+
const declaredType = tool.outputSchema.type;
|
|
305060
|
+
if (declaredType !== void 0 && declaredType !== "object") {
|
|
305061
|
+
warnings.push(`MCP_TOOL_OUTPUT_SCHEMA_INVALID: tool ${tool.name} outputSchema type is ${JSON.stringify(declaredType)}; the MCP specification requires tool output schemas to describe the structuredContent object`);
|
|
305062
|
+
}
|
|
305063
|
+
const packed = packSchema(index.documentJson, tool.outputSchema, "3.0", "response");
|
|
305064
|
+
if (packed.unsupported) {
|
|
305065
|
+
const code = isSchemaGraphOverflow(packed) ? "MCP_SCHEMA_NOT_COMPILED" : "MCP_TOOL_OUTPUT_SCHEMA_NOT_VALIDATED";
|
|
305066
|
+
warnings.push(`${code}: tool ${tool.name} outputSchema is not validated (${packed.unsupported})`);
|
|
305067
|
+
return;
|
|
305068
|
+
}
|
|
305069
|
+
if (!compileSchemaValidator(packed.schema)) {
|
|
305070
|
+
warnings.push(`MCP_TOOL_OUTPUT_SCHEMA_NOT_VALIDATED: tool ${tool.name} outputSchema could not be compiled to a validator`);
|
|
305071
|
+
}
|
|
305072
|
+
}
|
|
304799
305073
|
function validateTool(index, tool, warnings) {
|
|
304800
305074
|
if (!TOOL_NAME_RE.test(tool.name)) {
|
|
304801
305075
|
warnings.push(`MCP_TOOL_NAME_UNCONVENTIONAL: tool name "${tool.name}" is outside the conventional [A-Za-z0-9_./-]{1,128} identifier set`);
|
|
304802
305076
|
}
|
|
305077
|
+
validateToolAnnotations(tool, warnings);
|
|
305078
|
+
validateToolOutputSchema(index, tool, warnings);
|
|
304803
305079
|
if (!tool.inputSchema) return;
|
|
304804
305080
|
const declaredType = tool.inputSchema.type;
|
|
304805
305081
|
if (declaredType !== void 0 && declaredType !== "object") {
|