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
5 changes: 4 additions & 1 deletion .gitignore
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one should be deleted

Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,7 @@ target/
build/

### VS Code ###
.vscode/
.vscode/

# Bob-Shell
.bob/notes/
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,13 @@ void get_named_with_authentication_uses_auth_policy() {
assertEquals("GET", http.getWith().getMethod());
assertEquals(
"http://service/api/users",
http.getWith().getEndpoint().getUriTemplate().getLiteralUri().toString(),
http.getWith()
.getEndpoint()
.getEndpointConfiguration()
.getUri()
.getLiteralEndpointURI()
.getLiteralUri()
.toString(),
"endpoint should be set from get(name, endpoint, auth)");

assertNotNull(
Expand Down Expand Up @@ -304,7 +310,13 @@ void get_with_uri_and_authentication() {
assertEquals("GET", http.getWith().getMethod());
assertEquals(
endpoint.toString(),
http.getWith().getEndpoint().getUriTemplate().getLiteralUri().toString(),
http.getWith()
.getEndpoint()
.getEndpointConfiguration()
.getUri()
.getLiteralEndpointURI()
.getLiteralUri()
.toString(),
"endpoint should be derived from URI");

assertNotNull(http.getWith().getEndpoint().getEndpointConfiguration().getAuthentication());
Expand Down Expand Up @@ -371,7 +383,13 @@ void post_named_with_authentication() {
assertEquals("POST", http.getWith().getMethod());
assertEquals(
"https://orders.example.com/api/orders",
http.getWith().getEndpoint().getUriTemplate().getLiteralUri().toString());
http.getWith()
.getEndpoint()
.getEndpointConfiguration()
.getUri()
.getLiteralEndpointURI()
.getLiteralUri()
.toString());
assertEquals(body, http.getWith().getBody());

assertNotNull(http.getWith().getEndpoint().getEndpointConfiguration().getAuthentication());
Expand Down Expand Up @@ -409,7 +427,13 @@ void call_with_preconfigured_http_spec() {
assertEquals("POST", http.getWith().getMethod());
assertEquals(
"http://service/api",
http.getWith().getEndpoint().getUriTemplate().getLiteralUri().toString());
http.getWith()
.getEndpoint()
.getEndpointConfiguration()
.getUri()
.getLiteralEndpointURI()
.getLiteralUri()
.toString());
assertEquals(
"svc-auth",
http.getWith()
Expand Down
7 changes: 7 additions & 0 deletions experimental/test/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,13 @@
<dependency>
<groupId>io.serverlessworkflow</groupId>
<artifactId>serverlessworkflow-impl-jq</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.serverlessworkflow</groupId>
<artifactId>serverlessworkflow-impl-openapi</artifactId>
<version>${project.version}</version>
Comment thread
mcruzdev marked this conversation as resolved.
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Copyright 2020-Present The Serverless Workflow Specification Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.serverlessworkflow.fluent.test;

import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.openapi;

import io.serverlessworkflow.fluent.func.FuncWorkflowBuilder;
import io.serverlessworkflow.impl.WorkflowApplication;
import io.serverlessworkflow.impl.WorkflowDefinition;
import io.serverlessworkflow.impl.WorkflowInstance;
import io.serverlessworkflow.impl.WorkflowModel;
import java.io.IOException;
import java.net.URI;
import java.util.Map;
import mockwebserver3.MockResponse;
import mockwebserver3.MockWebServer;
import okhttp3.Headers;
import org.assertj.core.api.SoftAssertions;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class FuncOpenAPITest {

private static MockWebServer mockWebServer;

@BeforeEach
public void setup() throws IOException {
mockWebServer = new MockWebServer();
mockWebServer.start(0);
}

@AfterEach
public void tearDown() {
mockWebServer.close();
}

@Test
void test_openapi_document_with_non_jq_uri_string() {
String mockedSwaggerDoc =
"""
{
"swagger": "2.0",
"info": { "version": "1.0.0", "title": "Mock Petstore" },
"host": "localhost:%d",
"basePath": "/v2",
"schemes": [ "http" ],
"paths": {
"/pet/findByStatus": {
"get": {
"operationId": "findPetsByStatus",
"parameters": [
{
"name": "status",
"in": "query",
"required": true,
"type": "string"
}
],
"responses": { "200": { "description": "OK" } }
}
}
}
}
"""
.formatted(mockWebServer.getPort());

mockWebServer.enqueue(
new MockResponse(200, Headers.of("Content-Type", "application/json"), mockedSwaggerDoc));
mockWebServer.enqueue(
new MockResponse(
200,
Headers.of("Content-Type", "application/json"),
"""
{ "description": "OK" }
"""));
var w =
FuncWorkflowBuilder.workflow("openapi-call-workflow")
.tasks(
openapi()
.document(URI.create(mockWebServer.url("/v2/swagger.json").toString()))
.operation("findPetsByStatus")
.parameters(Map.of("status", "available")))
Comment on lines +91 to +96
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test is named as if it validates the new auto-detection in document(String), but it calls document(URI) (line 94) and therefore never exercises the updated logic that distinguishes literal URIs from JQ expressions. To prevent regressions of #1303, update/add assertions to cover openapi().document(mockWebServer.url(...).toString()) (string overload) and (ideally) a ${...} / raw-JQ case to ensure it still gets treated as a runtime expression.

Copilot uses AI. Check for mistakes.
.build();
Comment on lines +51 to +97
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test is named as if it validates the new document(String) auto-detection behavior, but it currently calls the document(URI) overload. That means it won’t catch regressions of the original issue (passing a URL as a string being treated as a JQ expression). Consider switching to document(mockWebServer.url("/v2/swagger.json").toString()) and/or adding an explicit test that document(String) with an http(s)://... value loads successfully (and optionally a separate test for a real JQ expression string).

Copilot uses AI. Check for mistakes.

try (WorkflowApplication app = WorkflowApplication.builder().build()) {

WorkflowDefinition def = app.workflowDefinition(w);
WorkflowInstance instance = def.instance(Map.of());
WorkflowModel model = instance.start().join();

SoftAssertions.assertSoftly(
softly -> {
softly.assertThat(model).isNotNull();
softly.assertThat(model.asMap()).contains(Map.of("description", "OK"));
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,57 +50,89 @@ default SELF method(String method) {
return self();
}

/**
* Sets the endpoint URI for the HTTP call.
*
* @param endpoint the URI to call
* @return this builder instance for method chaining
*/
default SELF endpoint(URI endpoint) {
((CallHTTP) this.self().getTask())
.getWith()
.setEndpoint(new Endpoint().withUriTemplate(new UriTemplate().withLiteralUri(endpoint)));
return self();
}

/**
* Sets the endpoint URI for the HTTP call with authentication configuration.
*
* @param endpoint the URI to call
* @param auth consumer to configure authentication policy
* @return this builder instance for method chaining
*/
default SELF endpoint(URI endpoint, Consumer<ReferenceableAuthenticationPolicyBuilder> auth) {
final ReferenceableAuthenticationPolicyBuilder policy =
new ReferenceableAuthenticationPolicyBuilder();
final UriTemplate uriTemplate = new UriTemplate().withLiteralUri(endpoint);
auth.accept(policy);
((CallHTTP) this.self().getTask())
.getWith()
.setEndpoint(
new Endpoint()
.withEndpointConfiguration(
new EndpointConfiguration()
.withUri(new EndpointUri().withLiteralEndpointURI(uriTemplate))
.withAuthentication(policy.build()))
.withUriTemplate(uriTemplate));
.withUri(
new EndpointUri()
.withLiteralEndpointURI(new UriTemplate().withLiteralUri(endpoint)))
.withAuthentication(policy.build())));
return self();
}

/**
* Sets the endpoint using a runtime expression or URI string.
*
* @param expr the runtime expression or URI string for the endpoint
* @return this builder instance for method chaining
*/
default SELF endpoint(String expr) {
((CallHTTP) this.self().getTask()).getWith().setEndpoint(EndpointUtil.fromString(expr));
return self();
}

/**
* Sets the endpoint using a runtime expression or URI string with authentication configuration.
*
* @param expr the runtime expression or URI string for the endpoint
* @param auth consumer to configure authentication policy
* @return this builder instance for method chaining
*/
default SELF endpoint(String expr, Consumer<ReferenceableAuthenticationPolicyBuilder> auth) {
final ReferenceableAuthenticationPolicyBuilder policy =
new ReferenceableAuthenticationPolicyBuilder();
auth.accept(policy);

final Endpoint endpoint = EndpointUtil.fromString(expr);
endpoint.setEndpointConfiguration(
new EndpointConfiguration().withAuthentication(policy.build()));

((CallHTTP) this.self().getTask()).getWith().setEndpoint(endpoint);
((CallHTTP) this.self().getTask())
.getWith()
.setEndpoint(EndpointUtil.fromString(expr, policy.build()));
return self();
}

/**
* Sets the endpoint using a runtime expression or URI string with authentication policy
* reference.
*
* @param expr the runtime expression or URI string for the endpoint
* @param authUse the name of the authentication policy to reference
* @return this builder instance for method chaining
*/
default SELF endpoint(String expr, String authUse) {
final Endpoint endpoint = EndpointUtil.fromString(expr);
endpoint.withEndpointConfiguration(
new EndpointConfiguration()
.withAuthentication(
((CallHTTP) this.self().getTask())
.getWith()
.setEndpoint(
EndpointUtil.fromString(
expr,
new ReferenceableAuthenticationPolicy()
.withAuthenticationPolicyReference(
new AuthenticationPolicyReference(authUse))));
((CallHTTP) this.self().getTask()).getWith().setEndpoint(endpoint);
return self();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import io.serverlessworkflow.api.types.EndpointUri;
import io.serverlessworkflow.api.types.ExternalResource;
import io.serverlessworkflow.api.types.OpenAPIArguments;
import io.serverlessworkflow.api.types.ReferenceableAuthenticationPolicy;
import io.serverlessworkflow.api.types.UriTemplate;
import io.serverlessworkflow.fluent.spec.ReferenceableAuthenticationPolicyBuilder;
import io.serverlessworkflow.fluent.spec.TaskBaseBuilder;
Expand All @@ -40,11 +41,19 @@ default CallOpenAPI build() {

SELF self();

/**
* Sets the OpenAPI document location. This method automatically detects whether the provided
* string is a literal URI or a JQ runtime expression.
*
* @param uri the OpenAPI document location as either a literal URI string or a JQ expression
* @return this builder instance for method chaining
* @see #document(URI) for setting a literal URI directly
* @see #document(String, AuthenticationConfigurer) for setting a document with authentication
*/
Comment on lines +44 to +52
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Javadoc says this method detects "literal URI vs JQ runtime expression", but the implementation delegates to EndpointUtil.fromString(...) which classifies inputs as literal only when they look like scheme://... (and treats everything else as an expression). Consider tightening the wording to match the actual behavior (e.g., require a URI scheme) or adjusting the detection logic so it aligns with the documented JQ vs literal distinction.

Copilot uses AI. Check for mistakes.
default SELF document(String uri) {
((CallOpenAPI) this.self().getTask())
.getWith()
.withDocument(
new ExternalResource().withEndpoint(new Endpoint().withRuntimeExpression(uri)));
.setDocument(new ExternalResource().withEndpoint(EndpointUtil.fromString(uri)));
return self();
}

Expand All @@ -62,41 +71,33 @@ default SELF document(String uri, AuthenticationConfigurer authenticationConfigu
final ReferenceableAuthenticationPolicyBuilder policy =
new ReferenceableAuthenticationPolicyBuilder();
authenticationConfigurer.accept(policy);
((CallOpenAPI) this.self().getTask()).getWith().setAuthentication(policy.build());
ReferenceableAuthenticationPolicy auth = policy.build();
((CallOpenAPI) this.self().getTask()).getWith().setAuthentication(auth);
((CallOpenAPI) this.self().getTask())
.getWith()
.setDocument(
new ExternalResource()
.withEndpoint(
new Endpoint()
.withRuntimeExpression(uri)
.withEndpointConfiguration(
new EndpointConfiguration()
.withUri(new EndpointUri().withExpressionEndpointURI(uri))
.withAuthentication(policy.build()))));
.setDocument(new ExternalResource().withEndpoint(EndpointUtil.fromString(uri, auth)));
return self();
}

default SELF document(URI uri, AuthenticationConfigurer authenticationConfigurer) {
final ReferenceableAuthenticationPolicyBuilder policy =
new ReferenceableAuthenticationPolicyBuilder();
authenticationConfigurer.accept(policy);

((CallOpenAPI) this.self().getTask()).getWith().setAuthentication(policy.build());
ReferenceableAuthenticationPolicy auth = policy.build();
((CallOpenAPI) this.self().getTask()).getWith().setAuthentication(auth);
((CallOpenAPI) this.self().getTask())
.getWith()
.setDocument(
new ExternalResource()
.withEndpoint(
new Endpoint()
.withUriTemplate(new UriTemplate().withLiteralUri(uri))
.withEndpointConfiguration(
new EndpointConfiguration()
.withUri(
new EndpointUri()
.withLiteralEndpointURI(
new UriTemplate().withLiteralUri(uri)))
.withAuthentication(policy.build()))));
.withAuthentication(auth))));
return self();
}

Expand Down
Loading
Loading