arazzo-runner 0.0.7 → 0.0.8

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 CHANGED
@@ -63,6 +63,18 @@ jq --arg password "$secret_password" '.workflowId1.password = $password' input.j
63
63
 
64
64
  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.
65
65
 
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
+
66
78
  ## OpenAPI Parameters
67
79
 
68
80
  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.
99
111
 
100
112
  ## Still unsupported
101
113
 
102
- ### PathOperation
114
+ ### Security
103
115
 
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
109
117
 
110
- ### OpenAPI server variables
118
+ ### PathOperation
111
119
 
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
113
121
 
114
122
  ### JSONPath and XPath criteria objects
115
123
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arazzo-runner",
3
- "version": "0.0.7",
3
+ "version": "0.0.8",
4
4
  "description": "A runner to run through Arazzo Document workflows",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -42,6 +42,7 @@
42
42
  "ajv": "^8.17.1",
43
43
  "jsonpath": "^1.1.1",
44
44
  "openapi-params": "^0.0.5",
45
+ "openapi-server-url-templating": "^1.3.0",
45
46
  "stream-chain": "^3.4.0",
46
47
  "stream-json": "^1.9.1"
47
48
  }
package/src/Arazzo.js CHANGED
@@ -210,8 +210,6 @@ class Arazzo extends Document {
210
210
 
211
211
  this.logger.notice(`Running Step: ${step.stepId}`);
212
212
 
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) {
215
213
  if (this.step.onSuccess) {
216
214
  rules.setSuccessRules(this.step.onSuccess);
217
215
  // this.workflow.rules.setStepSuccesses(this.step.onSuccess);
@@ -224,10 +222,6 @@ class Arazzo extends Document {
224
222
 
225
223
  rules.combineRules(this.workflow.rules);
226
224
  this.step.rules = rules;
227
- // this.step.rules.combineRules(this.workflow.rules);
228
-
229
- // this.stepContext[step.stepId].hasLoadedRules = true;
230
- // }
231
225
 
232
226
  await this.loadOperationData();
233
227
 
@@ -253,7 +247,7 @@ class Arazzo extends Document {
253
247
  * @private
254
248
  */
255
249
  async runOpenAPIStep() {
256
- this.operations = await this.sourceDescriptionFile.buildOperation(
250
+ this.operation = await this.sourceDescriptionFile.buildOperation(
257
251
  this.inputs,
258
252
  this.step,
259
253
  );
@@ -307,50 +301,48 @@ class Arazzo extends Document {
307
301
  }
308
302
  };
309
303
 
310
- for (const operation of this.operations) {
311
- let url = operation.url;
304
+ let url = this.operation.url;
312
305
 
313
- if (operation.queryParams.size) {
314
- url += `?${operation.queryParams}`;
315
- }
306
+ if (this.operation.queryParams.size) {
307
+ url += `?${this.operation.queryParams}`;
308
+ }
316
309
 
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
+ };
321
314
 
322
- if (operation.data) {
323
- options.body = operation.data;
324
- }
315
+ if (this.operation.data) {
316
+ options.body = this.operation.data;
317
+ }
325
318
 
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
+ }
332
325
 
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);
335
328
 
336
- this.logger.notice(`Making a ${options.method.toUpperCase()} to ${url}`);
329
+ this.logger.notice(`Making a ${options.method.toUpperCase()} to ${url}`);
337
330
 
338
- const response = await fetch(url, options);
331
+ const response = await fetch(url, options);
339
332
 
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;
345
337
  }
338
+ }
346
339
 
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);
349
342
 
350
- this.logger.notice(`${url} responded with a: ${response.status}`);
343
+ this.logger.notice(`${url} responded with a: ${response.status}`);
351
344
 
352
- await this.dealWithResponse(response);
353
- }
345
+ await this.dealWithResponse(response);
354
346
  }
355
347
 
356
348
  /**
@@ -725,10 +717,8 @@ class Arazzo extends Document {
725
717
  this.mapParameters();
726
718
  this.mapRequestBody();
727
719
 
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");
732
722
  }
733
723
 
734
724
  /**
@@ -765,19 +755,18 @@ class Arazzo extends Document {
765
755
  break;
766
756
 
767
757
  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);
780
768
  }
769
+
781
770
  break;
782
771
 
783
772
  case "query":
@@ -801,12 +790,9 @@ class Arazzo extends Document {
801
790
  }
802
791
 
803
792
  this.addParamsToContext(pathParams, "path", "request");
804
- // this.expression.addToContext("request.path", pathParams);
805
793
 
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;
810
796
  }
811
797
 
812
798
  /**
@@ -833,13 +819,14 @@ class Arazzo extends Document {
833
819
  this.step.requestBody.payload,
834
820
  );
835
821
 
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
+ );
842
827
  }
828
+
829
+ this.operation.data = payload;
843
830
  }
844
831
  }
845
832
 
package/src/OpenAPI.js CHANGED
@@ -1,5 +1,7 @@
1
1
  "use strict";
2
2
 
3
+ const { substitute, parse } = require("openapi-server-url-templating");
4
+
3
5
  const Document = require("./Document");
4
6
 
5
7
  class OpenAPI extends Document {
@@ -17,8 +19,9 @@ class OpenAPI extends Document {
17
19
  for (let operation in value[key]) {
18
20
  if (value[key][operation]?.operationId === operationId) {
19
21
  this.path = key;
20
- this.operation = operation;
22
+ this.method = operation;
21
23
  this.operationDetails = value[key][operation];
24
+ this.pathServers = value[key]?.["servers"] || [];
22
25
  }
23
26
  }
24
27
  }
@@ -30,11 +33,19 @@ class OpenAPI extends Document {
30
33
  }
31
34
 
32
35
  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
+ }
34
45
 
35
46
  this.buildOperations();
36
47
 
37
- return this.operations;
48
+ return this.operation;
38
49
  }
39
50
 
40
51
  async getServers() {
@@ -65,15 +76,42 @@ class OpenAPI extends Document {
65
76
  }
66
77
  }
67
78
 
68
- buildOperations() {
69
- this.operations = [];
79
+ parseServer() {
80
+ const server = this.servers.at(0);
70
81
 
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
+ }
76
101
  }
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
+ });
77
115
  }
78
116
  }
79
117