Chapter 3. Managing services 3.1. Configuring OpenAPI services The OpenAPI Specification (OAS) defines a standard, programming language-agnostic interface for HTTP APIs. You can understand a service's capabilities without access to the source code, additional documentation, or network traffic inspection. When you define a service by using the OpenAPI, you can understand and interact with it using minimal implementation logic. Just as interface descriptions simplify lower-level programming, the OpenAPI Specification eliminates guesswork in calling a service. 3.1.1. OpenAPI function definition OpenShift Serverless Logic allows the workflows to interact with remote services using an OpenAPI specfication reference in a function. Example OpenAPI function definition { "functions": [ { "name": "myFunction1", "operation": "classpath:/myopenapi-file.yaml#myFunction1" } ] } The operation attribute is a string composed of the following parameters: URI : The engine uses this to locate the specification file, such as classpath . Operation identifier: You can find this identifier in the OpenAPI specification file. OpenShift Serverless Logic supports the following URI schemes: classpath: Use this for files located in the src/main/resources folder of the application project. classpath is the default URI scheme. If you do not define a URI scheme, the file location is src/main/resources/myopenapifile.yaml . file: Use this for files located in the file system. http or https: Use these for remotely located files. Ensure the OpenAPI specification files are available during build time. OpenShift Serverless Logic uses an internal code generation feature to send requests at runtime. After you build the application image, OpenShift Serverless Logic will not have access to these files. If the OpenAPI service you want to add to the workflow does not have a specification file, you can either create one or update the service to generate and expose the file. 3.1.2. Sending REST requests based on the OpenAPI specification To send REST requests that are based on the OpenAPI specification files, you must perform the following procedures: Define the function references Access the defined functions in the workflow states Prerequisites You have OpenShift Serverless Logic Operator installed on your cluster. You have access to a OpenShift Serverless Logic project with the appropriate roles and permissions to create applications and other workloads in OpenShift Container Platform. You have access to the OpenAPI specification files. Procedure To define the OpenAPI functions: Identify and access the OpenAPI specification files for the services you intend to invoke. Copy the OpenAPI specification files into your workflow service directory, such as src/main/resources/specs . The following example shows the OpenAPI specification for 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 parameters: - in: header name: notUsed schema: type: string required: false requestBody: content: application/json: schema: USDref: '#/components/schemas/MultiplicationOperation' responses: "200": description: OK content: application/json: schema: type: object properties: product: format: float type: number components: schemas: MultiplicationOperation: type: object properties: leftElement: format: float type: number rightElement: format: float type: number To define functions in the workflow, use the operationId from the OpenAPI specification to reference the desired operations in your function definitions. Example function definitions in the temperature conversion application { "functions": [ { "name": "multiplication", "operation": "specs/multiplication.yaml#doOperation" }, { "name": "subtraction", "operation": "specs/subtraction.yaml#doOperation" } ] } Ensure that your function definitions reference the correct paths to the OpenAPI files stored in the src/main/resources/specs directory. To access the defined functions in the workflow states: Define workflow actions to call the function definitions you added. Ensure each action references a function defined earlier. Use the functionRef attribute to refer to the specific function by its name. Map the arguments in the functionRef using the parameters defined in the OpenAPI specification. The following example shows about mapping parameters in the request path instead of request body, you can refer to the following PetStore API example: Example for mapping function arguments in workflow { "states": [ { "name": "SetConstants", "type": "inject", "data": { "subtractValue": 32.0, "multiplyValue": 0.5556 }, "transition": "Computation" }, { "name": "Computation", "actionMode": "sequential", "type": "operation", "actions": [ { "name": "subtract", "functionRef": { "refName": "subtraction", "arguments": { "leftElement": ".fahrenheit", "rightElement": ".subtractValue" } } }, { "name": "multiply", "functionRef": { "refName": "multiplication", "arguments": { "leftElement": ".difference", "rightElement": ".multiplyValue" } } } ], "end": { "terminate": true } } ] } Check the Operation Object section of the OpenAPI specification to understand how to structure parameters in the request. Use jq expressions to extract data from the payload and map it to the required parameters. Ensure the engine maps parameter names according to the OpenAPI specification. For operations requiring parameters in the request path instead of the body, refer to the parameter definitions in the OpenAPI specification. For more information about mapping parameters in the request path instead of request body, you can refer to the following PetStore API example: Example for mapping path parameters { "/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 example, the function definition getPetById is for PetStore OpenAPI specification. 3 Arguments definition. OpenShift Serverless Logic adds the argument petId to the request path before sending a request. 3.1.3. Configuring the endpoint URL of OpenAPI services After accessing the function definitions in workflow states, you can configure the endpoint URL of OpenAPI services. Prerequisites You have access to a OpenShift Serverless Logic project with the appropriate roles and permissions to create applications and other workloads in OpenShift Container Platform. You have created your OpenShift Serverless Logic project. You have access to the OpenAPI specification files. You have defined the function definitions in the workflow. You have the access to the defined functions in the workflow states. Procedure Locate the OpenAPI specification file you want to configure. For example, substraction.yaml . Convert the file name into a valid configuration key by replacing special characters, such as . , with underscores and converting letters to lowercase. For example, change substraction.yaml to substraction_yaml . To define the configuration key, use the converted file name as the REST client configuration key. Set this key as an environment variable, as shown in the following example: quarkus.rest-client.subtraction_yaml.url=http://myserver.com To prevent hardcoding URLs in the application.properties file, use environment variable substitution, as shown in the following example: quarkus.rest-client.subtraction_yaml.url=USD{SUBTRACTION_URL:http://myserver.com} In this example: Configuration Key: quarkus.rest-client.subtraction_yaml.url Environment variable: SUBTRACTION_URL Fallback URL: http://myserver.com Ensure that the (SUBTRACTION_URL) environment variable is set in your system or deployment environment. If the variable is not found, the application uses the fallback URL (http://myserver.com) . Add the configuration key and URL substitution to the application.properties file: quarkus.rest-client.subtraction_yaml.url=USD{SUBTRACTION_URL:http://myserver.com} Deploy or restart your application to apply the new configuration settings. 3.2. Configuring OpenAPI services endpoints OpenShift Serverless Logic uses the kogito.sw.operationIdStrategy property to generate the REST client for invoking services defined in OpenAPI documents. This property determines how the configuration key is derived for the REST client configuration. The kogito.sw.operationIdStrategy property supports the following values: FILE_NAME , FULL_URI , FUNCTION_NAME , and SPEC_TITLE . FILE_NAME OpenShift Serverless Logic uses the OpenAPI document file name to create the configuration key. The key is based on the file name, where special characters are replaced with underscores. Example configuration: quarkus.rest-client.stock_portfolio_svc_yaml.url=http://localhost:8282/ 1 1 The OpenAPI File Path is src/main/resources/openapi/stock-portfolio-svc.yaml . The generated key that configures the URL for the REST client is stock_portfolio_svc_yaml FULL_URI OpenShift Serverless Logic uses the complete URI path of the OpenAPI document as the configuration key. The full URI is sanitized to form the key. Example for Serverless Workflow { "id": "myworkflow", "functions": [ { "name": "myfunction", "operation": "https://my.remote.host/apicatalog/apis/123/document" } ] ... } Example configuration: quarkus.rest-client.apicatalog_apis_123_document.url=http://localhost:8282/ 1 1 The URI path is https://my.remote.host/apicatalog/apis/123/document . The generated key that configures the URL for the REST client is apicatalog_apis_123_document . FUNCTION_NAME OpenShift Serverless Logic combines the workflow ID and the function name referencing the OpenAPI document to generate the configuration key. Example for Serverless Workflow { "id": "myworkflow", "functions": [ { "name": "myfunction", "operation": "https://my.remote.host/apicatalog/apis/123/document" } ] ... } Example configuration: quarkus.rest-client.myworkflow_myfunction.url=http://localhost:8282/ 1 1 The workflow ID is myworkflow . The function name is myfunction . The generated key that configures the URL for the REST client is myworkflow_myfunction . SPEC_TITLE OpenShift Serverless Logic uses the info.title value from the OpenAPI document to create the configuration key. The title is sanitized to form the key. Example for OpenAPI document openapi: 3.0.3 info: title: stock-service API version: 2.0.0-SNAPSHOT paths: /stock-price/{symbol}: ... Example configuration: quarkus.rest-client.stock-service_API.url=http://localhost:8282/ 1 1 The OpenAPI document title is stock-service API . The generated key that configures the URL for the REST client is stock-service_API . 3.2.1. Using URI alias As an alternative to the kogito.sw.operationIdStrategy property, you can assign an alias to a URI by using the workflow-uri-definitions custom extension. This alias simplifies the configuration process and can be used as a configuration key in REST client settings and function definitions. The workflow-uri-definitions extension allows you to map a URI to an alias, which you can reference throughout the workflow and in your configuration files. This approach provides a centralized way to manage URIs and their configurations. Prerequisites You have access to a OpenShift Serverless Logic project with the appropriate roles and permissions to create applications and other workloads in OpenShift Container Platform. You have access to the OpenAPI specification files. Procedure Add the workflow-uri-definitions extension to your workflow. Within this extension, create aliases for your URIs. Example workflow { "extensions": [ { "extensionid": "workflow-uri-definitions", 1 "definitions": { "remoteCatalog": "https://my.remote.host/apicatalog/apis/123/document" 2 } } ], "functions": [ 3 { "name": "operation1", "operation": "remoteCatalog#operation1" }, { "name": "operation2", "operation": "remoteCatalog#operation2" } ] } 1 Set the extension ID to workflow-uri-definitions . 2 Set the alias definition by mapping the remoteCatalog alias to a URI, for example, https://my.remote.host/apicatalog/apis/123/document URI. 3 Set the function operations by using the remoteCatalog alias with the operation identifiers, for example, operation1 and operation2 operation identifiers. In the application.properties file, configure the REST client by using the alias defined in the workflow. Example property quarkus.rest-client.remoteCatalog.url=http://localhost:8282/ In the example, the configuration key is set to quarkus.rest-client.remoteCatalog.url , and the URL is set to http://localhost:8282/ , which the REST clients use by referring to the remoteCatalog alias. In your workflow, use the alias when defining functions that operate on the URI. Example Workflow (continued): { "functions": [ { "name": "operation1", "operation": "remoteCatalog#operation1" }, { "name": "operation2", "operation": "remoteCatalog#operation2" } ] } 3.3. Troubleshooting services Efficient troubleshooting of the HTTP-based function invocations, such as those using OpenAPI functions, is crucial for maintaining workflow orchestrations. To diagnose issues, you can trace HTTP requests and responses. 3.3.1. Tracing HTTP requests and responses OpenShift Serverless Logic uses the Apache HTTP client to the trace HTTP requests and responses. Prerequisites You have access to an OpenShift Serverless Logic project with the appropriate roles and permissions to create applications and other workloads in OpenShift Container Platform. You have access to the OpenAPI specification files. You have access to the workflow definition and instance IDs for correlating HTTP requests and responses. You have access to the log configuration of the application where the HTTP service invocations are occurring Procedure To trace HTTP requests and responses, OpenShift Serverless Logic uses the Apache HTTP client by setting the following property: # Turning HTTP tracing on quarkus.log.category."org.apache.http".level=DEBUG Add the following configuration to your application's application.properties file to turn on debugging for the Apache HTTP Client: quarkus.log.category."org.apache.http".level=DEBUG Restart your application to propagate the log configuration changes. After restarting, check the logs for HTTP request traces. Example logs of a traced HTTP request 2023-09-25 19:00:55,242 DEBUG Executing request POST /v2/models/yolo-model/infer HTTP/1.1 2023-09-25 19:00:55,243 DEBUG http-outgoing-0 >> POST /v2/models/yolo-model/infer HTTP/1.1 2023-09-25 19:00:55,243 DEBUG http-outgoing-0 >> Accept: application/json 2023-09-25 19:00:55,243 DEBUG http-outgoing-0 >> Content-Type: application/json 2023-09-25 19:00:55,243 DEBUG http-outgoing-0 >> kogitoprocid: inferencepipeline 2023-09-25 19:00:55,243 DEBUG http-outgoing-0 >> kogitoprocinstanceid: 85114b2d-9f64-496a-bf1d-d3a0760cde8e 2023-09-25 19:00:55,243 DEBUG http-outgoing-0 >> kogitoprocist: Active 2023-09-25 19:00:55,243 DEBUG http-outgoing-0 >> kogitoproctype: SW 2023-09-25 19:00:55,243 DEBUG http-outgoing-0 >> kogitoprocversion: 1.0 2023-09-25 19:00:55,243 DEBUG http-outgoing-0 >> Content-Length: 23177723 2023-09-25 19:00:55,244 DEBUG http-outgoing-0 >> Host: yolo-model-opendatahub-model.apps.trustyai.dzzt.p1.openshiftapps.com Check the logs for HTTP response traces following the request logs. Example logs of a traced HTTP response 2023-09-25 19:01:00,738 DEBUG http-outgoing-0 << "HTTP/1.1 500 Internal Server Error[\r][\n]" 2023-09-25 19:01:00,738 DEBUG http-outgoing-0 << "content-type: application/json[\r][\n]" 2023-09-25 19:01:00,738 DEBUG http-outgoing-0 << "date: Mon, 25 Sep 2023 19:01:00 GMT[\r][\n]" 2023-09-25 19:01:00,738 DEBUG http-outgoing-0 << "content-length: 186[\r][\n]" 2023-09-25 19:01:00,738 DEBUG http-outgoing-0 << "set-cookie: 276e4597d7fcb3b2cba7b5f037eeacf5=5427fafade21f8e7a4ee1fa6c221cf40; path=/; HttpOnly; Secure; SameSite=None[\r][\n]" 2023-09-25 19:01:00,738 DEBUG http-outgoing-0 << "[\r][\n]" 2023-09-25 19:01:00,738 DEBUG http-outgoing-0 << "{"code":13, "message":"Failed to load Model due to adapter error: Error calling stat on model file: stat /models/yolo-model__isvc-1295fd6ba9/yolov5s-seg.onnx: no such file or directory"}"

Commands:
{ "functions": [ { "name": "myFunction1", "operation": "classpath:/myopenapi-file.yaml#myFunction1" } ] }
openapi: 3.0.3 info: title: Generated API version: "1.0" paths: /: post: operationId: doOperation parameters: - in: header name: notUsed schema: type: string required: false requestBody: content: application/json: schema: USDref: '#/components/schemas/MultiplicationOperation' responses: "200": description: OK content: application/json: schema: type: object properties: product: format: float type: number components: schemas: MultiplicationOperation: type: object properties: leftElement: format: float type: number rightElement: format: float type: number
{ "functions": [ { "name": "multiplication", "operation": "specs/multiplication.yaml#doOperation" }, { "name": "subtraction", "operation": "specs/subtraction.yaml#doOperation" } ] }
{ "states": [ { "name": "SetConstants", "type": "inject", "data": { "subtractValue": 32.0, "multiplyValue": 0.5556 }, "transition": "Computation" }, { "name": "Computation", "actionMode": "sequential", "type": "operation", "actions": [ { "name": "subtract", "functionRef": { "refName": "subtraction", "arguments": { "leftElement": ".fahrenheit", "rightElement": ".subtractValue" } } }, { "name": "multiply", "functionRef": { "refName": "multiplication", "arguments": { "leftElement": ".difference", "rightElement": ".multiplyValue" } } } ], "end": { "terminate": true } } ] }
{ "/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" } } ] } } }
{ "name": "CallPetStore", 1 "actionMode": "sequential", "type": "operation", "actions": [ { "name": "getPet", "functionRef": { "refName": "getPetById", 2 "arguments": { 3 "petId": ".petId" } } } ] }
quarkus.rest-client.subtraction_yaml.url=http://myserver.com
quarkus.rest-client.subtraction_yaml.url=USD{SUBTRACTION_URL:http://myserver.com}
quarkus.rest-client.subtraction_yaml.url=USD{SUBTRACTION_URL:http://myserver.com}
quarkus.rest-client.stock_portfolio_svc_yaml.url=http://localhost:8282/ 1
{ "id": "myworkflow", "functions": [ { "name": "myfunction", "operation": "https://my.remote.host/apicatalog/apis/123/document" } ] }
quarkus.rest-client.apicatalog_apis_123_document.url=http://localhost:8282/ 1
{ "id": "myworkflow", "functions": [ { "name": "myfunction", "operation": "https://my.remote.host/apicatalog/apis/123/document" } ] }
quarkus.rest-client.myworkflow_myfunction.url=http://localhost:8282/ 1
openapi: 3.0.3 info: title: stock-service API version: 2.0.0-SNAPSHOT paths: /stock-price/{symbol}:
quarkus.rest-client.stock-service_API.url=http://localhost:8282/ 1
{ "extensions": [ { "extensionid": "workflow-uri-definitions", 1 "definitions": { "remoteCatalog": "https://my.remote.host/apicatalog/apis/123/document" 2 } } ], "functions": [ 3 { "name": "operation1", "operation": "remoteCatalog#operation1" }, { "name": "operation2", "operation": "remoteCatalog#operation2" } ] }
quarkus.rest-client.remoteCatalog.url=http://localhost:8282/
{ "functions": [ { "name": "operation1", "operation": "remoteCatalog#operation1" }, { "name": "operation2", "operation": "remoteCatalog#operation2" } ] }
Turning HTTP tracing on quarkus.log.category."org.apache.http".level=DEBUG
quarkus.log.category."org.apache.http".level=DEBUG
2023-09-25 19:00:55,242 DEBUG Executing request POST /v2/models/yolo-model/infer HTTP/1.1 2023-09-25 19:00:55,243 DEBUG http-outgoing-0 >> POST /v2/models/yolo-model/infer HTTP/1.1 2023-09-25 19:00:55,243 DEBUG http-outgoing-0 >> Accept: application/json 2023-09-25 19:00:55,243 DEBUG http-outgoing-0 >> Content-Type: application/json 2023-09-25 19:00:55,243 DEBUG http-outgoing-0 >> kogitoprocid: inferencepipeline 2023-09-25 19:00:55,243 DEBUG http-outgoing-0 >> kogitoprocinstanceid: 85114b2d-9f64-496a-bf1d-d3a0760cde8e 2023-09-25 19:00:55,243 DEBUG http-outgoing-0 >> kogitoprocist: Active 2023-09-25 19:00:55,243 DEBUG http-outgoing-0 >> kogitoproctype: SW 2023-09-25 19:00:55,243 DEBUG http-outgoing-0 >> kogitoprocversion: 1.0 2023-09-25 19:00:55,243 DEBUG http-outgoing-0 >> Content-Length: 23177723 2023-09-25 19:00:55,244 DEBUG http-outgoing-0 >> Host: yolo-model-opendatahub-model.apps.trustyai.dzzt.p1.openshiftapps.com
2023-09-25 19:01:00,738 DEBUG http-outgoing-0 << "HTTP/1.1 500 Internal Server Error[\r][\n]" 2023-09-25 19:01:00,738 DEBUG http-outgoing-0 << "content-type: application/json[\r][\n]" 2023-09-25 19:01:00,738 DEBUG http-outgoing-0 << "date: Mon, 25 Sep 2023 19:01:00 GMT[\r][\n]" 2023-09-25 19:01:00,738 DEBUG http-outgoing-0 << "content-length: 186[\r][\n]" 2023-09-25 19:01:00,738 DEBUG http-outgoing-0 << "set-cookie: 276e4597d7fcb3b2cba7b5f037eeacf5=5427fafade21f8e7a4ee1fa6c221cf40; path=/; HttpOnly; Secure; SameSite=None[\r][\n]" 2023-09-25 19:01:00,738 DEBUG http-outgoing-0 << "[\r][\n]" 2023-09-25 19:01:00,738 DEBUG http-outgoing-0 << "{"code":13, "message":"Failed to load Model due to adapter error: Error calling stat on model file: stat /models/yolo-model__isvc-1295fd6ba9/yolov5s-seg.onnx: no such file or directory"}"