Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions antora.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ asciidoc:
attributes:
product_name: OpenShift Serverless Logic
context: OpenShift Serverless Logic
kogito_version_redhat: 1.27.0.Final-redhat-00005
kogito_version_redhat: 1.30.0.Final-redhat-00001
quarkus_platform: com.redhat.quarkus.platform
kogito_sw_ga: >-
org.kie.kogito:kogito-quarkus-serverless-workflow
Expand All @@ -17,15 +17,18 @@ asciidoc:
maven_min_version: 3.8.1
graalvm_min_version: 21.3.0
spec_version: 0.8
vscode_version: 1.46.0
kn_cli_version: 0.21.3
vscode_version: 1.66.0
kn_cli_version: 0.25.0
kie_tools_node_min_version: 16.13.2
kie_tools_pnpm_min_version: 7.0.0
kie_tools_golang_min_version: 1.19
docker_min_version: 20.10.7
docker_compose_min_version: 1.27.2
kogito_devservices_imagename: registry.redhat.io/openshift-serverless-1-tech-preview/logic-data-index-ephemeral-rhel8:1.24.0-11
kogito_examples_repository_url: 'https://github.com/kiegroup/kogito-examples'
kogito_sw_examples_url: https://github.com/kiegroup/kogito-examples/tree/1.27.x/serverless-workflow-examples
kogito_sw_examples_url: https://github.com/kiegroup/kogito-examples/tree/1.30.x/serverless-workflow-examples
kogito_examples_url: 'https://github.com/kiegroup/kogito-examples.git'
kogito_apps_url: https://github.com/kiegroup/kogito-apps/tree/main
quarkus_cli_url: 'https://quarkus.io/guides/cli-tooling'
spec_website_url: 'https://serverlessworkflow.io/'
spec_doc_url: >-
Expand All @@ -48,6 +51,7 @@ asciidoc:
java_install_url: 'https://www.java.com/en/download/help/download_options.html'
maven_install_url: 'https://maven.apache.org/install.html'
docker_install_url: 'https://docs.docker.com/engine/install/'
docker_compose_install_url: https://docs.docker.com/compose/install/
podman_install_url: 'https://docs.podman.io/en/latest/'
kubectl_install_url: 'https://kubernetes.io/docs/tasks/tools/install-kubectl'
kn_cli_install_url: 'https://github.com/knative/client/blob/main/docs/README.md#installing-kn'
Expand Down
1 change: 1 addition & 0 deletions modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
//**** xref:serverless-logic:core/accessing-workflow-metainformation-in-runtime.adoc[Accessing workflow metainformation in runtime]
**** xref:serverless-logic:core/defining-an-input-schema-for-workflows.adoc[Defining an input schema for your workflows]
**** xref:serverless-logic:core/custom-functions-support.adoc[Custom functions for your {context} service]
**** xref:serverless-logic:core/timeouts-support.adoc[Timeouts in {context}]
*** Tooling
**** xref:serverless-logic:tooling/serverless-workflow-editor/swf-editor-overview.adoc[Serverless Workflow editor]
***** xref:serverless-logic:tooling/serverless-workflow-editor/swf-editor-vscode-extension.adoc[VS Code extension for Serverless Workflow editor]
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified modules/serverless-logic/assets/images/core/error_handling.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ NOTE: You can skip the following procedure if you already have a workflow applic
.Clone an example application
[source,shell,subs="attributes+"]
----
git clone --branch main {kogito_sw_examples_git_repo_url}
git clone --branch main {kogito_sw_examples_git_repo_url}
cd kogito-examples/serverless-workflow-examples/serverless-workflow-greeting-quarkus
----

Expand Down Expand Up @@ -67,7 +67,7 @@ After installing the required tooling, you can start building your workflow appl

.Prerequisites
* You have created a Quarkus project.
* Quarkus CLI is installed.
* Quarkus CLI is installed.
For more information about installing the Quarkus CLI, see link:https://quarkus.io/guides/cli-tooling#installing-the-cli[Installing the Quarkus CLI].

.Procedure
Expand Down Expand Up @@ -157,7 +157,7 @@ When it comes to workflows, a small startup footprint is expected, which can be

.Prerequisites
* You have created a Quarkus project.
* Quarkus CLI is installed.
* Quarkus CLI is installed.
For more information about installing the Quarkus CLI, see link:{quarkus_cli_url}[Installing the Quarkus CLI].

.Procedure
Expand Down Expand Up @@ -242,7 +242,7 @@ Example request::
+
[source,shell]
----
curl -X POST -H 'Content-Type:application/json' -H 'Accept:application/json' -d '{"workflowdata" : {"name": "John", "language": "English"}}' http://localhost:8080/jsongreet
curl -X POST -H 'Content-Type:application/json' -H 'Accept:application/json' -d '{"name": "John", "language": "English"}' http://localhost:8080/jsongreet
----
Example response::
+
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ For more information, see link:{knative_issue_url}[How to use locally built dock

In that case, use the `-Dquarkus.container-image.registry=some_of_the_values_above` property to enable Knative fetch the container images from Minikube Docker Daemon.

If you do not use the values, you might need to set the `imagePullPolicy` to `Never` or `IfNotPresent`, otherwise, Minikube pulls the images from a remote registry.
If you do not use the values, you might need to set the `imagePullPolicy` to `Never` or `IfNotPresent`, otherwise, Minikube pulls the images from a remote registry.
This behavior can be avoided by tagging the image using previously listed domains.
====
--
Expand Down Expand Up @@ -294,7 +294,7 @@ http://hello.serverless-workflow-greeting-quarkus.10.103.94.37.sslip.io
.Example request
[source,shell]
----
curl -X POST -H 'Content-Type:application/json' -H 'Accept:application/json' -d '{"workflowdata" : {"name": "John", "language": "English"}}' http://hello.serverless-workflow-greeting-quarkus.10.103.94.37.sslip.io/jsongreet
curl -X POST -H 'Content-Type:application/json' -H 'Accept:application/json' -d '{"name": "John", "language": "English"}' http://hello.serverless-workflow-greeting-quarkus.10.103.94.37.sslip.io/jsongreet
----

.Example response
Expand Down Expand Up @@ -473,7 +473,7 @@ greeting-quarkus-kubectl http://greeting-quarkus-kubectl.serverless-workflow-g
.Access workflow application
[source,shell]
----
curl -X POST -H 'Content-Type:application/json' -H 'Accept:application/json' -d '{"workflowdata" : {"name": "John", "language": "English"}}' http://greeting-quarkus-kubectl.serverless-workflow-greeting-quarkus.10.103.94.37.sslip.io/jsongreet
curl -X POST -H 'Content-Type:application/json' -H 'Accept:application/json' -d '{"name": "John", "language": "English"}' http://greeting-quarkus-kubectl.serverless-workflow-greeting-quarkus.10.103.94.37.sslip.io/jsongreet
----
--

Expand Down Expand Up @@ -555,7 +555,7 @@ greeting-quarkus-cli http://greeting-quarkus-cli.serverless-workflow-greeting-
.Access your workflow application
[source,shell]
----
curl -X POST -H 'Content-Type:application/json' -H 'Accept:application/json' -d '{"workflowdata" : {"name": "John", "language": "English"}}' http://greeting-quarkus-cli.serverless-workflow-greeting-quarkus.10.103.94.37.sslip.io/jsongreet.37.sslip.io/jsongreet
curl -X POST -H 'Content-Type:application/json' -H 'Accept:application/json' -d '{"name": "John", "language": "English"}' http://greeting-quarkus-cli.serverless-workflow-greeting-quarkus.10.103.94.37.sslip.io/jsongreet.37.sslip.io/jsongreet
----
--

Expand Down
156 changes: 146 additions & 10 deletions modules/serverless-logic/pages/core/custom-functions-support.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ The following example shows the declaration of a `java` function:

=== Function Arguments

Your method interface signature must copy the arguments passed by the workflow.
Your method interface signature must copy the arguments passed by the workflow.

For example, if you invoke a function using one argument as follows, then your method signature assumes that the `number` model variable is an integer:

Expand All @@ -114,8 +114,8 @@ For example, if you invoke a function using one argument as follows, then your m
.Example of a `java` function implementation
[source,java]
----
public class MyInterfaceOrClass {
public class MyInterfaceOrClass {

public void myMethod(int number) {
if (number % 2 != 0) {
throw new IllegalArgumentException("Odd situation");
Expand All @@ -126,7 +126,7 @@ public class MyInterfaceOrClass {

As a particular case, if you provide no argument in the workflow definition, the signature of the Java method might include a link:https://github.com/FasterXML/jackson[Jackson's] `JsonNode` parameter. This means that the Java method expects the entire workflow model as input.

When using the following example function reference with no arguments, and if the method signature contains a `JsonNode` parameter, the entire workflow model is passed when the method call is performed.
When using the following example function reference with no arguments, and if the method signature contains a `JsonNode` parameter, the entire workflow model is passed when the method call is performed.

.Example of a `java` function reference with no arguments
[source,json]
Expand Down Expand Up @@ -160,7 +160,7 @@ If your method returns a `JsonNode`, the content of that node is merged into the

The same occurs if your method returns any Java `Object` descendant that is not a primitive wrapper, the Java object is recursively converted to a JSON object and the result is merged into the workflow model (you can use an action data filter to control what is merged).

If your method returns a primitive type or their corresponding wrapper object (int, boolean, long, and so on), then the primitive value is added to the workflow model with the name `response` (you can change that name using an action data filter).
If your method returns a primitive type or their corresponding wrapper object (int, boolean, long, and so on), then the primitive value is added to the workflow model with the name `response` (you can change that name using an action data filter).

If your method returns Java collections, it is converted to a JSON array and added to the workflow model with the name `response` (you can change that name using an action data filter).

Expand All @@ -174,16 +174,14 @@ Therefore, if you need to do so, you can update the signature of methods from pr
[source,java]
----
public class MyInterfaceOrClass {


public JsonNode myMethod(JsonNode workflowData, KogitoProcessContext context ) {
public JsonNode myMethod(JsonNode workflowData, KogitoProcessContext context ) {
// do whatever I want with the JsonNode and the Kogito process context
......
// return the modified content:
return workflowData;
}
}
----
----

.Example of a function accessing Kogito context
[source,java]
Expand All @@ -202,9 +200,147 @@ public class MyInterfaceOrClass {

[WARNING]
====
Avoid using `java` functions to call the external services, instead you can use the xref:serverless-logic:service-orchestration/orchestration-of-openapi-based-services.adoc[services orchestration features].
Avoid using `java` functions to call the external services, instead, you can use the xref:serverless-logic:service-orchestration/orchestration-of-openapi-based-services.adoc[services orchestration features].
====

== Custom function types

You can add your custom types by using the Kogito add-on mechanism. As predefined custom types like xref:serverless-logic:core/custom-functions-support.adoc#con-func-sysout[`sysout`] or xref:serverless-logic:core/custom-functions-support.adoc#con-func-java[`java`], the custom type identifier is the prefix of the operation field of the function definition.

Kogito add-ons relies on the link:{quarkus_guides_base_url}/writing-extensions[Quarkus extensions] mechanism. And the add-on consists of at least two Maven projects:

- The deployment module, which is responsible for generating the code required for the extension to work.
- The runtime module, which includes the non-generated classes that are required for the extension to work.

In the case of a Serverless Workflow custom type, following are the roles of the modules:

- *The deployment project*
+
The deployment project is expected to configure the work item handler used during runtime to perform the logic associated with the custom type.
It must contain a Java class that inherits from `WorkItemTypeHandler`. Its responsibilities are to indicate the custom type identifier (the operation prefix, as indicated earlier) and to set up the `WorkItemNodeFactory` instance passed as a parameter of the `fillWorkItemHandler` method. That instance is included in the Kogito process definition for that Workflow. As a part of this setup, you must indicate the name of the `WorkItemNodeFactory`. You might also provide any relevant metadata for that handler if needed.

- *The runtime project*
+
The runtime project consists of a `WorkflowWorkItemHandler` implementation, which name must match with the one provided to `WorkItemNodeFactory` during the deployment phase, and a `WorkItemHandlerConfig` bean that registers that handler with that name.
+
When a Serverless Workflow function is called, Kogito identifies the proper `WorkflowWorkItemHandler` instance to be used for that function type (using the handler name associated with that type by the deployment project) and then invokes the `internalExecute` method. The `Map` parameter contains the function arguments defined in the workflow, and the `WorkItem` parameter contains the metadata information added to the handler by the deployment project. Hence, the `executeWorkItem` implementation has an access to all the information needed to perform the computational logic intended for that custom type.

=== Custom function type example

Assuming you want to interact, from a workflow file, with a legacy RPC server as the one defined in link:{kogito_sw_examples_url}/serverless-workflow-custom-type/serverless-workflow-custom-rpc-server[this project]. This legacy server supports four simple arithmetic operations: add, minus, multiply and divide, which can be invoked using a custom RPC protocol.

Since this is an uncommon protocol, the workflow cannot handle them by using any of the predefined Serverless Workflow function types. The available options are to use a Java service, which invokes a Java class that knows how to interact with the server, or define a custom type that knows how to interact with the service.

Using the recent approach, you can write a link:{kogito_sw_examples_url}/serverless-workflow-custom-type/serverless-workflow-custom-type-example/src/main/resources/customType.sw.json[workflow file] defining this function.

.RPC Custom function definition example

[source,json]
----
"functions": [
{
"name": "division",
"type": "custom",
"operation": "rpc:division"
}
],
----

The `operation` starts with `rpc`, which is the custom type identifier, and continues with `division`, which denotes the operation that will be executed in the legacy server.

A Kogito addon that defines the `rpc` custom type must be developed for this function definition to be identified. It is consist of a link:{kogito_sw_examples_url}/serverless-workflow-custom-type/serverless-workflow-custom-rpc-deployment[deployment project] and a link:{kogito_sw_examples_url}/serverless-workflow-custom-type/serverless-workflow-custom-rpc[runtime project].

The deployment project is responsible for extending the link:{kogito_sw_examples_url}/serverless-workflow-custom-type/serverless-workflow-custom-rpc-deployment/src/main/java/org/kie/kogito/examples/sw/services/RPCCustomTypeHandler.java[`WorkItemTypeHandler`] and setup the `WorkItemNodeFactory` as follows:

.Example of the RPC function Java implementation

[source,java]
----

import static org.kie.kogito.examples.sw.custom.RPCCustomWorkItemHandler.NAME;
import static org.kie.kogito.examples.sw.custom.RPCCustomWorkItemHandler.OPERATION;

public class RPCCustomTypeHandler extends WorkItemTypeHandler{


@Override
public String type() {
return "rpc";
}

@Override
protected <T extends RuleFlowNodeContainerFactory<T, ?>> WorkItemNodeFactory<T> fillWorkItemHandler(Workflow workflow,
ParserContext context,
WorkItemNodeFactory<T> node,
FunctionDefinition functionDef) {
return node.workName(NAME).metaData(OPERATION, trimCustomOperation(functionDef));
}
}

----

This example setups the name of the `KogitoWorkItemHandler`, adds a metadata key with the name of the remote operation (extracted from the Serverless Workflow function definition operation property), and declares that the custom type is named as `rpc`.

The Runtime project contains the link:{kogito_sw_examples_url}/serverless-workflow-custom-type/serverless-workflow-custom-rpc/src/main/java/org/kie/kogito/examples/sw/custom/RPCCustomWorkItemHandler.java[KogitoWorkItemHandler] and the link:{kogito_sw_examples_url}/serverless-workflow-custom-type/serverless-workflow-custom-rpc/src/main/java/org/kie/kogito/examples/sw/custom/RPCCustomWorkItemHandlerConfig.java[WorkItemHandlerConfig] implementations.

As expected, `RPCCustomWorkItemHandler` implements the `internalExecute` method as follows:

.Example of implementation of the `internalExecute` method

[source, java]
----
@Override
protected Object internalExecute(KogitoWorkItem workItem, Map<String, Object> parameters) {
try {
Iterator<?> iter = parameters.values().iterator();
Map<String, Object> metadata = workItem.getNodeInstance().getNode().getMetaData();
String operationId = (String) metadata.get(OPERATION);
if (operationId == null) {
throw new IllegalArgumentException ("Operation is a mandatory parameter");
}
return CalculatorClient.invokeOperation((String)metadata.getOrDefault(HOST,"localhost"), (int) metadata.getOrDefault(PORT, 8082),
OperationId.valueOf(operationId.toUpperCase()), (Integer)iter.next(), (Integer)iter.next());
} catch (IOException io ) {
throw new UncheckedIOException(io);
}
}
----

The implementation invokes the link:{kogito_sw_examples_url}/serverless-workflow-custom-type/serverless-workflow-custom-rpc-server/src/main/java/org/kie/kogito/examples/sw/custom/CalculatorClient.java#L45-L67[`CalculatorClient.invokeOperation`], a java static method that knows how to interact with the legacy service. You can obtain the operation parameter from the `WorkItem` metadata. The dividend and the divisor parameters are obtained from the Map parameter, which contains the function arguments defined in the workflow file.

.Example of the custom function call from the workflow definition

[source, json]
----
"actions": [
{
"functionRef": {
"refName": "division",
"arguments": {
"dividend": ".dividend",
"divisor" : ".divisor"
}
}

}
----

The `RPCCustomWorkItemHandlerConfig` is a bean that registers the handler name.

.Example of injecting the custom`WorkItemHandler`

[source, java]
----
@Inject
RPCCustomWorkItemHandler handler;

@PostConstruct
void init () {
register(handler.getName(),handler);
}
----


== Additional resources

* xref:serverless-logic:getting-started/cncf-serverless-workflow-specification-support.adoc[CNCF Serverless Workflow specification]
Expand Down
Loading