Orchestrating OpenAPI Services
This document describes how to call REST services using an OpenAPI specification file.
-
A workflow project is created.
For more information about creating a workflow project, see Creating your first Serverless Workflow service.
-
You have installed the tooling.
For more information about the tooling, see Getting familiar with SonataFlow tooling.
OpenAPI function definition
SonataFlow follows the CNCF Serverless Workflow specification function definition to send requests to remote REST services using an OpenAPI specification reference as shown in the following example:
{
"functions":[
{
"name":"myFunction1",
"operation":"classpath:/myopenapi-file.yaml#myFunction1"
}
]
}
In the previous example function definition, the |
In the previous example, the operation
attribute is a string, which is composed using the following parameters:
-
URI that the engine uses to locate the specification file, such as
classpath
. -
Operation identifier. You can find the operation identifier in the OpenAPI specification file.
SonataFlow supports the following URI schemes:
-
classpath
: This URI scheme is supported for the files that are located in thesrc/main/resources
folder of the application project. Theclasspath
is the default URI scheme. If you do not define a URI scheme as shown in the following examples, the file location is defined assrc/main/resources/myopenapifile.yaml
:/myopenapifile.yaml
-
file
: This URI scheme is supported for the files that are located in the file system. -
http
orhttps
: This URI scheme is supported for remotely located files.
The OpenAPI specification files must be available during the build time, because SonataFlow leverages the internal code generation feature to send requests on runtime. Therefore, once you have built the application image, SonataFlow will not have access to the files. For more information about building workflow images, see Building workflow images using Quarkus CLI. |
To get started with orchestrating the OpenAPI based services, you must locate the REST services OpenAPI specification files. Usually, REST services expose their interface using the OpenAPI specification. For example, see the Petstore API.
In case, the OpenAPI service that you want to add to the workflow does not contain a specification file, you can either create a specification file or update the OpenAPI service to generate and expose the file.
There are various REST frameworks that support the OpenAPI specification generation, including:
-
Java
-
Go
-
Python
-
.NET
-
TypeScript
-
PHP
If you do not have access to the REST service, which is used to add the OpenAPI specification generator, you can use Swagger Inspector. The Swagger Inspector generates the specification file based on HTTP traffic.
Sending REST requests based on the OpenAPI specification
To send REST requests that are based on the OpenAPI specification files, you need to perform the following procedures:
-
Define the function references
-
Access the defined functions in the workflow states
Defining OpenAPI functions in a workflow
To send requests with the REST calls, first you need to define the OpenAPI functions.
-
You have access to the OpenAPI specification files.
Once you have access to the OpenAPI specification files, you can define the functions in the workflow.
You can also use the serverless-workflow-temperature-conversion
example application as a reference.
-
Copy the OpenAPI specification files of the service(s) you want to invoke into your workflow service directory, which can be
src/main/resources/specs
.In the
serverless-workflow-temperature-conversion
example application, the services that are invoked include multiplication and subtraction services.The OpenAPI specification files in
serverless-workflow-temperature-conversion
example application are extracted from the running multiplication and subtraction services. The REST Quarkus applications expose the related OpenAPI specification files using the Quarkus Swagger extension.Following is an example OpenAPI specification of the multiplication REST service:
Example multiplication REST service OpenAPI specificationopenapi: 3.0.3 info: title: Generated API version: "1.0" paths: /: post: operationId: doOperation (1) parameters: - in: header name: notUsed schema: type: string required: false requestBody: content: application/json: schema: $ref: '#/components/schemas/MultiplicationOperation' responses: "200": description: OK content: application/json: schema: type: object properties: product: format: float type: number components: schemas: MultiplicationOperation: (2) type: object properties: leftElement: format: float type: number rightElement: format: float type: number
1 operationId
of the REST operation. TheoperationId
in an OpenAPI specification is necessary to add a reference to the operation that you want to use in REST invocation.2 Data structure of the REST operation. -
Use the same
operationId
to compose the final URI in the function definition as shown in the following example:OpenAPI functions definition in the Temperature Conversion example{ "functions": [ { "name": "multiplication", "operation": "specs/multiplication.yaml#doOperation" (1) }, { "name": "subtraction", "operation": "specs/subtraction.yaml#doOperation" (2) } ] }
1 Function definition for OpenAPI multiplication function 2 Function definition for OpenAPI subtraction function Note that in the previous example, the
multiplication
andsubtraction
OpenAPI files are stored in thesrc/main/resources/specs
folder of the application.
Accessing functions in the workflow states
After defining the function definitions, you can access the defined functions in the workflow states.
-
You have defined the OpenAPI functions in a workflow. For more information, see Defining OpenAPI functions in a workflow.
-
Use a workflow action to call a function definition that you added.
Any workflow action that consists of a similar approach of referencing the functions that you used in the function definition can call a defined function.
-
To map the arguments of a function, you can refer to the parameters described in the Operation Object section of OpenAPI specification.
Also, the engine maps the parameter naming according to the OpenAPI specification. After that, you can use jq expressions to extract the payload data and map the data to the required parameters.
In the following example, the parameters are part of the request body:
Example of mapping a function arguments{ "states": [ { "name": "SetConstants", "type": "inject", "data": { "subtractValue": 32.0, "multiplyValue": 0.5556 }, "transition": "Computation" }, { "name": "Computation", "actionMode": "sequential", "type": "operation", (1) "actions": [ { "name": "subtract", "functionRef": { "refName": "subtraction", (2) "arguments": { (3) "leftElement": ".fahrenheit", "rightElement": ".subtractValue" } } }, { "name": "multiply", "functionRef": { "refName": "multiplication", (4) "arguments": { (5) "leftElement": ".difference", "rightElement": ".multiplyValue" } } } ], "end": { "terminate": "true" } } ] }
1 Operation State definition 2 Reference to the subtraction
function3 Definition of the subtraction
function arguments4 Reference to the multiplication
function5 Definition of the multiplication
function argumentsIn the
serverless-workflow-temperature-conversion
example application, the functions are called sequentially in an Operation State as shown in the previous example.For more information about mapping parameters in the request path instead of request body, you can refer to the following PetStore API example:
Example Petstore OpenAPI specification excerpt{ "/pet/{petId}": { "get": { "tags": [ "pet" ], "summary": "Find pet by ID", "description": "Returns a single pet", "operationId": "getPetById", "parameters": [ { "name": "petId", "in": "path", "description": "ID of pet to return", "required": true, "schema": { "type": "integer", "format": "int64" } } ] } } }
Following is an example invocation of a function, in which only one parameter named
petId
is added in the request path:Example of calling the PetStore function{ "name": "CallPetStore", (1) "actionMode": "sequential", "type": "operation", "actions": [ { "name": "getPet", "functionRef": { "refName": "getPetById", (2) "arguments": { (3) "petId": ".petId" } } } ] }
1 State definition, such as CallPetStore
.2 Function definition reference. In the previous example, the function definition getPetById
is for PetStore OpenAPI specification.3 Arguments definition. In the previous example, SonataFlow adds the argument petId
to the request path before sending a request.
Configuring the endpoint URL of OpenAPI services
After accessing the function definitions in workflow states, you can configure the endpoint URL of OpenAPI services.
SonataFlow uses the Quarkus OpenAPI Generator extension and the Quarkus REST Client, and all the configurations are based on these components, which are backed using Quarkus Configuration.
-
You have defined the function definitions in the workflow. For more information, see Defining OpenAPI functions in a workflow.
-
You have the access to the defined functions in the workflow states. For more information, see Accessing functions in the workflow states.
-
To configure the endpoints, you must use the sanitized OpenAPI specification file name as the REST client configuration key. The configuration key must be set as a valid environment variable.
For example, a file named as
subtraction.yaml
contains the configuration key assubtraction_yaml
.For more information about how to sanitize file names, see Environment Variables Mapping Rules.
-
You can use the same configuration key on each property that is related to the Quarkus REST client as shown in the following URL configuration example:
Example of URL configurationquarkus.rest-client.subtraction_yaml.url=http://myserver.com
-
To avoid hardcoding the URL in the
application.properties
file, you can use environment variables substitution, as shown in the following example:Example of URL configuration with environment variablesquarkus.rest-client.subtraction_yaml.url=${SUBTRACTION_URL:http://myserver.com}
In case the environment variable
SUBTRACTION_URL
is not found in the previous example, the URL redirects tohttp://myserver.com
.You can also use environment variables to configure the application in Kubernetes environments, in which variables can be injected into the pod of the application.
For more information about endpoints and general configuration, see Configuring the OpenAPI services endpoints.
In a future release, SonataFlow will provide support for Kubernetes Service Discovery feature. Using the Kubernetes Service Discovery feature, you can point to a Kubernetes or Knative object that you want to call. After that, the engine will automatically configure the endpoints once you deploy the endpoints in the cluster.
Found an issue?
If you find an issue or any misleading information, please feel free to report it here. We really appreciate it!