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 +12 -2
- package/package.json +2 -2
- package/src/Arazzo.js +62 -21
- package/src/Rules.js +93 -23
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
|
-
###
|
|
102
|
+
### PathOperation
|
|
97
103
|
|
|
98
|
-
OpenAPI
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
|
733
|
-
const
|
|
734
|
-
const
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
753
|
-
|
|
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.
|
|
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 =
|
|
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
|
-
|
|
17
|
-
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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)
|
|
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
|
}
|