diff --git a/extensions/data-plane/embedded-data-plane-registration/build.gradle.kts b/extensions/data-plane/embedded-data-plane-registration/build.gradle.kts new file mode 100644 index 00000000000..536bdbacd77 --- /dev/null +++ b/extensions/data-plane/embedded-data-plane-registration/build.gradle.kts @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2022 Microsoft Corporation + * + * 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: + * Microsoft Corporation - initial API and implementation + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - improvements + * Mercedes-Benz Tech Innovation GmbH - publish public api context into dedicated swagger hub page + * + */ + + +plugins { + `java-library` +} + +dependencies { + api(project(":spi:data-plane:data-plane-spi")) + implementation(project(":core:common:util")) + implementation(project(":spi:data-plane-selector:data-plane-selector-spi")) + implementation(project(":core:data-plane:data-plane-util")) + + testImplementation(project(":core:common:junit")) + testImplementation(project(":spi:data-plane:data-plane-spi")) + + + + implementation(libs.jakarta.rsApi) + + +} + + + diff --git a/extensions/data-plane/embedded-data-plane-registration/src/main/java/org/eclipse/edc/connector/dataplane/embedded/autoregistration/EmbeddedDataPlaneConfig.java b/extensions/data-plane/embedded-data-plane-registration/src/main/java/org/eclipse/edc/connector/dataplane/embedded/autoregistration/EmbeddedDataPlaneConfig.java new file mode 100644 index 00000000000..73e3bcf6934 --- /dev/null +++ b/extensions/data-plane/embedded-data-plane-registration/src/main/java/org/eclipse/edc/connector/dataplane/embedded/autoregistration/EmbeddedDataPlaneConfig.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2024 Zub4t (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: + * Zub4t (BMW AG) - initial API and implementation + * + */ +package org.eclipse.edc.connector.dataplane.embedded.autoregistration; + +public class EmbeddedDataPlaneConfig { + public static final String CONFIG_PREFIX = "edc.embedded.dataplane"; + public static final String URL_SUFFIX = "url"; + public static final String DESTINATION_TYPES_SUFFIX = "destinationtypes"; + public static final String SOURCE_TYPES_SUFFIX = "sourcetypes"; + public static final String PROPERTIES_SUFFIX = "properties"; + public static final String PUBLIC_API_URL_PROPERTY = "publicApiUrl"; +} diff --git a/extensions/data-plane/embedded-data-plane-registration/src/main/java/org/eclipse/edc/connector/dataplane/embedded/autoregistration/EmbeddedDataPlaneRegistration.java b/extensions/data-plane/embedded-data-plane-registration/src/main/java/org/eclipse/edc/connector/dataplane/embedded/autoregistration/EmbeddedDataPlaneRegistration.java new file mode 100644 index 00000000000..9aa09173084 --- /dev/null +++ b/extensions/data-plane/embedded-data-plane-registration/src/main/java/org/eclipse/edc/connector/dataplane/embedded/autoregistration/EmbeddedDataPlaneRegistration.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2024 Zub4t (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: + * Zub4t (BMW AG) - initial API and implementation + * + */ +package org.eclipse.edc.connector.dataplane.embedded.autoregistration; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.eclipse.edc.connector.dataplane.selector.spi.DataPlaneSelectorService; +import org.eclipse.edc.connector.dataplane.selector.spi.instance.DataPlaneInstance; +import org.eclipse.edc.connector.dataplane.spi.manager.DataPlaneManager; +import org.eclipse.edc.runtime.metamodel.annotation.Inject; +import org.eclipse.edc.spi.EdcException; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.spi.system.ServiceExtension; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.edc.spi.system.configuration.Config; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import static java.util.Objects.nonNull; + +public class EmbeddedDataPlaneRegistration implements ServiceExtension { + + private static final String NAME = "Embedded DataPlane Auto Registration"; + private static final String COMMA = ","; + private static final String LOG_MISSING_CONFIGURATION = NAME + ": Missing configuration for " + EmbeddedDataPlaneConfig.CONFIG_PREFIX + ".%s.%s"; + private static final String LOG_SKIP_BC_MISSING_CONFIGURATION = NAME + ": Configuration issues. Skip registering of Data Plane Instance '%s'"; + private static final String LOG_REGISTERED = NAME + ": Registered Data Plane Instance. (id=%s, url=%s, sourceTypes=%s, destinationTypes=%s, properties=)"; + private Monitor monitor; + @Inject + private DataPlaneSelectorService selectorService; + @Override + public String name() { + return NAME; + } + + @Override + public void initialize(ServiceExtensionContext context) { + this.monitor = context.getMonitor(); + final Config config = context.getConfig(EmbeddedDataPlaneConfig.CONFIG_PREFIX); + context.getService(DataPlaneManager.class); + configureDataPlaneInstance(config); + } + + public DataPlaneInstance createDataPlane(final Config config){ + + DataPlaneInstance dataPlaneInstance = null; + final String id = config.currentNode(); + final String url = config.getString(EmbeddedDataPlaneConfig.URL_SUFFIX, ""); + final List sourceTypes = + Arrays.stream(config.getString(EmbeddedDataPlaneConfig.SOURCE_TYPES_SUFFIX, "").split(COMMA)) + .map(String::trim) + .filter(Predicate.not(String::isEmpty)) + .distinct() + .collect(Collectors.toList()); + final List destinationTypes = + Arrays.stream(config.getString(EmbeddedDataPlaneConfig.DESTINATION_TYPES_SUFFIX, "").split(COMMA)) + .map(String::trim) + .filter(Predicate.not(String::isEmpty)) + .distinct() + .collect(Collectors.toList()); + final String propertiesJson = config.getString(EmbeddedDataPlaneConfig.PROPERTIES_SUFFIX, "{}"); + + if (url.isEmpty()) { + monitor.warning(String.format(LOG_MISSING_CONFIGURATION, id, EmbeddedDataPlaneConfig.URL_SUFFIX)); + } + + if (sourceTypes.isEmpty()) { + monitor.warning(String.format(LOG_MISSING_CONFIGURATION, id, EmbeddedDataPlaneConfig.SOURCE_TYPES_SUFFIX)); + } + + if (destinationTypes.isEmpty()) { + monitor.warning(String.format(LOG_MISSING_CONFIGURATION, id, EmbeddedDataPlaneConfig.DESTINATION_TYPES_SUFFIX)); + } + + final Map properties; + try { + ObjectMapper mapper = new ObjectMapper(); + properties = mapper.readValue(propertiesJson, new TypeReference>() { + }); + } catch (JsonProcessingException e) { + throw new EdcException(e); + } + + final boolean missingPublicApiProperty = !properties.containsKey(EmbeddedDataPlaneConfig.PUBLIC_API_URL_PROPERTY); + if (missingPublicApiProperty) { + monitor.warning(String.format(LOG_MISSING_CONFIGURATION, id, EmbeddedDataPlaneConfig.PROPERTIES_SUFFIX) + "." + EmbeddedDataPlaneConfig.PUBLIC_API_URL_PROPERTY); + } + + final boolean invalidConfiguration = + url.isEmpty() || sourceTypes.isEmpty() || destinationTypes.isEmpty(); + if (invalidConfiguration || missingPublicApiProperty) { + monitor.warning(String.format(LOG_SKIP_BC_MISSING_CONFIGURATION, id)); + return null; + } + final DataPlaneInstance.Builder builder = + DataPlaneInstance.Builder.newInstance().id(id).url(url); + + sourceTypes.forEach(builder::allowedSourceType); + destinationTypes.forEach(builder::allowedDestType); + properties.forEach(builder::property); + + dataPlaneInstance = builder.build(); + + monitor.debug( + String.format( + LOG_REGISTERED, + id, + url, + String.join(", ", sourceTypes), + String.join(", ", destinationTypes))); + return dataPlaneInstance; + } + private void configureDataPlaneInstance(final Config config) { + var dataPlaneInstance = createDataPlane(config); + if(nonNull(dataPlaneInstance)){ + selectorService.addInstance(dataPlaneInstance); + } + + + } +} \ No newline at end of file diff --git a/extensions/data-plane/embedded-data-plane-registration/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/extensions/data-plane/embedded-data-plane-registration/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension new file mode 100644 index 00000000000..370744c3a56 --- /dev/null +++ b/extensions/data-plane/embedded-data-plane-registration/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -0,0 +1 @@ +org.eclipse.edc.connector.dataplane.embedded.autoregistration.EmbeddedDataPlaneRegistration \ No newline at end of file diff --git a/extensions/data-plane/embedded-data-plane-registration/src/test/java/org/eclipse/edc/connector/dataplane/embedded/autoregistration/EmbeddedDataPlaneRegistrationTest.java b/extensions/data-plane/embedded-data-plane-registration/src/test/java/org/eclipse/edc/connector/dataplane/embedded/autoregistration/EmbeddedDataPlaneRegistrationTest.java new file mode 100644 index 00000000000..96dc7766c88 --- /dev/null +++ b/extensions/data-plane/embedded-data-plane-registration/src/test/java/org/eclipse/edc/connector/dataplane/embedded/autoregistration/EmbeddedDataPlaneRegistrationTest.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2024 Zub4t (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: + * Zub4t (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.edc.connector.dataplane.embedded.autoregistration; + + +import org.eclipse.edc.connector.dataplane.selector.spi.instance.DataPlaneInstance; +import org.eclipse.edc.connector.dataplane.spi.manager.DataPlaneManager; +import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.edc.spi.system.configuration.ConfigFactory; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.IntStream; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; + +@ExtendWith(DependencyInjectionExtension.class) +public class EmbeddedDataPlaneRegistrationTest { + + @Test + void testName(EmbeddedDataPlaneRegistration extension) { + Assertions.assertNotNull(extension.name()); + Assertions.assertEquals("Embedded DataPlane Auto Registration", extension.name()); + } + @Test + void embeddedDataPlaneRegistration_shouldThrowEdcException(EmbeddedDataPlaneRegistration extension, ServiceExtensionContext context) { + assertThrows(org.eclipse.edc.spi.EdcException.class, () -> { + extension.initialize(context); + var dataPlaneInstance = extension.createDataPlane(ConfigFactory.fromMap(Collections.emptyMap())); + + }); + } + @Test + void embeddedDataPlaneRegistration_shouldNotAddDataPlane(EmbeddedDataPlaneRegistration extension, ServiceExtensionContext context) { + var dataPlaneManager = mock(DataPlaneManager.class); + context.registerService(DataPlaneManager.class,dataPlaneManager); + extension.initialize(context); + var dataPlaneInstance = extension.createDataPlane(ConfigFactory.fromMap(Collections.emptyMap())); + assertThat(dataPlaneInstance).isNull(); + } + @Test + void embeddedDataPlaneRegistration_shouldAddDataPlane(EmbeddedDataPlaneRegistration extension, ServiceExtensionContext context) { + var dataPlaneManager = mock(DataPlaneManager.class); + context.registerService(DataPlaneManager.class,dataPlaneManager); + + List keys = List.of( + EmbeddedDataPlaneConfig.DESTINATION_TYPES_SUFFIX, + EmbeddedDataPlaneConfig.SOURCE_TYPES_SUFFIX, + EmbeddedDataPlaneConfig.URL_SUFFIX, + EmbeddedDataPlaneConfig.PROPERTIES_SUFFIX); + + List values = List.of("HttpProxy,HttpData","HttpData","http://localhost:19192/control/transfer","{\"publicApiUrl\": \"http://localhost:19291/public/\"}"); + Map map = IntStream.range(0, keys.size()) + .boxed() + .collect(HashMap::new, (m, i) -> m.put(keys.get(i), values.get(i)), Map::putAll); + + extension.initialize(context); + var dataPlaneInstance = extension.createDataPlane(ConfigFactory.fromMap(map)); + assertThat(dataPlaneInstance).isInstanceOf(DataPlaneInstance.class); + } +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index eeeab30bfd9..c9eb86c2280 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,4 +4,4 @@ version=0.5.2-SNAPSHOT annotationProcessorVersion=0.5.2-SNAPSHOT edcGradlePluginsVersion=0.5.2-SNAPSHOT edcScmUrl=https://github.com/eclipse-edc/Connector.git -edcScmConnection=scm:git:git@github.com:eclipse-edc/Connector.git +edcScmConnection=scm:git:git@github.com:eclipse-edc/Connector.git \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index b361b31d9fe..c91010a3946 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -177,6 +177,7 @@ include(":extensions:data-plane:data-plane-http-oauth2-core") include(":extensions:data-plane:data-plane-integration-tests") include(":extensions:data-plane:store:sql:data-plane-store-sql") include(":extensions:data-plane:data-plane-kafka") +include(":extensions:data-plane:embedded-data-plane-registration") include(":extensions:data-plane-selector:data-plane-selector-api") include(":extensions:data-plane-selector:data-plane-selector-client")