Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: introduce multi protocol dsp path #4861

Merged
merged 2 commits into from
Mar 13, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
import org.eclipse.edc.protocol.dsp.http.dispatcher.GetDspHttpRequestFactory;
import org.eclipse.edc.protocol.dsp.http.dispatcher.PostDspHttpRequestFactory;
import org.eclipse.edc.protocol.dsp.http.serialization.ByteArrayBodyExtractor;
import org.eclipse.edc.protocol.dsp.http.spi.DspProtocolParser;
import org.eclipse.edc.protocol.dsp.http.spi.dispatcher.DspHttpRemoteMessageDispatcher;
import org.eclipse.edc.protocol.dsp.http.spi.dispatcher.DspRequestBasePathProvider;
import org.eclipse.edc.protocol.dsp.http.spi.serialization.JsonLdRemoteMessageSerializer;
import org.eclipse.edc.runtime.metamodel.annotation.Extension;
import org.eclipse.edc.runtime.metamodel.annotation.Inject;
Expand All @@ -45,7 +45,7 @@ public class DspCatalogHttpDispatcherExtension implements ServiceExtension {
@Inject
private JsonLdRemoteMessageSerializer remoteMessageSerializer;
@Inject
private DspProtocolParser dspProtocolParser;
private DspRequestBasePathProvider dspRequestBasePathProvider;

@Override
public String name() {
Expand All @@ -58,12 +58,12 @@ public void initialize(ServiceExtensionContext context) {

messageDispatcher.registerMessage(
CatalogRequestMessage.class,
new PostDspHttpRequestFactory<>(remoteMessageSerializer, dspProtocolParser, m -> BASE_PATH + CATALOG_REQUEST),
new PostDspHttpRequestFactory<>(remoteMessageSerializer, dspRequestBasePathProvider, m -> BASE_PATH + CATALOG_REQUEST),
byteArrayBodyExtractor
);
messageDispatcher.registerMessage(
DatasetRequestMessage.class,
new GetDspHttpRequestFactory<>(dspProtocolParser, m -> BASE_PATH + DATASET_REQUEST + "/" + m.getDatasetId()),
new GetDspHttpRequestFactory<>(dspRequestBasePathProvider, m -> BASE_PATH + DATASET_REQUEST + "/" + m.getDatasetId()),
byteArrayBodyExtractor
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
import static org.eclipse.edc.protocol.dsp.spi.type.DspConstants.DSP_SCOPE_V_2024_1;
import static org.eclipse.edc.protocol.dsp.spi.type.DspConstants.DSP_TRANSFORMER_CONTEXT_V_08;
import static org.eclipse.edc.protocol.dsp.spi.type.DspConstants.DSP_TRANSFORMER_CONTEXT_V_2024_1;
import static org.eclipse.edc.protocol.dsp.spi.version.DspVersions.V_2024_1_PATH;
import static org.eclipse.edc.spi.constants.CoreConstants.EDC_NAMESPACE;
import static org.eclipse.edc.spi.constants.CoreConstants.EDC_PREFIX;
import static org.eclipse.edc.spi.constants.CoreConstants.JSON_LD;
Expand All @@ -84,6 +85,11 @@ public class DspApiConfigurationExtension implements ServiceExtension {
static final String DEFAULT_PROTOCOL_PATH = "/api/protocol";
static final int DEFAULT_PROTOCOL_PORT = 8282;

private static final boolean DEFAULT_WELL_KNOWN_PATH = false;

@Setting(description = "If set enable the well known path resolution scheme will be used", key = "edc.dsp.wellKnownPath.enabled", required = false, defaultValue = DEFAULT_WELL_KNOWN_PATH + "")
private boolean wellKnownPathEnabled;

@Setting(description = "Configures endpoint for reaching the Protocol API in the form \"<hostname:protocol.port/protocol.path>\"", key = "edc.dsp.callback.address", required = false)
private String callbackAddress;
@Configuration
Expand Down Expand Up @@ -119,8 +125,11 @@ public void initialize(ServiceExtensionContext context) {

var dspWebhookAddress = ofNullable(callbackAddress).orElseGet(() -> format("http://%s:%s%s", hostname.get(), portMapping.port(), portMapping.path()));


var v2024Path = dspWebhookAddress + (wellKnownPathEnabled ? "" : V_2024_1_PATH);

protocolWebhookRegistry.registerWebhook(DATASPACE_PROTOCOL_HTTP, () -> dspWebhookAddress);
protocolWebhookRegistry.registerWebhook(DATASPACE_PROTOCOL_HTTP_V_2024_1, () -> dspWebhookAddress);
protocolWebhookRegistry.registerWebhook(DATASPACE_PROTOCOL_HTTP_V_2024_1, () -> v2024Path);

// registers ns for DSP scope
registerNamespaces(DSP_SCOPE_V_08, DSP_NAMESPACE_V_08);
Expand All @@ -135,6 +144,7 @@ public void initialize(ServiceExtensionContext context) {
registerTransformers(DSP_TRANSFORMER_CONTEXT_V_2024_1, DSP_NAMESPACE_V_2024_1);
}


@Override
public void prepare() {
var mapper = typeManager.getMapper(JSON_LD);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
import static org.eclipse.edc.protocol.dsp.http.spi.types.HttpMessageProtocol.DATASPACE_PROTOCOL_HTTP_V_2024_1;
import static org.eclipse.edc.protocol.dsp.spi.type.DspConstants.DSP_SCOPE_V_08;
import static org.eclipse.edc.protocol.dsp.spi.type.DspConstants.DSP_SCOPE_V_2024_1;
import static org.eclipse.edc.protocol.dsp.spi.version.DspVersions.V_2024_1_PATH;
import static org.eclipse.edc.spi.constants.CoreConstants.EDC_NAMESPACE;
import static org.eclipse.edc.spi.constants.CoreConstants.EDC_PREFIX;
import static org.mockito.ArgumentMatchers.any;
Expand Down Expand Up @@ -93,7 +94,7 @@ void shouldComposeProtocolWebhook_whenNotConfigured(DspApiConfigurationExtension

var url = "http://hostname:%s%s".formatted(DEFAULT_PROTOCOL_PORT, DEFAULT_PROTOCOL_PATH);
verify(protocolWebhookRegistry).registerWebhook(eq(DATASPACE_PROTOCOL_HTTP), argThat(webhook -> webhook.url().equals(url)));
verify(protocolWebhookRegistry).registerWebhook(eq(DATASPACE_PROTOCOL_HTTP_V_2024_1), argThat(webhook -> webhook.url().equals(url)));
verify(protocolWebhookRegistry).registerWebhook(eq(DATASPACE_PROTOCOL_HTTP_V_2024_1), argThat(webhook -> webhook.url().equals(url + V_2024_1_PATH)));
}

@Test
Expand All @@ -102,7 +103,8 @@ void shouldUseConfiguredProtocolWebhook(ServiceExtensionContext context, ObjectF
when(context.getConfig()).thenReturn(ConfigFactory.fromMap(Map.of(
"web.http.protocol.port", String.valueOf(1234),
"web.http.protocol.path", "/path",
"edc.dsp.callback.address", webhookAddress))
"edc.dsp.callback.address", webhookAddress,
"edc.dsp.wellKnownPath.enabled", "true"))
);
var extension = factory.constructInstance(DspApiConfigurationExtension.class);

Expand All @@ -115,6 +117,7 @@ void shouldUseConfiguredProtocolWebhook(ServiceExtensionContext context, ObjectF

}


@Test
void initialize_shouldRegisterWebServiceProviders(DspApiConfigurationExtension extension, ServiceExtensionContext context) {
extension.initialize(context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,21 @@
import org.eclipse.edc.policy.context.request.spi.RequestVersionPolicyContext;
import org.eclipse.edc.policy.engine.spi.PolicyEngine;
import org.eclipse.edc.protocol.dsp.http.dispatcher.DspHttpRemoteMessageDispatcherImpl;
import org.eclipse.edc.protocol.dsp.http.dispatcher.DspRequestBasePathProviderImpl;
import org.eclipse.edc.protocol.dsp.http.message.DspRequestHandlerImpl;
import org.eclipse.edc.protocol.dsp.http.protocol.DspProtocolParserImpl;
import org.eclipse.edc.protocol.dsp.http.serialization.JsonLdRemoteMessageSerializerImpl;
import org.eclipse.edc.protocol.dsp.http.spi.DspProtocolParser;
import org.eclipse.edc.protocol.dsp.http.spi.dispatcher.DspHttpRemoteMessageDispatcher;
import org.eclipse.edc.protocol.dsp.http.spi.dispatcher.DspRequestBasePathProvider;
import org.eclipse.edc.protocol.dsp.http.spi.message.DspRequestHandler;
import org.eclipse.edc.protocol.dsp.http.spi.serialization.JsonLdRemoteMessageSerializer;
import org.eclipse.edc.protocol.dsp.http.transform.DspProtocolTypeTransformerRegistryImpl;
import org.eclipse.edc.protocol.dsp.spi.transform.DspProtocolTypeTransformerRegistry;
import org.eclipse.edc.runtime.metamodel.annotation.Extension;
import org.eclipse.edc.runtime.metamodel.annotation.Inject;
import org.eclipse.edc.runtime.metamodel.annotation.Provider;
import org.eclipse.edc.runtime.metamodel.annotation.Setting;
import org.eclipse.edc.spi.iam.AudienceResolver;
import org.eclipse.edc.spi.iam.IdentityService;
import org.eclipse.edc.spi.message.RemoteMessageDispatcherRegistry;
Expand Down Expand Up @@ -81,6 +84,11 @@ public class DspHttpCoreExtension implements ServiceExtension {

public static final String NAME = "Dataspace Protocol Core Extension";

private static final boolean DEFAULT_WELL_KNOWN_PATH = false;

@Setting(description = "If set enable the well known path resolution scheme will be used", key = "edc.dsp.wellKnownPath.enabled", required = false, defaultValue = DEFAULT_WELL_KNOWN_PATH + "")
private boolean wellKnownPathEnabled;

@Inject
private RemoteMessageDispatcherRegistry dispatcherRegistry;
@Inject
Expand Down Expand Up @@ -169,6 +177,10 @@ public DspProtocolParser dspProtocolParser() {
return dspProtocolParser;
}

@Provider
public DspRequestBasePathProvider dspRequestBasePathProvider() {
return new DspRequestBasePathProviderImpl(dspProtocolParser(), wellKnownPathEnabled);
}

private void registerNegotiationPolicyScopes(DspHttpRemoteMessageDispatcher dispatcher) {
dispatcher.registerPolicyScope(ContractAgreementMessage.class, ContractRemoteMessage::getPolicy, RequestContractNegotiationPolicyContext::new);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright (c) 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation
*
*/

package org.eclipse.edc.protocol.dsp.http.dispatcher;

import org.eclipse.edc.connector.controlplane.services.spi.protocol.ProtocolVersion;
import org.eclipse.edc.protocol.dsp.http.spi.DspProtocolParser;
import org.eclipse.edc.protocol.dsp.http.spi.dispatcher.DspRequestBasePathProvider;
import org.eclipse.edc.spi.EdcException;
import org.eclipse.edc.spi.types.domain.message.RemoteMessage;

public class DspRequestBasePathProviderImpl implements DspRequestBasePathProvider {

private final DspProtocolParser protocolParser;
private final boolean wellKnownPath;

public DspRequestBasePathProviderImpl(DspProtocolParser protocolParser, boolean wellKnownPath) {
this.protocolParser = protocolParser;
this.wellKnownPath = wellKnownPath;
}

@Override
public String provideBasePath(RemoteMessage message) {
var protocolPath = "";
if (wellKnownPath) {
protocolPath = protocolParser.parse(message.getProtocol())
.map(ProtocolVersion::path)
.map(this::removeTrailingSlash)
.orElseThrow(failure -> new EdcException(failure.getFailureDetail()));
}
return message.getCounterPartyAddress() + protocolPath;
}

private String removeTrailingSlash(String path) {
if (path.endsWith("/")) {
return path.substring(0, path.length() - 1);
}
return path;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,9 @@

import okhttp3.HttpUrl;
import okhttp3.Request;
import org.eclipse.edc.connector.controlplane.services.spi.protocol.ProtocolVersion;
import org.eclipse.edc.protocol.dsp.http.spi.DspProtocolParser;
import org.eclipse.edc.protocol.dsp.http.spi.dispatcher.DspHttpRequestFactory;
import org.eclipse.edc.protocol.dsp.http.spi.dispatcher.DspRequestBasePathProvider;
import org.eclipse.edc.protocol.dsp.http.spi.dispatcher.RequestPathProvider;
import org.eclipse.edc.spi.EdcException;
import org.eclipse.edc.spi.types.domain.message.RemoteMessage;

/**
Expand All @@ -30,23 +28,17 @@
*/
public class GetDspHttpRequestFactory<M extends RemoteMessage> implements DspHttpRequestFactory<M> {
private final RequestPathProvider<M> pathProvider;
private final DspRequestBasePathProvider dspBasePathProvider;

private final DspProtocolParser protocolParser;

public GetDspHttpRequestFactory(DspProtocolParser protocolParser, RequestPathProvider<M> pathProvider) {
this.protocolParser = protocolParser;
public GetDspHttpRequestFactory(DspRequestBasePathProvider dspBasePathProvider, RequestPathProvider<M> pathProvider) {
this.dspBasePathProvider = dspBasePathProvider;
this.pathProvider = pathProvider;
}

@Override
public Request createRequest(M message) {

var protocolPath = protocolParser.parse(message.getProtocol())
.map(ProtocolVersion::path)
.map(this::removeTrailingSlash)
.orElseThrow(failure -> new EdcException(failure.getFailureDetail()));

var url = HttpUrl.get(message.getCounterPartyAddress() + protocolPath + pathProvider.providePath(message));
var url = HttpUrl.get(dspBasePathProvider.provideBasePath(message) + pathProvider.providePath(message));

return new Request.Builder()
.url(url)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,10 @@
import okhttp3.MediaType;
import okhttp3.Request;
import okhttp3.RequestBody;
import org.eclipse.edc.connector.controlplane.services.spi.protocol.ProtocolVersion;
import org.eclipse.edc.protocol.dsp.http.spi.DspProtocolParser;
import org.eclipse.edc.protocol.dsp.http.spi.dispatcher.DspHttpRequestFactory;
import org.eclipse.edc.protocol.dsp.http.spi.dispatcher.DspRequestBasePathProvider;
import org.eclipse.edc.protocol.dsp.http.spi.dispatcher.RequestPathProvider;
import org.eclipse.edc.protocol.dsp.http.spi.serialization.JsonLdRemoteMessageSerializer;
import org.eclipse.edc.spi.EdcException;
import org.eclipse.edc.spi.types.domain.message.RemoteMessage;

/**
Expand All @@ -36,12 +34,11 @@ public class PostDspHttpRequestFactory<M extends RemoteMessage> implements DspHt
public static final String APPLICATION_JSON = "application/json";
private final RequestPathProvider<M> pathProvider;
private final JsonLdRemoteMessageSerializer serializer;
private final DspProtocolParser protocolParser;
private final DspRequestBasePathProvider dspBasePathProvider;


public PostDspHttpRequestFactory(JsonLdRemoteMessageSerializer serializer, DspProtocolParser protocolParser, RequestPathProvider<M> pathProvider) {
public PostDspHttpRequestFactory(JsonLdRemoteMessageSerializer serializer, DspRequestBasePathProvider dspBasePathProvider, RequestPathProvider<M> pathProvider) {
this.serializer = serializer;
this.protocolParser = protocolParser;
this.dspBasePathProvider = dspBasePathProvider;
this.pathProvider = pathProvider;
}

Expand All @@ -50,12 +47,7 @@ public Request createRequest(M message) {
var body = serializer.serialize(message);
var requestBody = RequestBody.create(body, MediaType.get(APPLICATION_JSON));

var protocolPath = protocolParser.parse(message.getProtocol())
.map(ProtocolVersion::path)
.map(this::removeTrailingSlash)
.orElseThrow(failure -> new EdcException(failure.getFailureDetail()));

var url = HttpUrl.get(message.getCounterPartyAddress() + protocolPath + pathProvider.providePath(message));
var url = HttpUrl.get(dspBasePathProvider.provideBasePath(message) + pathProvider.providePath(message));

return new Request.Builder()
.url(url)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,8 @@
package org.eclipse.edc.protocol.dsp.http.dispatcher;

import org.eclipse.edc.protocol.dsp.http.TestMessage;
import org.eclipse.edc.protocol.dsp.http.spi.DspProtocolParser;
import org.eclipse.edc.protocol.dsp.http.spi.dispatcher.DspRequestBasePathProvider;
import org.eclipse.edc.protocol.dsp.http.spi.dispatcher.RequestPathProvider;
import org.eclipse.edc.protocol.dsp.spi.version.DspVersions;
import org.eclipse.edc.spi.result.Result;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;
Expand All @@ -29,19 +27,19 @@
class GetDspHttpRequestFactoryTest {

private final RequestPathProvider<TestMessage> pathProvider = mock();
private final DspProtocolParser dspProtocolParser = mock();
private final GetDspHttpRequestFactory<TestMessage> factory = new GetDspHttpRequestFactory<>(dspProtocolParser, pathProvider);
private final DspRequestBasePathProvider dspRequestBasePathProvider = mock();
private final GetDspHttpRequestFactory<TestMessage> factory = new GetDspHttpRequestFactory<>(dspRequestBasePathProvider, pathProvider);

@Test
void shouldCreateProperHttpRequest() {
when(pathProvider.providePath(any())).thenReturn("/message/request/path");
when(dspProtocolParser.parse("protocol")).thenReturn(Result.success(DspVersions.V_08));
when(dspRequestBasePathProvider.provideBasePath(any())).thenReturn("http://counter-party");

var message = new TestMessage("protocol", "http://counter-party", "counterPartyId");
var request = factory.createRequest(message);

assertThat(request.url().url().toString()).isEqualTo("http://counter-party/message/request/path");
assertThat(request.method()).isEqualTo("GET");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,9 @@
import okhttp3.MediaType;
import okio.Buffer;
import org.eclipse.edc.protocol.dsp.http.TestMessage;
import org.eclipse.edc.protocol.dsp.http.spi.DspProtocolParser;
import org.eclipse.edc.protocol.dsp.http.spi.dispatcher.DspRequestBasePathProvider;
import org.eclipse.edc.protocol.dsp.http.spi.dispatcher.RequestPathProvider;
import org.eclipse.edc.protocol.dsp.http.spi.serialization.JsonLdRemoteMessageSerializer;
import org.eclipse.edc.protocol.dsp.spi.version.DspVersions;
import org.eclipse.edc.spi.result.Result;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;
Expand All @@ -33,13 +31,13 @@ class PostDspHttpRequestFactoryTest {

private final RequestPathProvider<TestMessage> pathProvider = mock();
private final JsonLdRemoteMessageSerializer serializer = mock();
private final DspProtocolParser dspProtocolParser = mock();
private final PostDspHttpRequestFactory<TestMessage> factory = new PostDspHttpRequestFactory<>(serializer, dspProtocolParser, pathProvider);
private final DspRequestBasePathProvider dspRequestBasePathProvider = mock();
private final PostDspHttpRequestFactory<TestMessage> factory = new PostDspHttpRequestFactory<>(serializer, dspRequestBasePathProvider, pathProvider);

@Test
void shouldCreateProperHttpRequest() {
when(serializer.serialize(any())).thenReturn("serializedMessage");
when(dspProtocolParser.parse("protocol")).thenReturn(Result.success(DspVersions.V_08));
when(dspRequestBasePathProvider.provideBasePath(any())).thenReturn("http://counter-party");
when(pathProvider.providePath(any())).thenReturn("/message/request/path");

var message = new TestMessage("protocol", "http://counter-party", "counterPartyId");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,6 @@
@FunctionalInterface
public interface DspHttpRequestFactory<M extends RemoteMessage> {

default String removeTrailingSlash(String path) {
if (path.endsWith("/")) {
return path.substring(0, path.length() - 1);
}
return path;
}

/**
* Create the request given the message and a {@link RequestPathProvider}
*
Expand Down
Loading