Skip to content

Commit

Permalink
Merge pull request #652 from ricardozanini/issue-420-2
Browse files Browse the repository at this point in the history
Fixes #420 - Add REST invocation function definition based on OpenAPI Path Object
  • Loading branch information
ricardozanini authored Oct 19, 2023
2 parents 9c015f5 + bfa990d commit 8a7152e
Show file tree
Hide file tree
Showing 3 changed files with 215 additions and 15 deletions.
2 changes: 1 addition & 1 deletion roadmap/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ _Status description:_
| ✔️| 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) |
| ✔️| Add the new `WORKFLOW` reserved keyword to workflow expressions |
| ✔️| Update `ForEach` state iteration parameter example. This parameter is an expression variable, not a JSON property |
| ✔️| Add the new `rest` function type [spec doc](https://github.com/serverlessworkflow/specification/tree/main/specification.md#using-functions-for-restful-service-invocations) |
| ✏️️| Add inline state defs in branches | |
| ✏️️| Update rest function definition | |
| ✏️️| Add "completedBy" functionality | |
| ✏️️| Define workflow context | |
| ✏️️| Start work on TCK | |
Expand Down
35 changes: 27 additions & 8 deletions schema/functions.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,23 +36,23 @@
"pattern": "^[a-z0-9](-?[a-z0-9])*$"
},
"operation": {
"type": "string",
"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.",
"minLength": 1
"type": "object",
"$ref": "#/definitions/operation"
},
"type": {
"type": "string",
"description": "Defines the function type. Is either `rest`, `asyncapi, `rpc`, `graphql`, `odata`, `expression`, or `custom`. Default is `rest`",
"description": "Defines the function type. Is either `rest`, `openapi`,`asyncapi, `rpc`, `graphql`, `odata`, `expression`, or `custom`. Default is `openapi`.",
"enum": [
"rest",
"openapi",
"asyncapi",
"rpc",
"graphql",
"odata",
"expression",
"custom"
],
"default": "rest"
"default": "openapi"
},
"authRef": {
"oneOf": [
Expand All @@ -65,14 +65,14 @@
{
"type": "object",
"description": "Configures both the auth definition used to retrieve the operation's resource and the auth definition used to invoke said operation",
"properties":{
"resource":{
"properties": {
"resource": {
"type": "string",
"description": "References an auth definition to be used to access the resource defined in the operation parameter",
"minLength": 1,
"pattern": "^[a-z0-9](-?[a-z0-9])*$"
},
"invocation":{
"invocation": {
"type": "string",
"description": "References an auth definition to be used to invoke the operation"
}
Expand All @@ -93,6 +93,25 @@
"name",
"operation"
]
},
"operation": {
"oneOf": [
{
"type": "string",
"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.",
"minLength": 1
},
{
"type": "object",
"description": "OpenAPI Path Object definition",
"$comment": "https://spec.openapis.org/oas/v3.1.0#paths-object",
"patternProperties": {
"^/": {
"type": "object"
}
}
}
]
}
}
}
193 changes: 187 additions & 6 deletions specification.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
+ [Using multiple data filters](#using-multiple-data-filters)
+ [Data Merging](#data-merging)
* [Workflow Functions](#workflow-functions)
+ [Using Functions for OpenAPI Service Invocations](#using-functions-for-openapi-service-invocations)
+ [Using Functions for RESTful Service Invocations](#using-functions-for-restful-service-invocations)
+ [Using Functions for Async API Service Invocations](#using-functions-for-async-api-service-invocations)
+ [Using Functions for RPC Service Invocations](#using-functions-for-rpc-service-invocations)
Expand Down Expand Up @@ -994,8 +995,9 @@ They can be referenced by their domain-specific names inside workflow [states](#

Reference the following sections to learn more about workflow functions:

* [Using functions for RESTful service invocations](#Using-Functions-for-RESTful-Service-Invocations)
* [Using Functions for Async API Service Invocations](#Using-Functions-for-Async-API-Service-Invocations)
* [Using functions for OpenAPI Service invocations](#using-functions-for-openapi-service-invocations)
+ [Using functions for RESTful Service Invocations](#using-functions-for-rest-service-invocations)
* [Using functions for Async API Service Invocations](#Using-Functions-for-Async-API-Service-Invocations)
* [Using functions for gRPC service invocation](#Using-Functions-For-RPC-Service-Invocations)
* [Using functions for GraphQL service invocation](#Using-Functions-For-GraphQL-Service-Invocations)
* [Using Functions for OData Service Invocations](#Using-Functions-for-OData-Service-Invocations)
Expand All @@ -1005,7 +1007,7 @@ Reference the following sections to learn more about workflow functions:
We can define if functions are invoked sync or async. Reference
the [functionRef](#FunctionRef-Definition) to learn more on how to do this.

#### Using Functions for RESTful Service Invocations
#### Using Functions for OpenAPI Service Invocations

[Functions](#Function-Definition) can be used to describe services and their operations that need to be invoked during
workflow execution. They can be referenced by states [action definitions](#Action-Definition) to clearly
Expand Down Expand Up @@ -1058,10 +1060,168 @@ For example:
}
```

Note that the referenced function definition type in this case must be `rest` (default type).
Note that the referenced function definition type in this case must be `openapi` (default type).

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

#### Using functions for RESTful Service Invocations

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).

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}`:

```json
{
"functions":[
{
"name":"queryUserById",
"operation": {
"/users": {
"get": {
"parameters": [{
"name": "id",
"in": "path",
"required": true
}]
}
}
},
"type":"rest"
}
]
}
```

Note that the [Function Definition](#Function-Definition)'s `operation` property must follow the [OpenAPI Paths Object](https://spec.openapis.org/oas/v3.1.0#paths-object) specification definition.

The function can be referenced during workflow execution when the invocation of the REST service is desired. For example:

```json
{
"states":[
{
"name":"QueryUserInfo",
"type":"operation",
"actions":[
{
"functionRef":"queryUserById",
"arguments":{
"id":"${ .user.id }"
}
}
],
"end":true
}
]
}
```

Example of the `POST` request sending the state data as part of the body:

```json
{
"functions":[
{
"name": "createUser",
"type": "rest",
"operation": {
"/users": {
"post": {
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"email": {
"type": "string"
}
},
"required": ["name", "email"]
}
}
}
}
}
}
}
}
]
}
```

Note that the `requestBody` [`content` attribute](https://spec.openapis.org/oas/v3.1.0#fixed-fields-10) is described inline rather than a reference to an external document.

You can reference the `createUser` function and filter the input data to invoke it. Given the workflow input data:

```json
{
"order":{
"id":"1234N",
"products":[
{
"name":"Product 1"
}
]
},
"user":{
"name":"John Doe",
"email":"[email protected]"
}
}
```

Function invocation example:

```json
{
"states":[
{
"name":"CreateNewUser",
"type":"operation",
"actions":[
{
"functionRef":"createUser",
"actionDataFilter":{
"fromStateData":"${ .user }",
"toStateData":"${ .user.id }"
}
}
],
"end":true
}
]
}
```

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:

```json
{
"order":{
"id":"1234N",
"products":[
{
"name":"Product 1"
}
]
},
"user":{
"id":"5678U",
"name":"John Doe",
"email":"[email protected]"
}
}
```

The specification does not support the [Security Requirement Object](https://spec.openapis.org/oas/v3.1.0#security-requirement-object) since its redundat to function [Auth Definition](#Auth-Definition). If provided, this field is ignored.

#### Using Functions for Async API Service Invocations

[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).
Expand Down Expand Up @@ -3197,12 +3357,31 @@ Note that `transition` and `end` properties are mutually exclusive, meaning that

| Parameter | Description | Type | Required |
| --- | --- | --- | --- |
<<<<<<< HEAD
| name | Unique function name. Must follow the [Serverless Workflow Naming Convention](#naming-convention) | string | yes |
| 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 |
| 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 |
=======
| name | Unique function name. Must follow the [Serverless Workflow Naming Convention](#naming-convention) | string | yes |
| operation | See the table "Function Operation description by type" below. | string or object | yes |
| 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 |
>>>>>>> 2351b12 (Fixes #420 - Add REST invocation function definition based on OpenAPI Path Item)
| authRef | References an [auth definition](#Auth-Definition) name to be used to access to resource defined in the operation parameter | string | no |
| [metadata](#Workflow-Metadata) | Metadata information. Can be used to define custom function information | object | no |

Function Operation description by type:

| Type | Operation Description |
| ---- | --------- |
| `openapi` | <path_to_openapi_definition>#<operation_id> |
| `rest` | [OpenAPI Paths Object](https://spec.openapis.org/oas/v3.1.0#paths-object) definition |
| `asyncapi` | <path_to_asyncapi_definition>#<operation_id> |
| `rpc` | <path_to_grpc_proto_file>#<service_name>#<service_method> |
| `graphql` | <url_to_graphql_endpoint>#<literal \"mutation\" or \"query\">#<query_or_mutation_name> |
| `odata` | <URI_to_odata_service>#<Entity_Set_Name> |
| `expression` | defines the workflow expression |
| `custom` | see [Defining custom function types](#defining-custom-function-types)

<details><summary><strong>Click to view example definition</strong></summary>
<p>

Expand Down Expand Up @@ -3237,12 +3416,14 @@ operation: https://hellworldservice.api.com/api.json#helloWorld

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

The `type` property defines the function type. Its value can be either `rest` or `expression`. Default value is `rest`.
The `type` enum property defines the function type. Its value can be either `rest`, `openapi` or `expression`. Default value is `openapi`.

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

* 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 '#'.
* 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 '#'.
For example `https://petstore.swagger.io/v2/swagger.json#getPetById`.
* If `type` is `rest`, an object definition of the [OpenAPI Paths Object](https://spec.openapis.org/oas/v3.1.0#paths-object).
For example, see [Using Functions for RESTful Service Invocations](#using-functions-for-rest-service-invocations).
* 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 '#'.
For example `file://streetlightsapi.yaml#onLightMeasured`.
* 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 '#'.
Expand Down

0 comments on commit 8a7152e

Please sign in to comment.