Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Harden-expression-and-condition-capabilities #77

Merged
merged 6 commits into from
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions examples/1.0.0/FAPI-PAR.workflow.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ workflows:
value: $inputs.PARrequestBody
successCriteria:
# assertions to determine step was successful
- $statusCode == 200
- condition: $statusCode == 200
outputs:
request_uri: $response.body.request_uri

Expand All @@ -131,7 +131,7 @@ workflows:
value: $inputs.client_id
successCriteria:
# assertions to determine step was successful
- $statusCode == 302
- condition: $statusCode == 302
outputs:
code: $response.body.code # Not really, this is a query parameter (need a way to represent out-of-band props)

Expand Down Expand Up @@ -166,7 +166,7 @@ workflows:
value: $inputs.code_verifier
successCriteria:
# assertions to determine step was successful
- $statusCode == 200
- condition: $statusCode == 200
outputs:
tokenResponse: $response.body

Expand Down
4 changes: 2 additions & 2 deletions examples/1.0.0/LoginAndRetrievePets.workflow.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ workflows:
value: $inputs.password
successCriteria:
# assertions to determine step was successful
- $statusCode == 200
- condition: $statusCode == 200
outputs:
# outputs from this step
tokenExpires: $response.header.X-Expires-After
Expand All @@ -53,7 +53,7 @@ workflows:
in: header
value: $steps.loginUser.outputs.sessionToken
successCriteria:
- $statusCode == 200
- condition: $statusCode == 200
outputs:
# outputs from this step
availablePets: $response.body
Expand Down
24 changes: 16 additions & 8 deletions examples/1.0.0/oauth.workflow.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,10 @@ workflows:
grant_type: refresh_token
refresh_token: $steps.do-the-auth-flow.outputs.my_refresh_token
successCriteria:
- $statusCode == 200
- $response.body.access_token != null
- condition: $statusCode == 200
- context: $response.body
condition: $.access_token != null
type: JSONPath
outputs:
access_token: $response.body.access_token
refresh_token: $response.body.refresh_token
Expand Down Expand Up @@ -105,8 +107,10 @@ workflows:
value: 'client_credentials'

successCriteria:
- $statusCode == 200
- $response.body.access_token != null
- condition: $statusCode == 200
- context: $response.body
condition: $.access_token != null
type: JSONPath
outputs:
access_token: $response.body.access_token

Expand Down Expand Up @@ -153,8 +157,10 @@ workflows:
in: query
value: '12345'
successCriteria:
- $statusCode == 200
- $response.body.access_token != null
- condition: $statusCode == 200
- context: $response.body
condition: $.access_token != null
type: JSONPath
outputs:
code: $response.body.code # Not really, this is a query parameter

Expand Down Expand Up @@ -185,8 +191,10 @@ workflows:
value: $steps.browser-authorize.outputs.code

successCriteria:
- $statusCode == 200
- $response.body.access_token != null
- condition: $statusCode == 200
- context: $response.body
condition: $.access_token != null
type: JSONPath
outputs:
access_token: $response.body.access_token
refresh_token: $response.body.refresh_token
Expand Down
12 changes: 6 additions & 6 deletions examples/1.0.0/pet-coupons.workflow.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ workflows:
in: query
value: $inputs.my_pet_tags
successCriteria:
- $statusCode == 200
- condition: $statusCode == 200
outputs:
my_pet_id: $outputs[0].id
# there is some implied selection here - findPetsByTags responds with a list of pets,
Expand All @@ -40,7 +40,7 @@ workflows:
in: path
value: $steps.find-pet.outputs.my_pet_id
successCriteria:
- $statusCode == 200
- condition: $statusCode == 200
outputs:
my_coupon_code: $response.body.couponCode
- stepId: place-order
Expand All @@ -53,7 +53,7 @@ workflows:
in: body
value: $steps.find-coupons.outputs.my_coupon_code
successCriteria:
- $statusCode == 200
- condition: $statusCode == 200
outputs:
my_order_id: $response.body.id
- workflowId: buy-available-pet
Expand All @@ -75,7 +75,7 @@ workflows:
- $ref: '#/components/parameters/pageSize'
value: 10
successCriteria:
- $statusCode == 200
- condition: $statusCode == 200
outputs:
my_pet_id: $outputs[0].id
- stepId: place-order
Expand All @@ -85,7 +85,7 @@ workflows:
in: body
value: $steps.find-pet.outputs.my_pet_id
successCriteria:
- $statusCode == 200
- condition: $statusCode == 200
outputs:
my_order_id: $response.body.id
- workflowId: place-order
Expand Down Expand Up @@ -130,7 +130,7 @@ workflows:
target: $request.body#/complete
value: false
successCriteria:
- $statusCode == 200
- condition: $statusCode == 200
outputs:
my_order_id: $response.body.id
components:
Expand Down
98 changes: 86 additions & 12 deletions versions/1.0.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,11 @@ The Workflows Specification can articulate these workflows in a human and machin
- [Component Object](#component-object)
- [Fixed Fields](#fixed-fields-8)
- [Component Object Example](#component-object-example)
- [Reference Object](#reference-object)
- [Criterion Object](#criterion-object)
- [Fixed Fields](#fixed-fields-9)
- [Criterion Object Example](#criterion-object-example)
- [Reference Object](#reference-object)
- [Fixed Fields](#fixed-fields-10)
- [Reference Object Example](#reference-object-example)
- [Runtime Expressions](#runtime-expressions)
- [Specification Extensions](#specification-extensions)
Expand Down Expand Up @@ -171,7 +174,7 @@ workflows:
value: $inputs.password
successCriteria:
# assertions to determine step was successful
- $statusCode == 200
- condition: $statusCode == 200
outputs:
# outputs from this step
tokenExpires: $response.header.X-Expires-After
Expand All @@ -189,7 +192,7 @@ workflows:
in: header
value: $steps.loginUser.outputs.sessionToken
successCriteria:
- $statusCode == 200
- condition: $statusCode == 200
outputs:
# outputs from this step
availablePets: $response.body
Expand Down Expand Up @@ -293,7 +296,7 @@ steps:
value: $inputs.password
successCriteria:
# assertions to determine step was successful
$statusCode == 200
- condition: $statusCode == 200
outputs:
# outputs from this step
tokenExpires: $response.header.X-Expires-After
Expand All @@ -317,7 +320,7 @@ Field Name | Type | Description
<a name="stepWorkflowId"></a>workflowId | `string` | The [workflowId](#fixed-fields-2) referencing an existing workflow within the Workflows document. If more than one `workflowsSpec` type `source` document is defined within a Workflows document, then the `workflowId` specified MUST be prefixed with the source name to avoid ambiguity or potential clashes. The field is mutually exclusive of the `operationId` and `operationRef` fields respectively.
<a name="stepParameters"></a>parameters | [[Parameter Object](#parameter-object) \| [Reference Object](#reference-object)] | A list of parameters to pass to an operation or workflow as referenced by `operationId`, `operationRef`, or `workflowId`. If a Reference Object is provided, it MUST link to parameters defined in [components/parameters](#component-object).
<a name="dependsOn"></a>dependsOn | [`string`] | A list of steps that MUST be completed before this step can be processed. This helps to ensure workflow steps are executed in the correct order and that dependent steps are not processed in parallel. The values provided MUST be the be the `stepId` which uniquely references a step.
<a name="stepSuccessCriteria"></a>successCriteria | [{expression}] | A list of assertions to determine the success of the step
<a name="stepSuccessCriteria"></a>successCriteria | [[Criterion Object](#criterion-object)] | A list of assertions to determine the success of the step. Each assertion is described using a [Criterion Object](#criterion-object).
<a name="stepOnSuccess"></a>onSuccess | [[Success Action Object](#success-action-object)] | An array of success action objects that specify what to do upon step success. If omitted, the next sequential step shall be executed as the default behavior.
<a name="stepOnFailure"></a>onFailure | [[Failure Action Object](#failure-action-object)] | An array of failure action objects that specify what to do upon step failure. If omitted, the default behavior is to break and return.
<a name="stepOutputs"></a>outputs | Map[`string`, {expression}] | A map between a friendly name and a dynamic output value defined using a [runtime expression](#runtime-expressions). The name MUST use keys that match the regular expression: `^[a-zA-Z0-9\.\-_]+$`.
Expand All @@ -342,7 +345,7 @@ parameters:
value: $inputs.password
successCriteria:
# assertions to determine step was successful
$statusCode == 200
- condition: $statusCode == 200
outputs:
# outputs from this step
tokenExpires: $response.header.X-Expires-After
Expand All @@ -366,7 +369,7 @@ steps:
value: $inputs.password
successCriteria:
# assertions to determine step was successful
- $statusCode == 200
- condition: $statusCode == 200
outputs:
# outputs from this step
tokenExpires: $response.header.X-Expires-After
Expand All @@ -383,7 +386,7 @@ steps:
in: header
value: $steps.loginUser.outputs.sessionToken
successCriteria:
- $statusCode == 200
- condition: $statusCode == 200
outputs:
# outputs from this step
availablePets: $response.body
Expand Down Expand Up @@ -460,7 +463,8 @@ Field Name | Type | Description
<a name="successActionType"></a> type | `string` | **REQUIRED**. The type of action to take. Possible values are `"end"` or `"goto"`.
<a name="successWorkflowId"></a> workflowId | `string` | The [workflowId](#fixed-fields-2) referencing an existing workflow within the Workflows document to transfer to upon success of the step. This field is only relevant when the `type` field value is `goto`. If more than one `workflowsSpec` type `source` document is defined within a Workflows document, then the `workflowId` specified MUST be prefixed with the source name to avoid ambiguity or potential clashes. This field is mutually exclusive to `stepId`.
<a name="successStepId"></a> stepId | `string` | 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` SHOULD be within the current workflow. This field is mutually exclusive to `workflowId`.
<a name="successCriteria"></a> criteria | [{expression}] | A list of assertions to determine if this action SHALL be executed.
<a name="successCriteria"></a> criteria | [[Criterion Object](#criterion-object)] | A list of assertions to determine if this action SHALL be executed. Each assertion is described using a [Criterion Object](#criterion-object).


**Note -** should multiple success actions have similar `criteria`, the first sequential action matching the criteria SHALL be the action executed.

Expand All @@ -471,7 +475,9 @@ type: goto
stepId: joinWaitingListStep
criteria:
# assertions to determine if this success action should be executed
- $response.body.Pets.length() > 0
- context: $response.body
condition: $.Pets.length() > 0
type: JSONPath
```

This object MAY be extended with [Specification Extensions](#specification-extensions).
Expand All @@ -491,7 +497,7 @@ Field Name | Type | Description
<a name="failureStepId"></a> stepId | `string` | 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` SHOULD 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.
<a name="failureRetryAfter"></a> retryAfter | `number` | A non-negative decimal indicating the milliseconds delay after the step failure before another attempt SHALL be made. **Note:** if an HTTP [Retry-After](https://www.rfc-editor.org/rfc/rfc9110.html#name-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` or `function`.
<a name="failureRetryLimit"></a> retryLimit | `integer` | 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.
<a name="failureCriteria"></a> criteria | [{expression}] | A list of assertions to determine if this action SHALL be executed.
<a name="failureCriteria"></a> criteria | [[Criterion Object](#criterion-object)] | A list of assertions to determine if this action SHALL be executed. Each assertion is described using a [Criterion Object](#criterion-object).

**Note -** should multiple success actions have similar `criteria`, the first sequential action matching the criteria SHALL be the action executed.

Expand All @@ -503,7 +509,7 @@ retryAfter: 1000
retryLimit: 5
criteria:
# assertions to determine if this action should be executed
- $statusCode == 503
- condition: $statusCode == 503
```

This object MAY be extended with [Specification Extensions](#specification-extensions).
Expand Down Expand Up @@ -614,6 +620,74 @@ This object cannot be extended with additional properties and any properties add
}
```

#### Criterion Object

An object used to specify the context, conditions, and condition types that can be used to prove or satisfy assertions specified in Step Object `successCriteria`, Success Action Object `criteria`, and Failure Action Object `criteria`.

There are three flavors of conditions supported:
- simple - where basic literals, operators, and loose comparisons are used in combination with [Runtime Expressions](#runtime-expressions).
- regex - where a regex pattern is applied on the supplied context. The context is defined by a [Runtime Expression](#runtime-expressions).
- JSONPath - where a JSONPath expression is applied. The root node context is defined by a [Runtime Expression](#runtime-expressions).

##### Literals
As part of a condition expression, you can use `boolean`, `null`, `number`, or `string` data types.

Type | Literal value
---|---
`boolean` | `true` or `false`
`null` | `null`
`number` | Any number format supported in [Data Types](#data-types)

##### Operators
Operator | Description
---|---
`<`| Less than
`<=`| Less than or equal
`>`| Greater than
`>=`| Greater than or equal
`==`| Equal
`!=`| Not equal
`!`| Not
`&&`| And
`\|\|`| Or
`()`| Logical Grouping
`[]`| Index (0-based)
`.`| Property de-reference

String comparisons `SHOULD` be case insensitive.

##### Fixed Fields
Field Name | Type | Description
---|:---:|---
<a name="criterionContext"></a>context | `{expression}` | A [runtime expression](#runtime-expressions) 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).
<a name="criterionCondition"></a>condition | `string` | **REQUIRED**. The condition to apply. Conditions can be simple (e.g. `$statusCode == 200` which applies a operator on a value obtained from a runtime expression), or a regex, or a JSONPath expression. For regex and [JSONPath](https://datatracker.ietf.org/doc/draft-ietf-jsonpath-base/21/), the `type` and `context` MUST be specified.
<a name="criterionType"></a>type | `string` | The type of condition to be applied. If specified, the options allowed are `regex` or `JSONPath`. If omitted, then the condition is assumed to be simple, which at most combines literals, operators and [Runtime Expressions](#runtime-expressions).
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm in two minds about this myself - but should be add a simple type which is the default type, instead of having an implicit simple type if none is specified?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was very much on the fence here. For completeness, it's probably better to have it as part of allowed values. I doubt, I'd ever write it but no harm in allowing folks to be explicit if they want to.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extended to now support simple



This object _MAY_ be extended with [Specification Extensions](#specificationExtensions).

##### Criterion Object Example

**Simple Condition Example**

```yaml
- condition: $statusCode == 200
```

**Regex Condition Example**
```yaml
- context: $statusCode
condition: '^200$'
type: regex
```

**JSONPath Condition Example**
```yaml
- context: $response.body
condition: $[?length(@.pets) > 0]
type: JSONPath
```

### <a name="runtimeExpressions"></a>Runtime Expressions
A runtime expression allows values to be defined based on information that will be available within an HTTP message, an event message, and within objects serialized from the Workflows document such as [workflows](#workflow-object) or [steps](#step-object).

Expand Down
Loading