Skip to content

Commit bf30a86

Browse files
committed
Fixes #420 - Add REST invocation function definition based on OpenAPI Path Item
Signed-off-by: Ricardo Zanini <[email protected]>
1 parent 7023383 commit bf30a86

File tree

3 files changed

+211
-17
lines changed

3 files changed

+211
-17
lines changed

roadmap/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ _Status description:_
3535
| ✔️| Apply fixes to auth spec schema [workflow schema](https://github.com/serverlessworkflow/specification/tree/main/schema) |
3636
| ✔️| Update the `dataInputSchema` top-level property by supporting the assignment of a JSON schema object [workflow schema](https://github.com/serverlessworkflow/specification/tree/main/specification.md#workflow-definition-structure) |
3737
| ✔️| Add the new `WORKFLOW` reserved keyword to workflow expressions |
38+
| ✔️| Add the new `rest` function type [spec doc](https://github.com/serverlessworkflow/specification/tree/main/specification.md#using-functions-for-restful-service-invocations) |
3839
| ✏️️| Add inline state defs in branches | |
39-
| ✏️️| Update rest function definition | |
4040
| ✏️️| Add "completedBy" functionality | |
4141
| ✏️️| Define workflow context | |
4242
| ✏️️| Start work on TCK | |

schema/functions.json

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,23 +35,23 @@
3535
"minLength": 1
3636
},
3737
"operation": {
38-
"type": "string",
39-
"description": "If type is `rest`, <path_to_openapi_definition>#<operation_id>. If type is `asyncapi`, <path_to_asyncapi_definition>#<operation_id>. If type is `rpc`, <path_to_grpc_proto_file>#<service_name>#<service_method>. If type is `graphql`, <url_to_graphql_endpoint>#<literal \\\"mutation\\\" or \\\"query\\\">#<query_or_mutation_name>. If type is `odata`, <URI_to_odata_service>#<Entity_Set_Name>. If type is `expression`, defines the workflow expression.",
40-
"minLength": 1
38+
"type": "object",
39+
"$ref": "#/definitions/operation"
4140
},
4241
"type": {
4342
"type": "string",
44-
"description": "Defines the function type. Is either `rest`, `asyncapi, `rpc`, `graphql`, `odata`, `expression`, or `custom`. Default is `rest`",
43+
"description": "Defines the function type. Is either `rest`, `openapi`,`asyncapi, `rpc`, `graphql`, `odata`, `expression`, or `custom`. Default is `openapi`.",
4544
"enum": [
4645
"rest",
46+
"openapi",
4747
"asyncapi",
4848
"rpc",
4949
"graphql",
5050
"odata",
5151
"expression",
5252
"custom"
5353
],
54-
"default": "rest"
54+
"default": "openapi"
5555
},
5656
"authRef": {
5757
"oneOf": [
@@ -63,13 +63,13 @@
6363
{
6464
"type": "object",
6565
"description": "Configures both the auth definition used to retrieve the operation's resource and the auth definition used to invoke said operation",
66-
"properties":{
67-
"resource":{
66+
"properties": {
67+
"resource": {
6868
"type": "string",
6969
"description": "References an auth definition to be used to access the resource defined in the operation parameter",
7070
"minLength": 1
7171
},
72-
"invocation":{
72+
"invocation": {
7373
"type": "string",
7474
"description": "References an auth definition to be used to invoke the operation"
7575
}
@@ -90,6 +90,25 @@
9090
"name",
9191
"operation"
9292
]
93+
},
94+
"operation": {
95+
"oneOf": [
96+
{
97+
"type": "string",
98+
"description": "If type is `openapi`, <path_to_openapi_definition>#<operation_id>. If type is `asyncapi`, <path_to_asyncapi_definition>#<operation_id>. If type is `rpc`, <path_to_grpc_proto_file>#<service_name>#<service_method>. If type is `graphql`, <url_to_graphql_endpoint>#<literal \\\"mutation\\\" or \\\"query\\\">#<query_or_mutation_name>. If type is `odata`, <URI_to_odata_service>#<Entity_Set_Name>. If type is `expression`, defines the workflow expression.",
99+
"minLength": 1
100+
},
101+
{
102+
"type": "object",
103+
"description": "OpenAPI Path Object definition",
104+
"$comment": "https://spec.openapis.org/oas/v3.1.0#paths-object",
105+
"patternProperties": {
106+
"^/": {
107+
"type": "object"
108+
}
109+
}
110+
}
111+
]
93112
}
94113
}
95114
}

specification.md

Lines changed: 183 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
+ [Using multiple data filters](#using-multiple-data-filters)
2424
+ [Data Merging](#data-merging)
2525
* [Workflow Functions](#workflow-functions)
26+
+ [Using Functions for OpenAPI Service Invocations](#using-functions-for-openapi-service-invocations)
2627
+ [Using Functions for RESTful Service Invocations](#using-functions-for-restful-service-invocations)
2728
+ [Using Functions for Async API Service Invocations](#using-functions-for-async-api-service-invocations)
2829
+ [Using Functions for RPC Service Invocations](#using-functions-for-rpc-service-invocations)
@@ -991,8 +992,9 @@ They can be referenced by their domain-specific names inside workflow [states](#
991992

992993
Reference the following sections to learn more about workflow functions:
993994

994-
* [Using functions for RESTful service invocations](#Using-Functions-for-RESTful-Service-Invocations)
995-
* [Using Functions for Async API Service Invocations](#Using-Functions-for-Async-API-Service-Invocations)
995+
* [Using functions for OpenAPI Service invocations](#using-functions-for-openapi-service-invocations)
996+
+ [Using functions for RESTful Service Invocations](#using-functions-for-rest-service-invocations)
997+
* [Using functions for Async API Service Invocations](#Using-Functions-for-Async-API-Service-Invocations)
996998
* [Using functions for gRPC service invocation](#Using-Functions-For-RPC-Service-Invocations)
997999
* [Using functions for GraphQL service invocation](#Using-Functions-For-GraphQL-Service-Invocations)
9981000
* [Using Functions for OData Service Invocations](#Using-Functions-for-OData-Service-Invocations)
@@ -1002,7 +1004,7 @@ Reference the following sections to learn more about workflow functions:
10021004
We can define if functions are invoked sync or async. Reference
10031005
the [functionRef](#FunctionRef-Definition) to learn more on how to do this.
10041006

1005-
#### Using Functions for RESTful Service Invocations
1007+
#### Using Functions for OpenAPI Service Invocations
10061008

10071009
[Functions](#Function-Definition) can be used to describe services and their operations that need to be invoked during
10081010
workflow execution. They can be referenced by states [action definitions](#Action-Definition) to clearly
@@ -1055,10 +1057,168 @@ For example:
10551057
}
10561058
```
10571059

1058-
Note that the referenced function definition type in this case must be `rest` (default type).
1060+
Note that the referenced function definition type in this case must be `openapi` (default type).
10591061

10601062
For more information about functions, reference the [Functions definitions](#Function-Definition) section.
10611063

1064+
#### Using functions for RESTful Service Invocations
1065+
1066+
The specification also supports describing REST invocations in the [functions definition](#Function-Definition) using [OpenAPI Paths Object](https://spec.openapis.org/oas/v3.1.0#paths-object).
1067+
1068+
Here is an example function definition for REST requests with method `GET` and request target corresponding with [URI Template](https://www.rfc-editor.org/rfc/rfc6570.html) `/users/{id}`:
1069+
1070+
```json
1071+
{
1072+
"functions":[
1073+
{
1074+
"name":"queryUserById",
1075+
"operation": {
1076+
"/users": {
1077+
"get": {
1078+
"parameters": [{
1079+
"name": "id",
1080+
"in": "path",
1081+
"required": true
1082+
}]
1083+
}
1084+
}
1085+
},
1086+
"type":"rest"
1087+
}
1088+
]
1089+
}
1090+
```
1091+
1092+
Note that the [Function Definition](#Function-Definition)'s `operation` property must follow the OpenAPI Paths Object specification definition.
1093+
1094+
The function can be referenced during workflow execution when the invocation of the REST service is desired. For example:
1095+
1096+
```json
1097+
{
1098+
"states":[
1099+
{
1100+
"name":"QueryUserInfo",
1101+
"type":"operation",
1102+
"actions":[
1103+
{
1104+
"functionRef":"queryUserById",
1105+
"arguments":{
1106+
"id":"${ .user.id }"
1107+
}
1108+
}
1109+
],
1110+
"end":true
1111+
}
1112+
]
1113+
}
1114+
```
1115+
1116+
Example of the `POST` request sending the state data as part of the body:
1117+
1118+
```json
1119+
{
1120+
"functions":[
1121+
{
1122+
"name": "createUser",
1123+
"operation": {
1124+
"/users": {
1125+
"post": {
1126+
"requestBody": {
1127+
"content": {
1128+
"application/json": {
1129+
"schema": {
1130+
"type": "object",
1131+
"properties": {
1132+
"id": {
1133+
"type": "string"
1134+
},
1135+
"name": {
1136+
"type": "string"
1137+
},
1138+
"email": {
1139+
"type": "string"
1140+
}
1141+
},
1142+
"required": ["name", "email"]
1143+
}
1144+
}
1145+
}
1146+
}
1147+
}
1148+
}
1149+
},
1150+
"type": "rest"
1151+
}
1152+
]
1153+
}
1154+
```
1155+
1156+
Note that the `requestBody` content schema is described inline rather than a reference to an external document.
1157+
1158+
You can reference the `createUser` function and filter the input data to invoke it, as shown in the example below:
1159+
1160+
```json
1161+
{
1162+
"order":{
1163+
"id":"1234N",
1164+
"products":[
1165+
{
1166+
"name":"Product 1"
1167+
}
1168+
]
1169+
},
1170+
"user":{
1171+
"name":"John Doe",
1172+
1173+
}
1174+
}
1175+
```
1176+
1177+
Function invocation example:
1178+
1179+
```json
1180+
{
1181+
"states":[
1182+
{
1183+
"name":"CreateNewUser",
1184+
"type":"operation",
1185+
"actions":[
1186+
{
1187+
"functionRef":"createUser",
1188+
"actionDataFilter":{
1189+
"fromStateData":"${ .user }",
1190+
"toStateData":"${ .user.id }"
1191+
}
1192+
}
1193+
],
1194+
"end":true
1195+
}
1196+
]
1197+
}
1198+
```
1199+
1200+
In this case, only the contents of the `user` attribute will be passed to the function. The user ID returned by the REST request body will then be added to the state data:
1201+
1202+
```json
1203+
{
1204+
"order":{
1205+
"id":"1234N",
1206+
"products":[
1207+
{
1208+
"name":"Product 1"
1209+
}
1210+
]
1211+
},
1212+
"user":{
1213+
"id":"5678U",
1214+
"name":"John Doe",
1215+
1216+
}
1217+
}
1218+
```
1219+
1220+
The specification does not support the [Security Requirement Object](https://spec.openapis.org/oas/v3.1.0#security-requirement-object). To define authentication for the REST operation, use the [Auth Definition](#Auth-Definition). If provided, this field is ignored.
1221+
10621222
#### Using Functions for Async API Service Invocations
10631223

10641224
[Functions](#Function-Definition) can be used to invoke PUBLISH and SUBSCRIBE operations on a message broker documented by the [Async API Specification](https://www.asyncapi.com/docs/specifications/v2.1.0).
@@ -3189,11 +3349,24 @@ Note that `transition` and `end` properties are mutually exclusive, meaning that
31893349
| Parameter | Description | Type | Required |
31903350
| --- | --- | --- | --- |
31913351
| name | Unique function name | string | yes |
3192-
| operation | If type is `rest`, <path_to_openapi_definition>#<operation_id>. If type is `asyncapi`, <path_to_asyncapi_definition>#<operation_id>. If type is `rpc`, <path_to_grpc_proto_file>#<service_name>#<service_method>. If type is `graphql`, <url_to_graphql_endpoint>#<literal \"mutation\" or \"query\">#<query_or_mutation_name>. If type is `odata`, <URI_to_odata_service>#<Entity_Set_Name>. If type is `expression`, defines the workflow expression. | string | yes |
3193-
| type | Defines the function type. Can be either `rest`, `asyncapi`, `rpc`, `graphql`, `odata`, `expression`, or [`custom`](#defining-custom-function-types). Default is `rest` | enum | no |
3352+
| operation | See the table "Function Operation description by type" below. | string or object | yes |
3353+
| type | Defines the function type. Can be either `rest`, `openapi`, `asyncapi`, `rpc`, `graphql`, `odata`, `expression`, or [`custom`](#defining-custom-function-types). Default is `openapi` | enum | no |
31943354
| authRef | References an [auth definition](#Auth-Definition) name to be used to access to resource defined in the operation parameter | string | no |
31953355
| [metadata](#Workflow-Metadata) | Metadata information. Can be used to define custom function information | object | no |
31963356

3357+
Function Operation description by type:
3358+
3359+
| Type | Operation Description |
3360+
| ---- | --------- |
3361+
| `openapi` | <path_to_openapi_definition>#<operation_id> |
3362+
| `rest` | [OpenAPI Paths Object](https://spec.openapis.org/oas/v3.1.0#paths-object) definition |
3363+
| `asyncapi` | <path_to_asyncapi_definition>#<operation_id> |
3364+
| `rpc` | <path_to_grpc_proto_file>#<service_name>#<service_method> |
3365+
| `graphql` | <url_to_graphql_endpoint>#<literal \"mutation\" or \"query\">#<query_or_mutation_name> |
3366+
| `odata` | <URI_to_odata_service>#<Entity_Set_Name> |
3367+
| `expression` | defines the workflow expression |
3368+
| `custom` | see [Defining custom function types](#defining-custom-function-types)
3369+
31973370
<details><summary><strong>Click to view example definition</strong></summary>
31983371
<p>
31993372

@@ -3228,12 +3401,14 @@ operation: https://hellworldservice.api.com/api.json#helloWorld
32283401

32293402
The `name` property defines an unique name of the function definition.
32303403

3231-
The `type` property defines the function type. Its value can be either `rest` or `expression`. Default value is `rest`.
3404+
The `type` property defines the function type. Its value can be either `rest`, `openapi` or `expression`. Default value is `openapi`.
32323405

32333406
Depending on the function `type`, the `operation` property can be:
32343407

3235-
* If `type` is `rest`, a combination of the function/service OpenAPI definition document URI and the particular service operation that needs to be invoked, separated by a '#'.
3408+
* If `type` is `openapi`, a combination of the function/service OpenAPI definition document URI and the particular service operation that needs to be invoked, separated by a '#'.
32363409
For example `https://petstore.swagger.io/v2/swagger.json#getPetById`.
3410+
* If `type` is `rest`, an object definition of the [OpenAPI Paths Object](https://spec.openapis.org/oas/v3.1.0#paths-object).
3411+
For example, see [Using Functions for RESTful Service Invocations](#using-functions-for-rest-service-invocations).
32373412
* If `type` is `asyncapi`, a combination of the AsyncApi definition document URI and the particular service operation that needs to be invoked, separated by a '#'.
32383413
For example `file://streetlightsapi.yaml#onLightMeasured`.
32393414
* If `type` is `rpc`, a combination of the gRPC proto document URI and the particular service name and service method name that needs to be invoked, separated by a '#'.

0 commit comments

Comments
 (0)