From 5cb0d3677dee501844b571e93a4429de80fecf67 Mon Sep 17 00:00:00 2001 From: fjtirado Date: Mon, 12 Jan 2026 14:03:53 +0100 Subject: [PATCH] [Fix #1022] Implementing default catalog Signed-off-by: fjtirado --- .../impl/WorkflowApplication.java | 20 +++++++ .../CallFunctionExecutorBuilder.java | 56 +++++++++++-------- .../impl/test/CustomFunctionTest.java | 3 +- ...all-custom-function-cataloged-default.yaml | 13 +++++ 4 files changed, 67 insertions(+), 25 deletions(-) create mode 100644 impl/test/src/test/resources/workflows-samples/call-custom-function-cataloged-default.yaml diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowApplication.java b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowApplication.java index 395b4049..8c880537 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowApplication.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowApplication.java @@ -41,6 +41,7 @@ import io.serverlessworkflow.impl.scheduler.WorkflowScheduler; import io.serverlessworkflow.impl.schema.SchemaValidator; import io.serverlessworkflow.impl.schema.SchemaValidatorFactory; +import java.net.URI; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -78,6 +79,7 @@ public class WorkflowApplication implements AutoCloseable { private final SchedulerListener schedulerListener; private final Optional templateResolver; private final Optional functionReader; + private final URI defaultCatalogURI; private WorkflowApplication(Builder builder) { this.taskFactory = builder.taskFactory; @@ -102,6 +104,7 @@ private WorkflowApplication(Builder builder) { this.secretManager = builder.secretManager; this.templateResolver = builder.templateResolver; this.functionReader = builder.functionReader; + this.defaultCatalogURI = builder.defaultCatalogURI; } public TaskExecutorFactory taskFactory() { @@ -184,6 +187,7 @@ public SchemaValidator getValidator(SchemaInline inline) { private SchedulerListener schedulerListener; private Optional templateResolver; private Optional functionReader; + private URI defaultCatalogURI; private Builder() { ServiceLoader.load(NamedWorkflowAdditionalObject.class) @@ -281,6 +285,15 @@ public Builder withContextFactory(WorkflowModelFactory contextFactory) { return this; } + public Builder withDefaultCatalogURI(String defaultCatalogURI) { + return withDefaultCatalogURI(URI.create(defaultCatalogURI)); + } + + public Builder withDefaultCatalogURI(URI defaultCatalogURI) { + this.defaultCatalogURI = defaultCatalogURI; + return this; + } + public WorkflowApplication build() { if (modelFactory == null) { modelFactory = @@ -344,6 +357,9 @@ public WorkflowApplication build() { } templateResolver = ServiceLoader.load(URITemplateResolver.class).findFirst(); functionReader = ServiceLoader.load(FunctionReader.class).findFirst(); + if (defaultCatalogURI == null) { + defaultCatalogURI = URI.create("https://github.com/serverlessworkflow/catalog"); + } return new WorkflowApplication(this); } } @@ -429,6 +445,10 @@ public Optional functionReader() { return functionReader; } + public URI defaultCatalogURI() { + return defaultCatalogURI; + } + public Optional additionalObject( String name, WorkflowContext workflowContext, TaskContext taskContext) { return Optional.ofNullable(additionalObjects.get(name)) diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/CallFunctionExecutorBuilder.java b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/CallFunctionExecutorBuilder.java index 2bb382b4..f7d66bfd 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/CallFunctionExecutorBuilder.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/CallFunctionExecutorBuilder.java @@ -21,8 +21,6 @@ import io.serverlessworkflow.api.types.Task; import io.serverlessworkflow.api.types.TaskBase; import io.serverlessworkflow.api.types.Use; -import io.serverlessworkflow.api.types.UseCatalogs; -import io.serverlessworkflow.api.types.UseFunctions; import io.serverlessworkflow.impl.WorkflowDefinition; import io.serverlessworkflow.impl.WorkflowMutablePosition; import io.serverlessworkflow.impl.WorkflowUtils; @@ -43,33 +41,43 @@ public void init( CallFunction task, WorkflowDefinition definition, WorkflowMutablePosition position) { String functionName = task.getCall(); Use use = definition.workflow().getUse(); + int indexOf = functionName.indexOf('@'); Task function = null; - if (use != null) { - UseFunctions functions = use.getFunctions(); - if (functions != null) { - function = functions.getAdditionalProperties().get(functionName); - } - if (function == null) { - int indexOf = functionName.indexOf('@'); - if (indexOf > 0) { - String catalogName = functionName.substring(indexOf + 1); - UseCatalogs catalogs = use.getCatalogs(); - if (catalogs != null) { - Catalog catalog = catalogs.getAdditionalProperties().get(catalogName); - ResourceLoader loader = definition.resourceLoader(); - function = - definition - .resourceLoader() - .loadURI( - WorkflowUtils.concatURI( - loader.uri(catalog.getEndpoint()), - pathFromFunctionName(functionName.substring(0, indexOf))), - h -> from(definition, h)); - } + if (indexOf > 0) { + // Catalog function + URI catalogEndpoint; + String catalogName = functionName.substring(indexOf + 1); + ResourceLoader loader = definition.resourceLoader(); + if (catalogName.equalsIgnoreCase("default")) { + catalogEndpoint = definition.application().defaultCatalogURI(); + } else { + if (use == null || use.getCatalogs() == null) { + throw new IllegalStateException( + "Using catalog " + catalogName + ", but there is not catalog definition"); + } + Catalog catalog = use.getCatalogs().getAdditionalProperties().get(catalogName); + if (catalog == null) { + throw new IllegalStateException( + "Catalog " + + catalogName + + " is not included in Catalog dictionary: " + + use.getCatalogs().getAdditionalProperties()); } + catalogEndpoint = loader.uri(catalog.getEndpoint()); } + function = + definition + .resourceLoader() + .loadURI( + WorkflowUtils.concatURI( + catalogEndpoint, pathFromFunctionName(functionName.substring(0, indexOf))), + h -> from(definition, h)); + } else if (use != null && use.getFunctions() != null) { + // search for inline function definition + function = use.getFunctions().getAdditionalProperties().get(functionName); } if (function == null) { + // try to load function if function name is an uri function = definition.resourceLoader().loadURI(URI.create(functionName), h -> from(definition, h)); } diff --git a/impl/test/src/test/java/io/serverlessworkflow/impl/test/CustomFunctionTest.java b/impl/test/src/test/java/io/serverlessworkflow/impl/test/CustomFunctionTest.java index ab795c06..b659b7af 100644 --- a/impl/test/src/test/java/io/serverlessworkflow/impl/test/CustomFunctionTest.java +++ b/impl/test/src/test/java/io/serverlessworkflow/impl/test/CustomFunctionTest.java @@ -62,7 +62,8 @@ void testCustomFunction() { @ValueSource( strings = { "workflows-samples/call-custom-function-cataloged.yaml", - "workflows-samples/call-custom-function-cataloged-global.yaml" + "workflows-samples/call-custom-function-cataloged-global.yaml", + "workflows-samples/call-custom-function-cataloged-default.yaml" }) void testCustomCatalogFunction(String fileName) throws IOException { assertThat( diff --git a/impl/test/src/test/resources/workflows-samples/call-custom-function-cataloged-default.yaml b/impl/test/src/test/resources/workflows-samples/call-custom-function-cataloged-default.yaml new file mode 100644 index 00000000..fb31f423 --- /dev/null +++ b/impl/test/src/test/resources/workflows-samples/call-custom-function-cataloged-default.yaml @@ -0,0 +1,13 @@ +document: + dsl: '1.0.2' + namespace: test + name: call-custom-function-cataloged-default + version: '0.1.0' +do: + - log: + call: log:1.0.0@default + with: + message: Hello, world! + level: information + timestamp: true + format: '{TIMESTAMP} [{LEVEL}] ({CONTEXT}): {MESSAGE}' \ No newline at end of file