Skip to content
Closed
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
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 @@ -371,7 +377,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 +421,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>
<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")))
.build();

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 @@ -84,23 +84,21 @@ default SELF endpoint(String expr, Consumer<ReferenceableAuthenticationPolicyBui
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();
}

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
*/
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
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
package io.serverlessworkflow.fluent.spec.spi;

import io.serverlessworkflow.api.types.Endpoint;
import io.serverlessworkflow.api.types.EndpointConfiguration;
import io.serverlessworkflow.api.types.EndpointUri;
import io.serverlessworkflow.api.types.ReferenceableAuthenticationPolicy;
import io.serverlessworkflow.api.types.UriTemplate;
import java.net.URI;
import java.util.Objects;
Expand All @@ -24,27 +27,48 @@ public final class EndpointUtil {

private EndpointUtil() {}

public static Endpoint fromString(String expr) {
Objects.requireNonNull(expr, "Endpoint expression cannot be null");
String trimmed = expr.trim();
public static Endpoint fromString(String uri) {
return fromString(uri, null);
}

public static Endpoint fromString(String uri, ReferenceableAuthenticationPolicy auth) {
Objects.requireNonNull(uri, "Endpoint URI cannot be null");
String trimmed = uri.trim();
Endpoint endpoint = new Endpoint();
if (isUrlLike(trimmed)) {
UriTemplate template = new UriTemplate();
if (trimmed.indexOf('{') >= 0 || trimmed.indexOf('}') >= 0) {
template.setLiteralUriTemplate(trimmed);
} else {
template.setLiteralUri(URI.create(trimmed));
}
endpoint.setUriTemplate(template);
return endpoint;
}

// Let the runtime engine to verify if it's a valid jq expression since ${} it's not the only
// way of checking it.
endpoint.setRuntimeExpression(expr);
if (auth != null) {
endpoint.setEndpointConfiguration(
new EndpointConfiguration(buildEndpointUri(trimmed)).withAuthentication(auth));
} else if (isUrlLike(trimmed)) {
endpoint.setUriTemplate(buildUriTemplate(trimmed));
} else {
// Let the runtime engine to verify if it's a valid jq expression since ${} it's not the only
// way of checking it.
endpoint.setRuntimeExpression(uri);
}
Comment thread
fjtirado marked this conversation as resolved.
return endpoint;
}

private static EndpointUri buildEndpointUri(String uri) {
EndpointUri endpointUri = new EndpointUri();
if (isUrlLike(uri)) {
endpointUri.setLiteralEndpointURI(buildUriTemplate(uri));
Comment thread
fjtirado marked this conversation as resolved.
} else {
endpointUri.setExpressionEndpointURI(uri);
}
return endpointUri;
}

private static UriTemplate buildUriTemplate(String trimmed) {
UriTemplate template = new UriTemplate();
if (trimmed.indexOf('{') >= 0 || trimmed.indexOf('}') >= 0) {
template.setLiteralUriTemplate(trimmed);
} else {
template.setLiteralUri(URI.create(trimmed));
}
return template;
}

private static boolean isUrlLike(String value) {
// same idea as UriTemplate.literalUriTemplate_Pattern: ^[A-Za-z][A-Za-z0-9+\\-.]*://.*
int idx = value.indexOf("://");
Expand Down
Loading
Loading