Skip to content

Commit

Permalink
feat: make DataAddressValidator extensible
Browse files Browse the repository at this point in the history
  • Loading branch information
ndr-brt committed Oct 26, 2023
1 parent 0afa286 commit 019ed67
Show file tree
Hide file tree
Showing 69 changed files with 660 additions and 542 deletions.
1 change: 1 addition & 0 deletions core/common/connector-core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ dependencies {
implementation(project(":core:common:state-machine"))
implementation(project(":core:common:transform-core"))
implementation(project(":core:common:util"))
implementation(project(":core:common:validator-core"))

implementation(libs.dnsOverHttps)
implementation(libs.bouncyCastle.bcpkixJdk18on)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ public TransactionContext defaultTransactionContext(ServiceExtensionContext cont
public DataSourceRegistry dataSourceRegistry(ServiceExtensionContext context) {
context.getMonitor().warning("No DataSourceRegistry registered, DefaultDataSourceRegistry will be used, not suitable for production environments");
return new DefaultDataSourceRegistry();

}

@Provider(isDefault = true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.eclipse.edc.connector.core.health.HealthCheckServiceImpl;
import org.eclipse.edc.connector.core.security.DefaultPrivateKeyParseFunction;
import org.eclipse.edc.connector.core.security.KeyPairFactoryImpl;
import org.eclipse.edc.connector.core.validator.DataAddressValidatorRegistryImpl;
import org.eclipse.edc.connector.core.validator.JsonObjectValidatorRegistryImpl;
import org.eclipse.edc.core.transform.TypeTransformerRegistryImpl;
import org.eclipse.edc.policy.engine.PolicyEngineImpl;
Expand Down Expand Up @@ -50,12 +51,17 @@
import org.eclipse.edc.spi.system.health.HealthCheckService;
import org.eclipse.edc.spi.types.TypeManager;
import org.eclipse.edc.transform.spi.TypeTransformerRegistry;
import org.eclipse.edc.validator.dataaddress.HttpDataDataAddressValidator;
import org.eclipse.edc.validator.dataaddress.KafkaDataAddressValidator;
import org.eclipse.edc.validator.spi.DataAddressValidatorRegistry;
import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry;
import org.eclipse.edc.validator.spi.ValidationResult;

import java.security.PrivateKey;
import java.time.Duration;

import static org.eclipse.edc.spi.agent.ParticipantAgentService.DEFAULT_IDENTITY_CLAIM_KEY;
import static org.eclipse.edc.spi.dataaddress.HttpDataAddressSchema.HTTP_DATA_TYPE;

@BaseExtension
@Extension(value = CoreServicesExtension.NAME)
Expand Down Expand Up @@ -193,6 +199,19 @@ public JsonObjectValidatorRegistry jsonObjectValidator() {
return new JsonObjectValidatorRegistryImpl();
}

@Provider
public DataAddressValidatorRegistry dataAddressValidatorRegistry(ServiceExtensionContext context) {
var validator = new DataAddressValidatorRegistryImpl(context.getMonitor());
var httpData = new HttpDataDataAddressValidator();
var kafka = new KafkaDataAddressValidator();
validator.registerSourceValidator(HTTP_DATA_TYPE, httpData);
validator.registerDestinationValidator(HTTP_DATA_TYPE, httpData);
validator.registerDestinationValidator("HttpProxy", dataAddress -> ValidationResult.success());
validator.registerSourceValidator("Kafka", kafka);
validator.registerDestinationValidator("Kafka", kafka);
return validator;
}

private HealthCheckServiceConfiguration getHealthCheckConfig(ServiceExtensionContext context) {
return HealthCheckServiceConfiguration.Builder.newInstance()
.livenessPeriod(Duration.ofSeconds(context.getSetting(LIVENESS_PERIOD_SECONDS_SETTING, DEFAULT_DURATION)))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright (c) 2023 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.connector.core.validator;

import org.eclipse.edc.spi.monitor.Monitor;
import org.eclipse.edc.spi.types.domain.DataAddress;
import org.eclipse.edc.validator.spi.DataAddressValidatorRegistry;
import org.eclipse.edc.validator.spi.ValidationResult;
import org.eclipse.edc.validator.spi.Validator;
import org.jetbrains.annotations.NotNull;

import java.util.HashMap;
import java.util.Map;

public class DataAddressValidatorRegistryImpl implements DataAddressValidatorRegistry {

private final Map<String, Validator<DataAddress>> sourceValidators = new HashMap<>();
private final Map<String, Validator<DataAddress>> destinationValidators = new HashMap<>();
private final Monitor monitor;

public DataAddressValidatorRegistryImpl(Monitor monitor) {
this.monitor = monitor;
}

@Override
public void registerSourceValidator(String type, Validator<DataAddress> validator) {
sourceValidators.put(type, validator);
}

@Override
public void registerDestinationValidator(String type, Validator<DataAddress> validator) {
destinationValidators.put(type, validator);
}

@Override
public ValidationResult validateSource(DataAddress dataAddress) {
return sourceValidators.getOrDefault(dataAddress.getType(), d -> warning("source")).validate(dataAddress);
}

@Override
public ValidationResult validateDestination(DataAddress dataAddress) {
return destinationValidators.getOrDefault(dataAddress.getType(), d -> warning("destination")).validate(dataAddress);
}

@NotNull
private ValidationResult warning(String type) {
monitor.warning("No %s DataAddress validator has been registered, please register one as it is strongly recommended.".formatted(type));
return ValidationResult.success();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* Copyright (c) 2023 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.connector.core.validator;

import org.eclipse.edc.spi.monitor.Monitor;
import org.eclipse.edc.spi.types.domain.DataAddress;
import org.eclipse.edc.validator.spi.DataAddressValidatorRegistry;
import org.eclipse.edc.validator.spi.ValidationResult;
import org.eclipse.edc.validator.spi.Validator;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

class DataAddressValidatorRegistryImplTest {

private final Monitor monitor = mock();
private final DataAddressValidatorRegistry validator = new DataAddressValidatorRegistryImpl(monitor);

@Nested
class Source {
@Test
void shouldCallRegisteredValidator() {
Validator<DataAddress> typeValidator = mock();
when(typeValidator.validate(any())).thenReturn(ValidationResult.success());
validator.registerSourceValidator("type", typeValidator);
var dataAddress = DataAddress.Builder.newInstance()
.property("type", "type")
.build();

var result = validator.validateSource(dataAddress);

assertThat(result).isSucceeded();
verify(typeValidator).validate(dataAddress);
}

@Test
void shouldSucceedWithWarning_whenTypeIsNotRegistered() {
var dataAddress = DataAddress.Builder.newInstance()
.property("type", "not-registered")
.build();

var result = validator.validateSource(dataAddress);

assertThat(result).isSucceeded();
verify(monitor).warning(anyString());
}
}

@Nested
class Destination {
@Test
void shouldCallRegisteredValidator() {
Validator<DataAddress> typeValidator = mock();
when(typeValidator.validate(any())).thenReturn(ValidationResult.success());
validator.registerDestinationValidator("type", typeValidator);
var dataAddress = DataAddress.Builder.newInstance()
.property("type", "type")
.build();

var result = validator.validateDestination(dataAddress);

assertThat(result).isSucceeded();
verify(typeValidator).validate(dataAddress);
}

@Test
void shouldSucceedWithWarning_whenTypeIsNotRegistered() {
var dataAddress = DataAddress.Builder.newInstance()
.property("type", "not-registered")
.build();

var result = validator.validateDestination(dataAddress);

assertThat(result).isSucceeded();
verify(monitor).warning(anyString());
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright (c) 2023 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.validator.dataaddress;

import org.eclipse.edc.spi.types.domain.DataAddress;
import org.eclipse.edc.validator.spi.ValidationResult;
import org.eclipse.edc.validator.spi.Validator;

import java.net.MalformedURLException;
import java.net.URL;

import static org.eclipse.edc.spi.dataaddress.HttpDataAddressSchema.BASE_URL;
import static org.eclipse.edc.spi.dataaddress.HttpDataAddressSchema.HTTP_DATA_TYPE;
import static org.eclipse.edc.validator.spi.Violation.violation;

/**
* Validator for HttpData type
*/
public class HttpDataDataAddressValidator implements Validator<DataAddress> {

@Override
public ValidationResult validate(DataAddress dataAddress) {
var baseUrl = dataAddress.getStringProperty(BASE_URL);
try {
new URL(baseUrl);
return ValidationResult.success();
} catch (MalformedURLException e) {
var violation = violation("DataAddress of type %s must contain a valid baseUrl.".formatted(HTTP_DATA_TYPE), BASE_URL, baseUrl);
return ValidationResult.failure(violation);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2023 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.validator.dataaddress;

import org.eclipse.edc.spi.types.domain.DataAddress;
import org.eclipse.edc.validator.spi.ValidationResult;
import org.eclipse.edc.validator.spi.Validator;

import java.util.Objects;
import java.util.stream.Stream;

import static org.eclipse.edc.spi.dataaddress.KafkaDataAddressSchema.BOOTSTRAP_SERVERS;
import static org.eclipse.edc.spi.dataaddress.KafkaDataAddressSchema.TOPIC;
import static org.eclipse.edc.validator.spi.Violation.violation;

public class KafkaDataAddressValidator implements Validator<DataAddress> {

@Override
public ValidationResult validate(DataAddress input) {
var violations = Stream.of(TOPIC, BOOTSTRAP_SERVERS)
.map(it -> {
var value = input.getStringProperty(it);
if (value == null || value.isBlank()) {
return violation("'%s' is a mandatory attribute".formatted(it), it, value);
}
return null;
})
.filter(Objects::nonNull)
.toList();

if (violations.isEmpty()) {
return ValidationResult.success();
}

return ValidationResult.failure(violations);
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
* Copyright (c) 2023 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
Expand All @@ -8,39 +8,28 @@
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - improvements
* Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation
*
*/

package org.eclipse.edc.connector.service.dataaddress;
package org.eclipse.edc.validator.dataaddress;

import org.eclipse.edc.spi.types.domain.DataAddress;
import org.eclipse.edc.validator.spi.DataAddressValidator;
import org.junit.jupiter.api.Test;

import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat;
import static org.eclipse.edc.spi.types.domain.HttpDataAddress.HTTP_DATA;
import static org.eclipse.edc.spi.dataaddress.HttpDataAddressSchema.BASE_URL;
import static org.eclipse.edc.spi.dataaddress.HttpDataAddressSchema.HTTP_DATA_TYPE;

class DataAddressValidatorImplTest {
class HttpDataDataAddressValidatorImplTest {

private final DataAddressValidator validator = new DataAddressValidatorImpl();

@Test
void shouldPass_whenTypeDoesNotNeedValidation() {
var dataAddress = DataAddress.Builder.newInstance()
.property("type", "any")
.build();

var result = validator.validate(dataAddress);

assertThat(result).isSucceeded();
}
private final HttpDataDataAddressValidator validator = new HttpDataDataAddressValidator();

@Test
void shouldPass_whenHttpDataIsValid() {
var dataAddress = DataAddress.Builder.newInstance()
.property("type", HTTP_DATA)
.property("baseUrl", "http://this.is/valid/url")
.property("type", HTTP_DATA_TYPE)
.property(BASE_URL, "http://this.is/valid/url")
.build();

var result = validator.validate(dataAddress);
Expand All @@ -51,12 +40,13 @@ void shouldPass_whenHttpDataIsValid() {
@Test
void shouldFail_whenHttpDataBaseUrlNotValid() {
var dataAddress = DataAddress.Builder.newInstance()
.property("type", HTTP_DATA)
.property("baseUrl", "not-a-valid-url")
.property("type", HTTP_DATA_TYPE)
.property(BASE_URL, "not-a-valid-url")
.build();

var result = validator.validate(dataAddress);

assertThat(result).isFailed();
}

}
Loading

0 comments on commit 019ed67

Please sign in to comment.