Skip to content

Commit 0b3857c

Browse files
authored
Merge pull request #26 from JaredCE/handle-servers-correctly
Handle servers correctly
2 parents f6189b2 + 04beb16 commit 0b3857c

File tree

22 files changed

+1805
-92
lines changed

22 files changed

+1805
-92
lines changed

README.md

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,18 @@ jq --arg password "$secret_password" '.workflowId1.password = $password' input.j
6363

6464
Obviously, if you have a lot of secret variables that need adding as inputs, then you might need to write a script that can alter the `input.json` file for you within your CI/CD runner.
6565

66+
## OpenAPI Servers
67+
68+
OpenAPI Documents allow you to specify servers at the root, [path](https://spec.openapis.org/oas/latest.html#path-item-object) and [operation](https://spec.openapis.org/oas/latest.html#operation-object) level. They allow you to specify multiple servers, however the OpenAPI specification is opinionated that all servers specified in a Document should return the same thing.
69+
70+
This Arazzo Runner will pick the first server it comes across in the array of servers and run the operation against that.
71+
72+
- If the operation has servers specified, it will use the first server at the operation level, ignoring path and root servers.
73+
- If the operation does not have a server specified, and the path level does, it will use the path level server, ignoring the root level
74+
- If the operation only has servers specified at the root of the document, it will only use the first root level server.
75+
76+
It will attempt to map to the [Server Variables](https://spec.openapis.org/oas/latest.html#server-variable-object), using the `default` that is set.
77+
6678
## OpenAPI Parameters
6779

6880
OpenAPI Documents allow you to specify [`header`, `path` and `query` parameters](https://spec.openapis.org/oas/latest.html#parameter-object) in myriad of styles. This Arazzo Runner will respect your styling and send the format to the server as specified by your OpenAPI document.
@@ -99,17 +111,13 @@ Work on Reporting still needs completeing.
99111

100112
## Still unsupported
101113

102-
### PathOperation
114+
### Security
103115

104-
Accessing an OpenAPI operation by Operation Path `'{$sourceDescriptions.petstoreDescription.url}#/paths/~1pet~1findByStatus/get'` does not work currently
105-
106-
### OpenAPI Servers on various levels
107-
108-
This pulls from the top level servers object of an OpenAPI Document. Server variables do not work either.
116+
OpenAPI security is still not fully supported
109117

110-
### OpenAPI server variables
118+
### PathOperation
111119

112-
OpenAPI server variables currently do not work
120+
Accessing an OpenAPI operation by Operation Path `'{$sourceDescriptions.petstoreDescription.url}#/paths/~1pet~1findByStatus/get'` does not work currently
113121

114122
### JSONPath and XPath criteria objects
115123

package-lock.json

Lines changed: 15 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.7",
3+
"version": "0.0.8",
44
"description": "A runner to run through Arazzo Document workflows",
55
"main": "index.js",
66
"scripts": {
@@ -42,6 +42,7 @@
4242
"ajv": "^8.17.1",
4343
"jsonpath": "^1.1.1",
4444
"openapi-params": "^0.0.5",
45+
"openapi-server-url-templating": "^1.3.0",
4546
"stream-chain": "^3.4.0",
4647
"stream-json": "^1.9.1"
4748
}

src/Arazzo.js

Lines changed: 53 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,6 @@ class Arazzo extends Document {
210210

211211
this.logger.notice(`Running Step: ${step.stepId}`);
212212

213-
// need to deal with reloading the rules when in a retry state or a goto state
214-
// if (this.stepContext?.[step.stepId]?.hasLoadedRules === false) {
215213
if (this.step.onSuccess) {
216214
rules.setSuccessRules(this.step.onSuccess);
217215
// this.workflow.rules.setStepSuccesses(this.step.onSuccess);
@@ -224,10 +222,6 @@ class Arazzo extends Document {
224222

225223
rules.combineRules(this.workflow.rules);
226224
this.step.rules = rules;
227-
// this.step.rules.combineRules(this.workflow.rules);
228-
229-
// this.stepContext[step.stepId].hasLoadedRules = true;
230-
// }
231225

232226
await this.loadOperationData();
233227

@@ -253,7 +247,7 @@ class Arazzo extends Document {
253247
* @private
254248
*/
255249
async runOpenAPIStep() {
256-
this.operations = await this.sourceDescriptionFile.buildOperation(
250+
this.operation = await this.sourceDescriptionFile.buildOperation(
257251
this.inputs,
258252
this.step,
259253
);
@@ -307,50 +301,48 @@ class Arazzo extends Document {
307301
}
308302
};
309303

310-
for (const operation of this.operations) {
311-
let url = operation.url;
304+
let url = this.operation.url;
312305

313-
if (operation.queryParams.size) {
314-
url += `?${operation.queryParams}`;
315-
}
306+
if (this.operation.queryParams.size) {
307+
url += `?${this.operation.queryParams}`;
308+
}
316309

317-
const options = {
318-
method: operation.operation,
319-
headers: operation.headers,
320-
};
310+
const options = {
311+
method: this.operation.method,
312+
headers: this.operation.headers,
313+
};
321314

322-
if (operation.data) {
323-
options.body = operation.data;
324-
}
315+
if (this.operation.data) {
316+
options.body = this.operation.data;
317+
}
325318

326-
if (this.retryAfter) {
327-
this.logger.notice(
328-
`retryAfter was set: waiting ${this.retryAfter * 1000} seconds`,
329-
);
330-
await sleep(this.retryAfter * 1000);
331-
}
319+
if (this.retryAfter) {
320+
this.logger.notice(
321+
`retryAfter was set: waiting ${this.retryAfter * 1000} seconds`,
322+
);
323+
await sleep(this.retryAfter * 1000);
324+
}
332325

333-
this.expression.addToContext("url", url);
334-
this.expression.addToContext("method", options.method);
326+
this.expression.addToContext("url", url);
327+
this.expression.addToContext("method", options.method);
335328

336-
this.logger.notice(`Making a ${options.method.toUpperCase()} to ${url}`);
329+
this.logger.notice(`Making a ${options.method.toUpperCase()} to ${url}`);
337330

338-
const response = await fetch(url, options);
331+
const response = await fetch(url, options);
339332

340-
if (response.headers.has("retry-after")) {
341-
const retryAfter = parseRetryAfter(response.headers.get("retry-after"));
342-
if (retryAfter !== null) {
343-
this.retryAfter = retryAfter;
344-
}
333+
if (response.headers.has("retry-after")) {
334+
const retryAfter = parseRetryAfter(response.headers.get("retry-after"));
335+
if (retryAfter !== null) {
336+
this.retryAfter = retryAfter;
345337
}
338+
}
346339

347-
this.addParamsToContext(response.headers, "header", "response");
348-
this.expression.addToContext("statusCode", response.status);
340+
this.addParamsToContext(response.headers, "header", "response");
341+
this.expression.addToContext("statusCode", response.status);
349342

350-
this.logger.notice(`${url} responded with a: ${response.status}`);
343+
this.logger.notice(`${url} responded with a: ${response.status}`);
351344

352-
await this.dealWithResponse(response);
353-
}
345+
await this.dealWithResponse(response);
354346
}
355347

356348
/**
@@ -725,10 +717,8 @@ class Arazzo extends Document {
725717
this.mapParameters();
726718
this.mapRequestBody();
727719

728-
for (const operation of this.operations) {
729-
this.addParamsToContext(operation.headers, "headers", "request");
730-
this.addParamsToContext(operation.queryParams, "query", "request");
731-
}
720+
this.addParamsToContext(this.operation.headers, "headers", "request");
721+
this.addParamsToContext(this.operation.queryParams, "query", "request");
732722
}
733723

734724
/**
@@ -765,19 +755,18 @@ class Arazzo extends Document {
765755
break;
766756

767757
case "path":
768-
for (const operation of this.operations) {
769-
const pathStyle = operationDetailParam?.style || "simple";
770-
const pathExplode = operationDetailParam?.explode || false;
771-
pathParams.append(param.name, value, {
772-
style: pathStyle,
773-
explode: pathExplode,
774-
});
775-
for (const [name, value] of pathParams.entries()) {
776-
operation.url = operation.url.replace(`{${name}}`, value);
777-
}
778-
// operation.url = operation.url.replace(`{${param.name}}`, value);
779-
// Object.assign(pathParams, { [param.name]: value });
758+
const pathStyle = operationDetailParam?.style || "simple";
759+
const pathExplode = operationDetailParam?.explode || false;
760+
761+
pathParams.append(param.name, value, {
762+
style: pathStyle,
763+
explode: pathExplode,
764+
});
765+
766+
for (const [name, value] of pathParams.entries()) {
767+
this.operation.url = this.operation.url.replace(`{${name}}`, value);
780768
}
769+
781770
break;
782771

783772
case "query":
@@ -801,12 +790,9 @@ class Arazzo extends Document {
801790
}
802791

803792
this.addParamsToContext(pathParams, "path", "request");
804-
// this.expression.addToContext("request.path", pathParams);
805793

806-
for (const operation of this.operations) {
807-
operation.headers = headersObj;
808-
operation.queryParams = queryParams;
809-
}
794+
this.operation.headers = headersObj;
795+
this.operation.queryParams = queryParams;
810796
}
811797

812798
/**
@@ -833,13 +819,14 @@ class Arazzo extends Document {
833819
this.step.requestBody.payload,
834820
);
835821

836-
for (const operation of this.operations) {
837-
if (this.step.requestBody.contentType) {
838-
operation.headers.append("accept", this.step.requestBody.contentType);
839-
}
840-
841-
operation.data = payload;
822+
if (this.step.requestBody.contentType) {
823+
this.operation.headers.append(
824+
"accept",
825+
this.step.requestBody.contentType,
826+
);
842827
}
828+
829+
this.operation.data = payload;
843830
}
844831
}
845832

src/OpenAPI.js

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"use strict";
22

3+
const { substitute, parse } = require("openapi-server-url-templating");
4+
35
const Document = require("./Document");
46

57
class OpenAPI extends Document {
@@ -17,8 +19,9 @@ class OpenAPI extends Document {
1719
for (let operation in value[key]) {
1820
if (value[key][operation]?.operationId === operationId) {
1921
this.path = key;
20-
this.operation = operation;
22+
this.method = operation;
2123
this.operationDetails = value[key][operation];
24+
this.pathServers = value[key]?.["servers"] || [];
2225
}
2326
}
2427
}
@@ -30,11 +33,19 @@ class OpenAPI extends Document {
3033
}
3134

3235
async buildOperation(inputs, step) {
33-
await this.getServers();
36+
if (!this.pathServers.length && !this.operationDetails?.servers?.length) {
37+
await this.getServers();
38+
} else {
39+
if (this.operationDetails.servers) {
40+
this.servers = this.operationDetails.servers;
41+
} else {
42+
this.servers = this.pathServers;
43+
}
44+
}
3445

3546
this.buildOperations();
3647

37-
return this.operations;
48+
return this.operation;
3849
}
3950

4051
async getServers() {
@@ -65,15 +76,42 @@ class OpenAPI extends Document {
6576
}
6677
}
6778

68-
buildOperations() {
69-
this.operations = [];
79+
parseServer() {
80+
const server = this.servers.at(0);
7081

71-
for (const server of this.servers) {
72-
this.operations.push({
73-
url: `${server.url}${this.path}`,
74-
operation: this.operation,
75-
});
82+
if (server.variables) {
83+
const parseResult = parse(server.url);
84+
const parts = [];
85+
86+
parseResult.ast.translate(parts);
87+
88+
for (const partType of parts) {
89+
const [type, value] = partType;
90+
91+
if (type === "server-variable-name") {
92+
const replacementValueData = server.variables[value];
93+
if (replacementValueData.default) {
94+
server.url = server.url.replace(
95+
`{${value}}`,
96+
replacementValueData.default,
97+
);
98+
}
99+
}
100+
}
76101
}
102+
103+
return server;
104+
}
105+
106+
buildOperations() {
107+
this.operation = {};
108+
109+
const server = this.parseServer();
110+
111+
Object.assign(this.operation, {
112+
url: `${server.url}${this.path}`,
113+
method: this.method,
114+
});
77115
}
78116
}
79117

0 commit comments

Comments
 (0)