arazzo-runner 0.0.5 → 0.0.7

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,12 @@ 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 Parameters
67
+
68
+ 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.
69
+
70
+ It currently does not follow the `allowEmptyValue`, `allowReserved` or the `content` keywords currently.
71
+
66
72
  ## Logging And Reporting
67
73
 
68
74
  ### Logging
@@ -93,14 +99,18 @@ Work on Reporting still needs completeing.
93
99
 
94
100
  ## Still unsupported
95
101
 
96
- ### OpenAPI Params
102
+ ### PathOperation
97
103
 
98
- OpenAPI parameter types with style and explode are not quite supported yet
104
+ Accessing an OpenAPI operation by Operation Path `'{$sourceDescriptions.petstoreDescription.url}#/paths/~1pet~1findByStatus/get'` does not work currently
99
105
 
100
106
  ### OpenAPI Servers on various levels
101
107
 
102
108
  This pulls from the top level servers object of an OpenAPI Document. Server variables do not work either.
103
109
 
110
+ ### OpenAPI server variables
111
+
112
+ OpenAPI server variables currently do not work
113
+
104
114
  ### JSONPath and XPath criteria objects
105
115
 
106
116
  Criteria Objects set as type JSON Path or XPath do not work
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arazzo-runner",
3
- "version": "0.0.5",
3
+ "version": "0.0.7",
4
4
  "description": "A runner to run through Arazzo Document workflows",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -41,7 +41,7 @@
41
41
  "@swaggerexpert/json-pointer": "^2.10.2",
42
42
  "ajv": "^8.17.1",
43
43
  "jsonpath": "^1.1.1",
44
- "openapi-params": "^0.0.4",
44
+ "openapi-params": "^0.0.5",
45
45
  "stream-chain": "^3.4.0",
46
46
  "stream-json": "^1.9.1"
47
47
  }
package/src/Arazzo.js CHANGED
@@ -68,7 +68,6 @@ class Arazzo extends Document {
68
68
  } catch (err) {
69
69
  if (err.name === "AbortError") {
70
70
  if (err.goto) {
71
- // console.log("goto error");
72
71
  await this.handleGotoRule(err.goto);
73
72
  }
74
73
  } else {
@@ -112,16 +111,18 @@ class Arazzo extends Document {
112
111
 
113
112
  this.expression.addToContext("inputs", this.inputs);
114
113
 
115
- this.workflow.rules = rules;
116
-
117
114
  if (this.workflow.onSuccess) {
118
- this.workflow.rules.setWorkflowSuccess(this.workflow.onSuccess);
115
+ // this.workflow.rules.set(this.workflow.onSuccess);
116
+ rules.setSuccessRules(this.workflow.onSuccess);
119
117
  }
120
118
 
121
119
  if (this.workflow.onFailure) {
122
- this.workflow.rules.setWorkflowFailures(this.workflow.onFailure);
120
+ // this.workflow.rules.setWorkflowFailures(this.workflow.onFailure);
121
+ rules.setFailureRules(this.workflow.onFailure);
123
122
  }
124
123
 
124
+ this.workflow.rules = rules;
125
+
125
126
  await this.runSteps();
126
127
 
127
128
  if (this.workflow.outputs) {
@@ -203,6 +204,7 @@ class Arazzo extends Document {
203
204
 
204
205
  if (step) {
205
206
  this.step = step;
207
+ const rules = new Rules(this.expression, { logger: this.logger });
206
208
  // if (!this.stepContext[step.stepId])
207
209
  // Object.assign(this.stepContext, { [step.stepId]: {} });
208
210
 
@@ -211,13 +213,19 @@ class Arazzo extends Document {
211
213
  // need to deal with reloading the rules when in a retry state or a goto state
212
214
  // if (this.stepContext?.[step.stepId]?.hasLoadedRules === false) {
213
215
  if (this.step.onSuccess) {
214
- this.workflow.rules.setStepSuccesses(this.step.onSuccess);
216
+ rules.setSuccessRules(this.step.onSuccess);
217
+ // this.workflow.rules.setStepSuccesses(this.step.onSuccess);
215
218
  }
216
219
 
217
220
  if (this.step.onFailure) {
218
- this.workflow.rules.setStepFailures(this.step.onFailure);
221
+ rules.setFailureRules(this.step.onFailure);
222
+ // this.workflow.rules.setStepFailures(this.step.onFailure);
219
223
  }
220
224
 
225
+ rules.combineRules(this.workflow.rules);
226
+ this.step.rules = rules;
227
+ // this.step.rules.combineRules(this.workflow.rules);
228
+
221
229
  // this.stepContext[step.stepId].hasLoadedRules = true;
222
230
  // }
223
231
 
@@ -434,7 +442,7 @@ class Arazzo extends Document {
434
442
  await this.dealWithStepOutputs(response);
435
443
  }
436
444
 
437
- const whatNext = this.workflow.rules.runRules(true);
445
+ const whatNext = this.step.rules.runRules(true);
438
446
 
439
447
  if (whatNext.endWorkflow) {
440
448
  this.workflowIndex += 1;
@@ -496,7 +504,7 @@ class Arazzo extends Document {
496
504
  // await this.dealWithStepOutputs(response);
497
505
  // }
498
506
 
499
- const whatNext = this.workflow.rules.runRules();
507
+ const whatNext = this.step.rules.runRules();
500
508
  if (whatNext.endWorkflow) {
501
509
  this.workflowIndex += 1;
502
510
  this.logger.notice(
@@ -683,8 +691,6 @@ class Arazzo extends Document {
683
691
 
684
692
  if (this.retryLimits[whatNext.name] === 0)
685
693
  this.retrySet.delete(whatNext.name);
686
-
687
- // console.log("I need to return here after retrying");
688
694
  }
689
695
 
690
696
  /**
@@ -729,13 +735,14 @@ class Arazzo extends Document {
729
735
  * @private
730
736
  */
731
737
  mapParameters() {
732
- const headers = new Headers();
733
- const queryParams = new URLSearchParams();
734
- const pathParams = {};
738
+ const headersObj = new Headers();
739
+ const headers = new URLParams();
740
+ const queryParams = new URLParams();
741
+ const pathParams = new URLParams();
735
742
 
736
743
  for (const param of this.step?.parameters || []) {
737
744
  const operationDetailParam =
738
- this.sourceDescription.operationDetails?.parameters
745
+ this.sourceDescriptionFile.operationDetails?.parameters
739
746
  .filter((obj) => obj.name === param.name && obj.in === param.in)
740
747
  .at(0);
741
748
 
@@ -743,27 +750,61 @@ class Arazzo extends Document {
743
750
 
744
751
  switch (param.in) {
745
752
  case "header":
746
- headers.append(param.name, value);
753
+ const headerStyle = operationDetailParam?.style || "simple";
754
+ const headerExplode = operationDetailParam?.explode || false;
755
+ headers.append(param.name, value, {
756
+ style: headerStyle,
757
+ explode: headerExplode,
758
+ });
759
+ for (const [header, value] of headers) {
760
+ if (header === param.name) {
761
+ headersObj.append(param.name, value);
762
+ }
763
+ }
747
764
 
748
765
  break;
749
766
 
750
767
  case "path":
751
768
  for (const operation of this.operations) {
752
- operation.url = operation.url.replace(`{${param.name}}`, value);
753
- Object.assign(pathParams, { [param.name]: value });
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 });
754
780
  }
755
781
  break;
756
782
 
757
783
  case "query":
758
- queryParams.append(param.name, value);
784
+ // queryParams.append(param.name, value);
785
+ const style = operationDetailParam?.style || "form";
786
+ let explode = false;
787
+ if (Object.hasOwn(operationDetailParam, "explode")) {
788
+ explode = operationDetailParam.explode;
789
+ } else {
790
+ if (style === "form") {
791
+ explode = true;
792
+ }
793
+ }
794
+ // const explode = operationDetailParam?.explode || false;
795
+ queryParams.append(param.name, value, {
796
+ style: style,
797
+ explode: explode,
798
+ });
759
799
  break;
760
800
  }
761
801
  }
762
802
 
763
- this.expression.addToContext("request.path", pathParams);
803
+ this.addParamsToContext(pathParams, "path", "request");
804
+ // this.expression.addToContext("request.path", pathParams);
764
805
 
765
806
  for (const operation of this.operations) {
766
- operation.headers = headers;
807
+ operation.headers = headersObj;
767
808
  operation.queryParams = queryParams;
768
809
  }
769
810
  }
package/src/Rules.js CHANGED
@@ -13,24 +13,65 @@ class Rules {
13
13
  };
14
14
  }
15
15
 
16
- setWorkflowFailures(failureActions) {
17
- this.workflowFailures = failureActions;
16
+ /**
17
+ * @typedef {Object} criteriaType
18
+ * @property {string} type - REQUIRED. The type of condition to be applied. The options allowed are jsonpath or xpath.
19
+ * @property {string=} version - REQUIRED. A short hand string representing the version of the expression type being used. The allowed values for JSONPath are draft-goessner-dispatch-jsonpath-00. The allowed values for XPath are xpath-30, xpath-20, or xpath-10.
20
+ */
21
+
22
+ /**
23
+ * @typedef {Object} criteria
24
+ * @property {string} context - A Runtime Expression used to set the context for the condition to be applied on. If type is specified, then the context MUST be provided (e.g. $response.body would set the context that a JSONPath query expression could be applied to).
25
+ * @property {string} condition - REQUIRED. The condition to apply. Conditions can be simple (e.g. $statusCode == 200 which applies an operator on a value obtained from a runtime expression), or a regex, or a JSONPath expression. For regex or JSONPath, the type and context MUST be specified.
26
+ * @property {criteriaType} type - The type of condition to be applied. If specified, the options allowed are simple, regex, jsonpath or xpath. If omitted, then the condition is assumed to be simple, which at most combines literals, operators and Runtime Expressions. If jsonpath, then the expression MUST conform to JSONPath. If xpath the expression MUST conform to XML Path Language 3.1. Should other variants of JSONPath or XPath be required, then a Criterion Expression Type Object MUST be specified.
27
+ */
28
+
29
+ /**
30
+ * @typedef {Object} failureActionsObject
31
+ * @property {string} name - REQUIRED. The name of the failure action. Names are case sensitive.
32
+ * @property {string} type - REQUIRED. The type of action to take. Possible values are "end", "retry", or "goto".
33
+ * @property {string=} workflowId - The workflowId referencing an existing workflow within the Arazzo Description to transfer to upon failure of the step. This field is only relevant when the type field value is "goto" or "retry". If the referenced workflow is contained within an arazzo type sourceDescription, then the workflowId MUST be specified using a Runtime Expression (e.g., $sourceDescriptions.<name>.<workflowId>) to avoid ambiguity or potential clashes. This field is mutually exclusive to stepId. When used with "retry", context transfers back upon completion of the specified workflow.
34
+ * @property {string=} stepId - The stepId to transfer to upon failure of the step. This field is only relevant when the type field value is "goto" or "retry". The referenced stepId MUST be within the current workflow. This field is mutually exclusive to workflowId. When used with "retry", context transfers back upon completion of the specified step.
35
+ * @property {number=} retryAfter - A non-negative decimal indicating the seconds to delay after the step failure before another attempt SHALL be made. Note: if an HTTP Retry-After response header was returned to a step from a targeted operation, then it SHOULD overrule this particular field value. This field only applies when the type field value is "retry".
36
+ * @property {number=} retryLimit - A non-negative integer indicating how many attempts to retry the step MAY be attempted before failing the overall step. If not specified then a single retry SHALL be attempted. This field only applies when the type field value is "retry". The retryLimit MUST be exhausted prior to executing subsequent failure actions.
37
+ * @property {criteria[]} criteria - A list of assertions to determine if this action SHALL be executed. Each assertion is described using a Criterion Object.
38
+ */
39
+
40
+ /**
41
+ * @typedef {Object} successActionsObject
42
+ * @property {string} name - REQUIRED. The name of the failure action. Names are case sensitive.
43
+ * @property {string} type - REQUIRED. The type of action to take. Possible values are "end" or "goto".
44
+ * @property {string=} workflowId - The workflowId referencing an existing workflow within the Arazzo Description to transfer to upon success of the step. This field is only relevant when the type field value is "goto". If the referenced workflow is contained within an arazzo type sourceDescription, then the workflowId MUST be specified using a Runtime Expression (e.g., $sourceDescriptions.<name>.<workflowId>) to avoid ambiguity or potential clashes. This field is mutually exclusive to stepId.
45
+ * @property {string=} stepId - The stepId to transfer to upon success of the step. This field is only relevant when the type field value is "goto". The referenced stepId MUST be within the current workflow. This field is mutually exclusive to workflowId.
46
+ * @property {criteria[]} criteria - A list of assertions to determine if this action SHALL be executed. Each assertion is described using a Criterion Object. All criteria assertions MUST be satisfied for the action to be executed.
47
+ */
48
+
49
+ /**
50
+ * Sets the Failure Action Rules to be run
51
+ * @function setFailureRules
52
+ * @param {failureActionsObject[]} failureActions
53
+ */
54
+ setFailureRules(failureActions) {
18
55
  this.failureRules.push(...failureActions);
19
56
  }
20
57
 
21
- setStepFailures(failureActions) {
22
- this.stepFailures = failureActions;
23
- this.failureRules.push(...failureActions);
24
- }
25
-
26
- setWorkflowSuccess(successActions) {
27
- this.workflowSuccesses = successActions;
58
+ /**
59
+ * Sets the Success Action Rules to be run
60
+ * @function setSuccessRules
61
+ * @param {successActionsObject[]} successActions
62
+ */
63
+ setSuccessRules(successActions) {
28
64
  this.successRules.push(...successActions);
29
65
  }
30
66
 
31
- setStepSuccesses(successActions) {
32
- this.stepSuccesses = successActions;
33
- this.successRules.push(...successActions);
67
+ /**
68
+ * Takes a Rules instance and combines the rules
69
+ * @function combineRules
70
+ * @param {Rules} rulesObj
71
+ */
72
+ combineRules(rulesObj) {
73
+ this.failureRules.push(...rulesObj.failureRules);
74
+ this.successRules.push(...rulesObj.successRules);
34
75
  }
35
76
 
36
77
  runRules(successRules = false) {
@@ -48,6 +89,11 @@ class Rules {
48
89
  this.logger.notice(`Running onFailure Rules`);
49
90
  }
50
91
 
92
+ if (this.rules.length)
93
+ this.logger.notice(
94
+ "==================================================================================",
95
+ );
96
+
51
97
  for (const rule of this.rules) {
52
98
  if (rule.criteria) {
53
99
  const passedCriteria = this.criteriaChecks(rule);
@@ -60,6 +106,7 @@ class Rules {
60
106
  obj.goto = true;
61
107
  if (rule.stepId) obj.stepId = rule.stepId;
62
108
  else obj.workflowId = rule.workflowId;
109
+ break;
63
110
  } else {
64
111
  obj.name = rule.name;
65
112
  obj.retry = true;
@@ -67,30 +114,51 @@ class Rules {
67
114
  if (rule.workflowId) obj.workflowId = rule.workflowId;
68
115
  obj.retryLimit = rule?.retryLimit || 1;
69
116
  if (rule.retryAfter) obj.retryAfter = rule.retryAfter;
117
+ break;
70
118
  }
71
119
  }
72
120
  }
73
121
  }
74
122
 
123
+ if (this.rules.length)
124
+ this.logger.notice(
125
+ "==================================================================================",
126
+ );
127
+
75
128
  return obj;
76
129
  }
77
130
 
131
+ /**
132
+ * @function criteriaChecks
133
+ * @private
134
+ * @param {successActionsObject|failureActionsObject} rule
135
+ * @returns {boolean[]}
136
+ */
78
137
  criteriaChecks(rule) {
79
138
  const passedCriteria = [];
80
139
 
81
- this.logger.notice(
82
- "==================================================================================",
83
- );
84
-
85
140
  for (const criteriaObject of rule.criteria) {
86
141
  if (criteriaObject?.type) {
87
142
  if (criteriaObject.type === "regex") {
143
+ this.logger.notice(
144
+ `checking ${rule.name} rule: ${criteriaObject.context} matches ${criteriaObject.condition} `,
145
+ );
88
146
  const hasPassedCheck = this.expression.checkRegexExpression(
89
147
  criteriaObject.context,
90
148
  criteriaObject.condition,
91
149
  );
92
150
 
93
- if (hasPassedCheck) hasPassed.push(true);
151
+ if (hasPassedCheck) {
152
+ this.logger.success(
153
+ `${criteriaObject.context} matched ${criteriaObject.condition}`,
154
+ );
155
+ passedCriteria.push(hasPassedCheck);
156
+ } else {
157
+ this.logger.error(
158
+ `${criteriaObject.context} did not match ${criteriaObject.condition}`,
159
+ );
160
+ }
161
+ // if (hasPassedCheck) hasPassed.push(true);
94
162
  } else {
95
163
  }
96
164
  } else {
@@ -110,20 +178,22 @@ class Rules {
110
178
  }
111
179
  }
112
180
 
113
- this.logger.notice(
114
- "==================================================================================",
115
- );
116
-
117
181
  return passedCriteria;
118
182
  }
119
183
 
184
+ /**
185
+ * @function buildFailureRules
186
+ * @private
187
+ */
120
188
  buildFailureRules() {
121
- this.failureRules.reverse();
122
189
  this.rules = this.failureRules;
123
190
  }
124
191
 
192
+ /**
193
+ * @function buildSuccessRules
194
+ * @private
195
+ */
125
196
  buildSuccessRules() {
126
- this.successRules.reverse();
127
197
  this.rules = this.successRules;
128
198
  }
129
199
  }