Skip to content

Commit 4eb9bce

Browse files
authored
Merge pull request #35 from JaredCE/deal-with-XML
Deal with xml
2 parents 32fbe74 + de43222 commit 4eb9bce

File tree

8 files changed

+136
-47
lines changed

8 files changed

+136
-47
lines changed

package-lock.json

Lines changed: 33 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "arazzo-runner",
3-
"version": "0.0.12",
3+
"version": "0.0.13",
44
"description": "A runner to run through Arazzo Document workflows",
55
"main": "index.js",
66
"scripts": {
@@ -40,6 +40,7 @@
4040
"@swaggerexpert/arazzo-runtime-expression": "^1.0.1",
4141
"@swaggerexpert/json-pointer": "^2.10.2",
4242
"ajv": "^8.17.1",
43+
"fast-xml-parser": "^5.3.3",
4344
"jsonpath": "^1.1.1",
4445
"openapi-params": "^0.0.5",
4546
"openapi-server-url-templating": "^1.3.0",

src/Arazzo.js

Lines changed: 45 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -348,9 +348,9 @@ class Arazzo extends Document {
348348
} else {
349349
this.logger.verbose("Using fetch call");
350350

351-
this.logger.verbose(`url: ${url}`);
352-
this.logger.verbose(`method: ${options.method}`);
353-
this.logger.verbose("headers:");
351+
this.logger.verbose(`request url: ${url}`);
352+
this.logger.verbose(`request method: ${options.method}`);
353+
this.logger.verbose("request headers:");
354354
for (const [key, value] of options.headers.entries()) {
355355
this.logger.verbose(`${key}: ${value}`);
356356
}
@@ -360,6 +360,12 @@ class Arazzo extends Document {
360360
response = await fetch(url, options);
361361
}
362362

363+
this.logger.verbose(`received StatusCode: ${response.status}`);
364+
this.logger.verbose(`received headers:`);
365+
for (const [key, value] of response.headers.entries()) {
366+
this.logger.verbose(`${key}: ${value}`);
367+
}
368+
363369
if (response.headers.has("retry-after")) {
364370
const retryAfter = parseRetryAfter(response.headers.get("retry-after"));
365371
if (retryAfter !== null) {
@@ -504,32 +510,48 @@ class Arazzo extends Document {
504510
* @param {*} response
505511
*/
506512
async dealWithResponse(response) {
513+
if (
514+
response.headers.has("Content-Type") &&
515+
response.headers.get("Content-Type") === "application/json"
516+
) {
517+
const json = await response?.json().catch((err) => {
518+
this.logger.error(
519+
`Error trying to resolve ${this.step.stepId} outputs`,
520+
);
521+
throw new Error(err);
522+
});
523+
524+
this.expression.addToContext("response.body", json);
525+
} else {
526+
const body = await response.body;
527+
528+
this.expression.addToContext("response.body", body);
529+
}
530+
507531
this.doNotProcessStep = false;
508532
this.alreadyProcessingOnFailure = false;
509533

510534
if (this.step.successCriteria) {
511-
if (this.step.successCriteria) {
512-
const passedSuccessCriteria = this.hasPassedSuccessCriteria();
513-
514-
if (passedSuccessCriteria) {
515-
this.logger.success("All criteria checks passed");
516-
if (this.currentRetryRule) {
517-
if (this.retryContext.doNotDeleteRetryLimits) {
518-
this.retryLimits[this.currentRetryRule] = 0;
519-
this.logger.notice("Retries stopped");
520-
}
535+
const passedSuccessCriteria = this.hasPassedSuccessCriteria();
536+
537+
if (passedSuccessCriteria) {
538+
this.logger.success("All criteria checks passed");
539+
if (this.currentRetryRule) {
540+
if (this.retryContext.doNotDeleteRetryLimits) {
541+
this.retryLimits[this.currentRetryRule] = 0;
542+
this.logger.notice("Retries stopped");
521543
}
544+
}
522545

523-
await this.dealWithPassedRule(response);
546+
await this.dealWithPassedRule(response);
547+
} else {
548+
this.logger.error("Not all criteria checks passed");
549+
if (this.step.onFailure) {
550+
await this.dealWithFailedRule();
524551
} else {
525-
this.logger.error("Not all criteria checks passed");
526-
if (this.step.onFailure) {
527-
await this.dealWithFailedRule();
528-
} else {
529-
throw new Error(
530-
`${this.step.stepId} step of the ${this.workflow.workflowId} workflow failed the successCriteria`,
531-
);
532-
}
552+
throw new Error(
553+
`${this.step.stepId} step of the ${this.workflow.workflowId} workflow failed the successCriteria`,
554+
);
533555
}
534556
}
535557
} else {
@@ -843,14 +865,7 @@ class Arazzo extends Document {
843865
* @private
844866
* @param {*} response
845867
*/
846-
async dealWithStepOutputs(response) {
847-
const json = await response?.json().catch((err) => {
848-
this.logger.error(`Error trying to resolve ${this.step.stepId} outputs`);
849-
throw new Error(err);
850-
});
851-
852-
this.expression.addToContext("response.body", json);
853-
868+
async dealWithStepOutputs() {
854869
const outputs = {};
855870
for (const key in this.step.outputs) {
856871
const value = this.expression.resolveExpression(this.step.outputs[key]);

src/Expression.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,18 @@ class Expression {
242242
// return expression;
243243
// }
244244

245+
/**
246+
* @private
247+
* @param {*} value
248+
* @returns {boolean}
249+
*/
250+
isXML(value) {
251+
return (
252+
typeof value === "string" &&
253+
(value.trim().startsWith("<?xml") || value.trim().startsWith("<"))
254+
);
255+
}
256+
245257
/**
246258
* Resolves a runtime expression to its value in the context
247259
* @public
@@ -470,6 +482,18 @@ class Expression {
470482
throw new Error(`Context path '${normalised}' not found`);
471483
}
472484

485+
// NEW: Check if contextData is XML
486+
if (this.isXML(contextData)) {
487+
const { XMLParser } = require("fast-xml-parser");
488+
const parser = new XMLParser({
489+
ignoreAttributes: false,
490+
attributeNamePrefix: "@_",
491+
});
492+
const jsonData = parser.parse(contextData);
493+
return evaluate(jsonData, pointer);
494+
}
495+
496+
// Regular JSON pointer evaluation
473497
try {
474498
return evaluate(contextData, pointer);
475499
} catch (err) {

test/unit/Arazzo.parameters.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1589,7 +1589,7 @@ describe(`OpenAPI Parameter Types`, function () {
15891589
"access-control-allow-origin": "*",
15901590
connection: "keep-alive",
15911591
"content-length": "102",
1592-
"content-type": "application/xml",
1592+
"content-type": "application/json",
15931593
date: "Thu, 15 Jan 2026 20:26:31 GMT",
15941594
server: "Jetty(9.2.9.v20150224)",
15951595
},

test/unit/Arazzo.security.spec.js

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -880,7 +880,6 @@ describe(`OpenAPI Security`, function () {
880880

881881
describe(`mutualTLS`, function () {
882882
it(`handles the security requirements`, async function () {
883-
// nock.recorder.rec();
884883
const AccessToken =
885884
"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.NHVaYe26MbtOYhSKkoKYdFVomg4i8ZJd8_-RU8VNbftc4TSMb4bXP3l3YlNWACwyXPGffz5aXHc6lty1Y2t4SWRqGteragsVdZufDn5BlnJl9pdR_kdVFUsra2rWKEofkZeIC4yWytE58sMIihvo9H1ScmmVwBcQP6XETqYd0aSHp1gOa9RdUPDvoXQ5oqygTqVtxaDr6wUFKrKItgBMzWIdNZ6y7O9E0DhEPTbE9rfBo6KTFsHAZnMg4k68CDp2woYIaXbmYTWcvbzIuHO7_37GT79XdIwkm95QJ7hYC9RiwrV7mesbY4PAahERJawntho0my942XheVLmGwLMBkQ";
886885

@@ -950,16 +949,20 @@ describe(`OpenAPI Security`, function () {
950949
reqheaders: { Authorization: AccessToken },
951950
})
952951
.delete("/v2/user/jack")
953-
.reply(200, "", {
954-
"access-control-allow-headers":
955-
"Content-Type, api_key, Authorization",
956-
"access-control-allow-methods": "GET, POST, DELETE, PUT",
957-
"access-control-allow-origin": "*",
958-
connection: "close",
959-
"content-length": "0",
960-
date: "Fri, 16 Jan 2026 23:49:12 GMT",
961-
server: "Jetty(9.2.9.v20150224)",
962-
});
952+
.reply(
953+
200,
954+
{},
955+
{
956+
"access-control-allow-headers":
957+
"Content-Type, api_key, Authorization",
958+
"access-control-allow-methods": "GET, POST, DELETE, PUT",
959+
"access-control-allow-origin": "*",
960+
connection: "close",
961+
"content-length": `${JSON.stringify({}).length}`,
962+
date: "Fri, 16 Jan 2026 23:49:12 GMT",
963+
server: "Jetty(9.2.9.v20150224)",
964+
},
965+
);
963966

964967
const inputFile = new Input(
965968
"./test/mocks/inputs/security/input.json",

test/unit/Arazzo.spec.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ describe(`Arazzo Document`, function () {
144144
} catch (err) {
145145
expect(err).to.be.instanceOf(Error);
146146
expect(err.message).to.be.equal(
147-
`SyntaxError: Unexpected token '<', "<?xml vers"... is not valid JSON`,
147+
`Invalid JSON pointer '/id': Invalid object key "id" at position 0 in "/id": key not found in object`,
148148
);
149149
}
150150
});
@@ -2266,7 +2266,7 @@ describe(`Arazzo Document`, function () {
22662266
} catch (err) {
22672267
expect(err).to.be.instanceOf(Error);
22682268
expect(err.message).to.be.equal(
2269-
`SyntaxError: Unexpected token '<', "<?xml vers"... is not valid JSON`,
2269+
`Invalid JSON pointer '/id': Invalid object key "id" at position 0 in "/id": key not found in object`,
22702270
);
22712271
}
22722272
});

test/unit/Expression.spec.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,21 @@ describe(`Expression`, function () {
549549
expect(expected).to.be.eql("john");
550550
});
551551

552+
it(`can resolve a json pointer expression when the context is XML`, function () {
553+
const expression = new Expression();
554+
555+
expression.addToContext(
556+
"response.body",
557+
`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><apiResponse><name>john</name></apiResponse>`,
558+
);
559+
560+
const expected = expression.resolveExpression(
561+
"$response.body#/apiResponse/name",
562+
);
563+
564+
expect(expected).to.be.eql("john");
565+
});
566+
552567
it(`can resolve a templated expression to a json Pointer`, function () {
553568
const expression = new Expression();
554569

0 commit comments

Comments
 (0)