Lab 2 – Call HTTP & OpenAPI services
In this lab you:
-
Add an HTTP call to a secured endpoint.
-
Call an OpenAPI operation by
operationId. -
See how
WorkflowExceptionis mapped to an RFC 7807 /WorkflowErrorresponse. -
Use Dev UI and logs to troubleshoot.
We will reuse the existing example classes:
-
org.acme.example.CustomerProfileFlow -
org.acme.example.CustomerProfileResource -
org.acme.example.PetstoreFlow
1. Add HTTP client configuration
First, configure basic HTTP timeouts and logging:
quarkus.flow.http.client.connect-timeout=5000
quarkus.flow.http.client.read-timeout=10000
quarkus.flow.http.client.logging.scope=request-response
quarkus.flow.http.client.logging.body-limit=2048
quarkus.log.category."org.jboss.resteasy.reactive.client.logging".level=DEBUG
For details see Configure the HTTP client.
2. Add the HTTP workflow
Create a workflow that calls a secured endpoint and uses Basic auth via secrets:
package org.acme.example;
import static io.serverlessworkflow.fluent.func.FuncWorkflowBuilder.workflow;
import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.get;
import static io.serverlessworkflow.fluent.spec.dsl.DSL.basic;
import static io.serverlessworkflow.fluent.spec.dsl.DSL.secret;
import java.net.URI;
import jakarta.enterprise.context.ApplicationScoped;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import io.quarkiverse.flow.Flow;
import io.serverlessworkflow.api.types.Workflow;
@ApplicationScoped
public class CustomerProfileFlow extends Flow {
@ConfigProperty(name = "demo.server", defaultValue = "http://localhost:8080")
String securedServer;
@Override
public Workflow descriptor() {
URI endpoint = URI.create(securedServer + "/secure/profile");
return workflow("secure-customer-profile")
// Load secrets into workflow data (see secrets documentation)
.use(secret("demo"))
.tasks(
// GET with HTTP Basic credentials taken from the secret
get(endpoint, basic("${ $secret.demo.username }", "${ $secret.demo.password }")))
.build();
}
}
This workflow uses:
-
get(…)from the Func DSL to perform an HTTP GET. -
basic(…)auth wired to secret values (demo.username/demo.password). -
Standard data transformations to shape the response.
Configure the demo credentials in application.properties as shown in
Call HTTP and OpenAPI services.
| You can see a fully functional project of this example at https://github.com/quarkiverse/quarkus-flow/tree/main/examples/http-basic-auth. |
3. Expose the workflow as a REST endpoint
Create a resource that invokes the workflow:
package org.acme.example;
import java.util.Map;
import java.util.concurrent.CompletionStage;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
@Path("/api/profile")
@Produces(MediaType.APPLICATION_JSON)
public class CustomerProfileResource {
@Inject
CustomerProfileFlow customerProfileFlow;
@GET
public CompletionStage<Map<String, Object>> getProfileViaWorkflow() {
return customerProfileFlow.instance(Map.of()).start().thenApply(r -> r.asMap().orElseThrow());
}
}
Notice:
-
The method returns
CompletionStage<Response>and callsinstance(…).start(). -
If the HTTP call fails (for example,
401or404), aWorkflowExceptionis thrown. -
Quarkus Flow’s
ExceptionMapper<WorkflowException>returns a Problem Details JSON body based on the underlyingWorkflowError.
4. Try a failing call
With dev mode running:
-
Call the endpoint with wrong credentials or misconfigured auth.
-
Observe the HTTP response:
HTTP/1.1 401 Unauthorized
Content-Type: application/json
{
"type": "https://serverlessworkflow.io/spec/1.0.0/errors/communication",
"status": 401,
"title": "HTTP 401 Unauthorized",
"instance": "do/0/..."
}
-
Open Dev UI and look at:
-
REST call in the REST Client (if OpenAPI is enabled).
-
Logs from the REST client logger and from Flow tracing.
-
5. Call an OpenAPI operation
Add a workflow that calls the Petstore demo by operationId:
package org.acme.example;
import static io.serverlessworkflow.fluent.func.FuncWorkflowBuilder.workflow;
import static io.serverlessworkflow.fluent.func.dsl.FuncDSL.openapi;
import java.net.URI;
import jakarta.enterprise.context.ApplicationScoped;
import io.quarkiverse.flow.Flow;
import io.serverlessworkflow.api.types.Workflow;
@ApplicationScoped
public class PetstoreFlow extends Flow {
@Override
public Workflow descriptor() {
final URI petstoreUri = URI.create("openapi/petstore.json");
return workflow("petstore")
.tasks(
// 1) Find pets by status
openapi()
.document(petstoreUri).operation("findPetsByStatus").parameter("status", "sold")
// Pick the first pet id and expose it as `selectedPetId`
.outputAs("${ { selectedPetId: .[0].id } }"),
// 2) Fetch that pet by id
openapi()
.document(petstoreUri).operation("getPetById").parameter("petId", "${ .selectedPetId }"))
.build();
}
}
and put the petstore.json OpenAPI document under src/main/resources/openapi/petstore.json
(or similar) as shown in Call HTTP and OpenAPI services.
| You can see a fully functional project of this example at https://github.com/quarkiverse/quarkus-flow/tree/main/examples/petstore-openapi. |
Run the workflow and:
-
Inspect how the operation is resolved by
operationId. -
Use Dev UI and logs to verify the actual URL, method and payload being used.