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
17 changes: 7 additions & 10 deletions api/src/test/java/io/serverlessworkflow/api/ApiTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,10 @@
import io.serverlessworkflow.api.types.CallHTTP;
import io.serverlessworkflow.api.types.CallTask;
import io.serverlessworkflow.api.types.HTTPArguments;
import io.serverlessworkflow.api.types.OAuth2AuthenticationData;
import io.serverlessworkflow.api.types.OAuth2AuthenticationData.OAuth2AuthenticationDataGrant;
import io.serverlessworkflow.api.types.OAuth2AuthenticationPolicy;
import io.serverlessworkflow.api.types.OAuth2AuthenticationPolicyConfiguration;
import io.serverlessworkflow.api.types.OAuth2AuthenticationPropertiesEndpoints;
import io.serverlessworkflow.api.types.OAuth2ConnectAuthenticationProperties;
import io.serverlessworkflow.api.types.Task;
import io.serverlessworkflow.api.types.Workflow;
import java.io.IOException;
Expand Down Expand Up @@ -99,20 +98,18 @@ void testOauth2Auth() throws IOException {
.getAuthenticationPolicy()
.getOAuth2AuthenticationPolicy();
assertThat(oauthPolicy).isNotNull();
OAuth2AuthenticationPolicyConfiguration oauth2Props =
OAuth2ConnectAuthenticationProperties oauth2Props =
oauthPolicy.getOauth2().getOAuth2ConnectAuthenticationProperties();
assertThat(oauth2Props).isNotNull();
OAuth2AuthenticationPropertiesEndpoints endpoints =
oauth2Props.getOAuth2ConnectAuthenticationProperties().getEndpoints();
OAuth2AuthenticationPropertiesEndpoints endpoints = oauth2Props.getEndpoints();
assertThat(endpoints.getToken()).isEqualTo("/auth/token");
assertThat(endpoints.getIntrospection()).isEqualTo("/auth/introspect");

OAuth2AuthenticationData oauth2Data = oauth2Props.getOAuth2AuthenticationData();
assertThat(oauth2Data.getAuthority().getLiteralUri())
assertThat(oauth2Props.getAuthority().getLiteralUri())
.isEqualTo(URI.create("http://keycloak/realms/fake-authority"));
assertThat(oauth2Data.getGrant()).isEqualTo(OAuth2AuthenticationDataGrant.CLIENT_CREDENTIALS);
assertThat(oauth2Data.getClient().getId()).isEqualTo("workflow-runtime-id");
assertThat(oauth2Data.getClient().getSecret()).isEqualTo("workflow-runtime-secret");
assertThat(oauth2Props.getGrant()).isEqualTo(OAuth2AuthenticationDataGrant.CLIENT_CREDENTIALS);
assertThat(oauth2Props.getClient().getId()).isEqualTo("workflow-runtime-id");
assertThat(oauth2Props.getClient().getSecret()).isEqualTo("workflow-runtime-secret");
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,41 +17,30 @@

import io.serverlessworkflow.api.types.OAuth2AuthenticationPolicy;
import io.serverlessworkflow.api.types.OAuth2AuthenticationPolicyConfiguration;
import io.serverlessworkflow.api.types.OAuth2ConnectAuthenticationProperties;
import io.serverlessworkflow.api.types.Oauth2;
import java.util.function.Consumer;

public final class OAuth2AuthenticationPolicyBuilder
extends OIDCBuilder<OAuth2AuthenticationPolicy> {

private final OAuth2ConnectAuthenticationProperties properties;

OAuth2AuthenticationPolicyBuilder() {
super();
this.properties = new OAuth2ConnectAuthenticationProperties();
}

public OAuth2AuthenticationPolicyBuilder endpoints(
Consumer<OAuth2AuthenticationPropertiesEndpointsBuilder> endpointsConsumer) {
final OAuth2AuthenticationPropertiesEndpointsBuilder builder =
new OAuth2AuthenticationPropertiesEndpointsBuilder();
endpointsConsumer.accept(builder);
this.properties.setEndpoints(builder.build());
this.authenticationData.setEndpoints(builder.build());
return this;
}

public OAuth2AuthenticationPolicy build() {
final OAuth2AuthenticationPolicyConfiguration configuration =
new OAuth2AuthenticationPolicyConfiguration();
configuration.setOAuth2AuthenticationData(this.getAuthenticationData());
configuration.setOAuth2ConnectAuthenticationProperties(this.properties);

final Oauth2 oauth2 = new Oauth2();
oauth2.setOAuth2ConnectAuthenticationProperties(configuration);

configuration.setOAuth2ConnectAuthenticationProperties(this.authenticationData);
final OAuth2AuthenticationPolicy policy = new OAuth2AuthenticationPolicy();
policy.setOauth2(oauth2);

policy.setOauth2(configuration);
return policy;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,17 @@
import io.serverlessworkflow.api.types.OAuth2AuthenticationData;
import io.serverlessworkflow.api.types.OAuth2AuthenticationDataClient;
import io.serverlessworkflow.api.types.OAuth2AuthenticationPropertiesEndpoints;
import io.serverlessworkflow.api.types.OAuth2ConnectAuthenticationProperties;
import io.serverlessworkflow.api.types.OAuth2TokenDefinition;
import io.serverlessworkflow.api.types.OAuth2TokenRequest;
import java.util.List;
import java.util.function.Consumer;

public abstract class OIDCBuilder<T extends AuthenticationPolicy> {
private final OAuth2AuthenticationData authenticationData;
protected final OAuth2ConnectAuthenticationProperties authenticationData;

OIDCBuilder() {
this.authenticationData = new OAuth2AuthenticationData();
this.authenticationData = new OAuth2ConnectAuthenticationProperties();
this.authenticationData.setRequest(new OAuth2TokenRequest());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,17 +170,15 @@ public static boolean whenExceptTest(
public static WorkflowValueResolver<Duration> fromTimeoutAfter(
WorkflowApplication application, TimeoutAfter timeout) {
if (timeout.getDurationExpression() != null) {
if (ExpressionUtils.isExpr(timeout.getDurationExpression())) {
return (w, f, t) ->
Duration.parse(
application
.expressionFactory()
.resolveString(ExpressionDescriptor.from(timeout.getDurationExpression()))
.apply(w, f, t));
} else {
Duration duration = Duration.parse(timeout.getDurationExpression());
return (w, f, t) -> duration;
}
return (w, f, t) ->
Duration.parse(
application
.expressionFactory()
.resolveString(ExpressionDescriptor.from(timeout.getDurationExpression()))
.apply(w, f, t));
} else if (timeout.getDurationLiteral() != null) {
Duration duration = Duration.parse(timeout.getDurationLiteral());
return (w, f, t) -> duration;
} else if (timeout.getDurationInline() != null) {
DurationInline inlineDuration = timeout.getDurationInline();
return (w, t, f) ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
package io.serverlessworkflow.impl.executors.http;

import io.serverlessworkflow.api.types.OAuth2AuthenticationPolicy;
import io.serverlessworkflow.api.types.Oauth2;
import io.serverlessworkflow.api.types.OAuth2AuthenticationPolicyConfiguration;
import io.serverlessworkflow.api.types.Workflow;
import io.serverlessworkflow.impl.TaskContext;
import io.serverlessworkflow.impl.WorkflowApplication;
Expand All @@ -36,9 +36,10 @@ public class OAuth2AuthProvider implements AuthProvider {

public OAuth2AuthProvider(
WorkflowApplication application, Workflow workflow, OAuth2AuthenticationPolicy authPolicy) {
Oauth2 oauth2 = authPolicy.getOauth2();
OAuth2AuthenticationPolicyConfiguration oauth2 = authPolicy.getOauth2();
if (oauth2.getOAuth2ConnectAuthenticationProperties() != null) {
this.requestBuilder = new OAuthRequestBuilder(application, oauth2);
this.requestBuilder =
new OAuthRequestBuilder(application, oauth2.getOAuth2ConnectAuthenticationProperties());
} else if (oauth2.getOAuth2AuthenticationPolicySecret() != null) {
throw new UnsupportedOperationException("Secrets are still not supported");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,35 +16,29 @@
package io.serverlessworkflow.impl.executors.http.auth.requestbuilder;

import io.serverlessworkflow.api.types.OAuth2AuthenticationPropertiesEndpoints;
import io.serverlessworkflow.api.types.Oauth2;
import io.serverlessworkflow.api.types.OAuth2ConnectAuthenticationProperties;
import io.serverlessworkflow.impl.WorkflowApplication;
import java.net.URI;
import java.util.Map;

public class OAuthRequestBuilder extends AbstractAuthRequestBuilder {

private final Oauth2 oauth2;

private final Map<String, String> defaults =
Map.of(
"endpoints.token", "oauth2/token",
"endpoints.revocation", "oauth2/revoke",
"endpoints.introspection", "oauth2/introspect");

public OAuthRequestBuilder(WorkflowApplication application, Oauth2 oauth2) {
super(
oauth2.getOAuth2ConnectAuthenticationProperties().getOAuth2AuthenticationData(),
application);
this.oauth2 = oauth2;
public OAuthRequestBuilder(
WorkflowApplication application,
OAuth2ConnectAuthenticationProperties oAuth2ConnectAuthenticationProperties) {
super(oAuth2ConnectAuthenticationProperties, application);
}

@Override
protected void authenticationURI(HttpRequestBuilder requestBuilder) {
OAuth2AuthenticationPropertiesEndpoints endpoints =
oauth2
.getOAuth2ConnectAuthenticationProperties()
.getOAuth2ConnectAuthenticationProperties()
.getEndpoints();
((OAuth2ConnectAuthenticationProperties) authenticationData).getEndpoints();

String baseUri =
authenticationData.getAuthority().getLiteralUri().toString().replaceAll("/$", "");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,11 @@ void testRetry(String path) throws IOException {
.setHeader("Content-Type", "application/json")
.setBody(JsonUtils.mapper().writeValueAsString(result)));
CompletableFuture<WorkflowModel> future =
app.workflowDefinition(readWorkflowFromClasspath(path)).instance(Map.of()).start();
app.workflowDefinition(readWorkflowFromClasspath(path))
.instance(Map.of("delay", 0.01))
.start();
Awaitility.await()
.atMost(Duration.ofSeconds(1))
.atMost(Duration.ofSeconds(100))
.until(() -> future.join().as(JsonNode.class).orElseThrow().equals(result));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ do:
type: https://serverlessworkflow.io/spec/1.0.0/errors/communication
status: 404
retry:
delay: "PT0.01S"
delay: ${"PT\(.delay)S"}
backoff:
exponential: {}
limit:
Expand Down
119 changes: 78 additions & 41 deletions types/src/main/resources/schema/workflow.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -312,10 +312,10 @@ $defs:
type: string
title: WithGRPCServiceHost
description: The hostname of the GRPC service to call.
pattern: ^[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$
pattern: ^[a-zA-Z0-9](?:[a-zA-Z0-9-.]{0,61}[a-zA-Z0-9])?$
port:
type: integer
title: WithGRPCServicePost
title: WithGRPCServicePort
description: The port number of the GRPC service to call.
minimum: 0
maximum: 65535
Expand Down Expand Up @@ -434,6 +434,45 @@ $defs:
description: Specifies whether redirection status codes (`300–399`) should be treated as errors.
required: [ document, operationId ]
unevaluatedProperties: false
- title: CallA2A
description: Defines the A2A call to perform.
type: object
unevaluatedProperties: false
required: [ call, with ]
allOf:
- $ref: '#/$defs/taskBase'
- properties:
call:
type: string
const: a2a
with:
type: object
title: A2AArguments
description: The A2A call arguments.
properties:
agentCard:
$ref: '#/$defs/externalResource'
title: WithA2AAgentCard
description: The Agent Card that defines the agent to call.
server:
title: A2AServer
description: The server endpoint to send the request to.
$ref: '#/$defs/endpoint'
method:
type: string
title: WithA2AMethod
description: The A2A method to send.
enum: [ 'message/send', 'message/stream', 'tasks/get', 'tasks/list', 'tasks/cancel', 'tasks/resubscribe', 'tasks/pushNotificationConfig/set', 'tasks/pushNotificationConfig/get', 'tasks/pushNotificationConfig/list', 'tasks/pushNotificationConfig/delete', 'agent/getAuthenticatedExtendedCard' ]
parameters:
oneOf:
- type: object
minProperties: 1
additionalProperties: true
- type: string
title: WithA2AParameters
description: The parameters object to send with the A2A method.
required: [ method ]
unevaluatedProperties: false
- title: CallFunction
description: Defines the function call to perform.
type: object
Expand All @@ -445,7 +484,7 @@ $defs:
call:
type: string
not:
enum: ["asyncapi", "grpc", "http", "openapi"]
enum: ["asyncapi", "grpc", "http", "openapi", "a2a"]
description: The name of the function to call.
with:
type: object
Expand Down Expand Up @@ -1001,37 +1040,35 @@ $defs:
description: The configuration of the OAuth2 authentication policy.
unevaluatedProperties: false
oneOf:
- type: object
- $ref: '#/$defs/oauth2AuthenticationProperties'
type: object
title: OAuth2ConnectAuthenticationProperties
description: The inline configuration of the OAuth2 authentication policy.
unevaluatedProperties: false
allOf:
- $ref: '#/$defs/oauth2AuthenticationProperties'
- type: object
properties:
endpoints:
type: object
title: OAuth2AuthenticationPropertiesEndpoints
description: The endpoint configurations for OAuth2.
properties:
endpoints:
type: object
title: OAuth2AuthenticationPropertiesEndpoints
description: The endpoint configurations for OAuth2.
properties:
token:
type: string
format: uri-template
default: /oauth2/token
title: OAuth2TokenEndpoint
description: The relative path to the token endpoint. Defaults to `/oauth2/token`.
revocation:
type: string
format: uri-template
default: /oauth2/revoke
title: OAuth2RevocationEndpoint
description: The relative path to the revocation endpoint. Defaults to `/oauth2/revoke`.
introspection:
type: string
format: uri-template
default: /oauth2/introspect
title: OAuth2IntrospectionEndpoint
description: The relative path to the introspection endpoint. Defaults to `/oauth2/introspect`.
token:
type: string
format: uri-template
default: /oauth2/token
title: OAuth2TokenEndpoint
description: The relative path to the token endpoint. Defaults to `/oauth2/token`.
revocation:
type: string
format: uri-template
default: /oauth2/revoke
title: OAuth2RevocationEndpoint
description: The relative path to the revocation endpoint. Defaults to `/oauth2/revoke`.
introspection:
type: string
format: uri-template
default: /oauth2/introspect
title: OAuth2IntrospectionEndpoint
description: The relative path to the introspection endpoint. Defaults to `/oauth2/introspect`.
- $ref: '#/$defs/secretBasedAuthenticationPolicy'
title: OAuth2AuthenticationPolicySecret
description: Secret based configuration of the OAuth2 authentication policy.
Expand Down Expand Up @@ -1178,10 +1215,13 @@ $defs:
description: Number of milliseconds, if any.
title: DurationInline
description: The inline definition of a duration.
- $ref: '#/$defs/runtimeExpression'
title: DurationExpression
description: Runtime expression that generates an ISO 8601
- type: string
pattern: '^P(?!$)(\d+(?:\.\d+)?Y)?(\d+(?:\.\d+)?M)?(\d+(?:\.\d+)?W)?(\d+(?:\.\d+)?D)?(T(?=\d)(\d+(?:\.\d+)?H)?(\d+(?:\.\d+)?M)?(\d+(?:\.\d+)?S)?)?$'
title: DurationExpression
description: The ISO 8601 expression of a duration.
title: DurationLiteral
description: The Literal ISO 8601 representation of a duration.
error:
type: object
title: Error
Expand Down Expand Up @@ -1697,23 +1737,20 @@ $defs:
type: object
title: AsyncApiMessagePayload
description: The message's payload, if any.
additionalProperties: true
headers:
type: object
title: AsyncApiMessageHeaders
description: The message's headers, if any.
additionalProperties: true
asyncApiInboundMessage:
type: object
title: AsyncApiInboundMessage
description: Represents a message counsumed by an AsyncAPI subscription.
allOf:
- $ref: '#/$defs/asyncApiOutboundMessage'
properties:
correlationId:
type: string
title: AsyncApiMessageCorrelationId
description: The message's correlation id, if any.
- $ref: '#/$defs/asyncApiOutboundMessage'
- properties:
correlationId:
type: string
title: AsyncApiMessageCorrelationId
description: The message's correlation id, if any.
asyncApiSubscription:
type: object
title: AsyncApiSubscription
Expand Down