Orchestrating the OpenAPI services

This document describes how to call REST services using an OpenAPI specification file.

Prerequisites

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:

Example of an OpenAPI function definition
{
   "functions":[
      {
         "name":"myFunction1",
         "operation":"classpath:/myopenapi-file.yaml#myFunction1"
      }
   ]
}

In the previous example function definition, the type attribute can be omitted as the default value is rest.

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 the src/main/resources folder of the application project. The classpath is the default URI scheme. If you do not define a URI scheme as shown in the following examples, the file location is defined as src/main/resources/myopenapifile.yaml:

    /myopenapifile.yaml

  • file: This URI scheme is supported for the files that are located in the file system.

  • http or https: 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:

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:

  1. Define the function references

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

Prerequisites
  • 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.

Procedure
  1. 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 specification
    openapi: 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. The operationId 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.
  2. 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 and subtraction OpenAPI files are stored in the src/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.

Prerequisites
Procedure
  1. 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.

  2. 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 function
    3 Definition of the subtraction function arguments
    4 Reference to the multiplication function
    5 Definition of the multiplication function arguments

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

Prerequisites
Procedure
  1. 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 as subtraction_yaml.

    For more information about how to sanitize file names, see Environment Variables Mapping Rules.

  2. 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 configuration
    quarkus.rest-client.subtraction_yaml.url=http://myserver.com
  3. 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 variables
    quarkus.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 to http://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!