Skip to content

Commit

Permalink
feat: implement TransferProcess api controller validation (#3217)
Browse files Browse the repository at this point in the history
feat: implement TransferProcess api controller validation
  • Loading branch information
ndr-brt authored Jun 23, 2023
1 parent 6afc66d commit 070aa11
Show file tree
Hide file tree
Showing 31 changed files with 810 additions and 677 deletions.
1 change: 1 addition & 0 deletions extensions/common/api/api-core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ dependencies {
api(project(":spi:common:json-ld-spi"))

implementation(project(":core:common:util"))
implementation(project(":core:common:validator-core"))
implementation(libs.jakarta.rsApi)
implementation(libs.jakarta.validation)
implementation(libs.jersey.beanvalidation) //for validation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,14 @@
package org.eclipse.edc.api.model;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.NotNull;

import java.util.Map;

@JsonDeserialize(builder = DataAddressDto.Builder.class)
public class DataAddressDto extends BaseDto {

@NotNull(message = "properties cannot be null")
private Map<String, String> properties;

private DataAddressDto() {
Expand All @@ -36,12 +32,6 @@ public Map<String, String> getProperties() {
return properties;
}

@JsonIgnore
@AssertTrue(message = "property keys cannot be blank and property 'type' is mandatory")
public boolean isValid() {
return properties != null && properties.keySet().stream().noneMatch(it -> it == null || it.isBlank()) && properties.containsKey("type");
}

@JsonPOJOBuilder(withPrefix = "")
public static final class Builder {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* 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.api.validation;

import jakarta.json.JsonObject;
import org.eclipse.edc.validator.jsonobject.JsonObjectValidator;
import org.eclipse.edc.validator.jsonobject.validators.MandatoryValue;
import org.eclipse.edc.validator.spi.Validator;

import static org.eclipse.edc.spi.types.domain.DataAddress.EDC_DATA_ADDRESS_TYPE_PROPERTY;

public class DataAddressDtoValidator {
public static Validator<JsonObject> instance() {
return instance(JsonObjectValidator.newValidator()).build();
}

public static JsonObjectValidator.Builder instance(JsonObjectValidator.Builder builder) {
return builder
.verify(EDC_DATA_ADDRESS_TYPE_PROPERTY, MandatoryValue::new);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* 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.api.validation;

import jakarta.json.JsonArrayBuilder;
import jakarta.json.JsonObject;
import org.eclipse.edc.validator.spi.ValidationFailure;
import org.eclipse.edc.validator.spi.Validator;
import org.eclipse.edc.validator.spi.Violation;
import org.junit.jupiter.api.Test;

import static jakarta.json.Json.createArrayBuilder;
import static jakarta.json.Json.createObjectBuilder;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.InstanceOfAssertFactories.list;
import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.VALUE;
import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat;
import static org.eclipse.edc.spi.types.domain.DataAddress.EDC_DATA_ADDRESS_TYPE_PROPERTY;

class DataAddressDtoValidatorTest {

private final Validator<JsonObject> validator = DataAddressDtoValidator.instance();

@Test
void shouldSucceed_whenObjectIsValid() {
var contractDefinition = createObjectBuilder()
.add(EDC_DATA_ADDRESS_TYPE_PROPERTY, value("value"))
.build();

var result = validator.validate(contractDefinition);

assertThat(result).isSucceeded();
}

@Test
void shouldFail_whenMandatoryFieldsAreMissing() {
var contractDefinition = createObjectBuilder().build();

var result = validator.validate(contractDefinition);

assertThat(result).isFailed().extracting(ValidationFailure::getViolations).asInstanceOf(list(Violation.class))
.isNotEmpty()
.anySatisfy(violation -> assertThat(violation.path()).isEqualTo(EDC_DATA_ADDRESS_TYPE_PROPERTY));
}

private JsonArrayBuilder value(String value) {
return createArrayBuilder().add(createObjectBuilder().add(VALUE, value));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package org.eclipse.edc.connector.api.management.asset;

import org.eclipse.edc.api.validation.DataAddressDtoValidator;
import org.eclipse.edc.connector.api.management.asset.transform.AssetRequestDtoToAssetTransformer;
import org.eclipse.edc.connector.api.management.asset.transform.AssetToAssetResponseDtoTransformer;
import org.eclipse.edc.connector.api.management.asset.transform.AssetUpdateRequestWrapperDtoToAssetTransformer;
Expand All @@ -33,6 +34,7 @@
import org.eclipse.edc.web.spi.WebService;

import static org.eclipse.edc.connector.api.management.asset.model.AssetEntryNewDto.EDC_ASSET_ENTRY_DTO_TYPE;
import static org.eclipse.edc.spi.types.domain.DataAddress.EDC_DATA_ADDRESS_TYPE;
import static org.eclipse.edc.spi.types.domain.asset.Asset.EDC_ASSET_TYPE;

@Extension(value = AssetApiExtension.NAME)
Expand Down Expand Up @@ -74,6 +76,7 @@ public void initialize(ServiceExtensionContext context) {

validator.register(EDC_ASSET_ENTRY_DTO_TYPE, AssetEntryDtoValidator.assetEntryValidator());
validator.register(EDC_ASSET_TYPE, AssetEntryDtoValidator.assetValidator());
validator.register(EDC_DATA_ADDRESS_TYPE, DataAddressDtoValidator.instance());

webService.registerResource(config.getContextAlias(), new AssetApiController(assetService, dataAddressResolver, transformerRegistry, monitor, validator));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,11 @@
package org.eclipse.edc.connector.api.management.asset.model;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
import jakarta.validation.constraints.AssertTrue;

import java.util.Map;
import java.util.Optional;

import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.ID;

Expand All @@ -35,27 +32,6 @@ public class AssetCreationRequestDto extends AssetRequestDto {
private AssetCreationRequestDto() {
}

@JsonIgnore
@AssertTrue(message = "no empty property keys and no duplicate keys")
public boolean isValid() {
return mapKeysValid();
}

@JsonIgnore
@AssertTrue(message = "no duplicate keys in properties and private properties")
public boolean isDistinctKeysValid() {
return checkDistinctKeys();
}

@JsonIgnore
@AssertTrue(message = "id must be either null or not blank")
public boolean isIdValid() {
return Optional.of(this)
.map(it -> it.id)
.map(it -> !id.isBlank())
.orElse(true);
}

public Map<String, Object> getProperties() {
return properties;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,11 @@
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import org.eclipse.edc.api.model.DataAddressDto;

@JsonDeserialize(builder = AssetEntryDto.Builder.class)
public class AssetEntryDto {
@NotNull(message = "asset cannot be null")
@Valid
private AssetCreationRequestDto asset;
@NotNull(message = "dataAddress cannot be null")
@Valid
private DataAddressDto dataAddress;

private AssetEntryDto() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

package org.eclipse.edc.connector.api.management.asset.model;

import jakarta.validation.constraints.NotNull;
import org.eclipse.edc.api.model.BaseDto;

import java.util.HashMap;
Expand All @@ -23,24 +22,10 @@

public abstract class AssetRequestDto extends BaseDto {

@NotNull(message = "properties cannot be null")
protected Map<String, Object> properties;

protected Map<String, Object> privateProperties = new HashMap<>();

protected boolean checkDistinctKeys() {
if (privateProperties != null && properties != null) {
return properties.keySet().stream().distinct().noneMatch(privateProperties::containsKey);
}
return false;
}

protected boolean mapKeysValid() {
boolean validPrivate = privateProperties != null && privateProperties.keySet().stream().noneMatch(it -> it == null || it.isBlank());
boolean validPublic = properties != null && properties.keySet().stream().noneMatch(it -> it == null || it.isBlank());
return validPrivate && validPublic;
}

protected abstract static class Builder<A extends AssetRequestDto, B extends Builder<A, B>> {

protected final A dto;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,8 @@
package org.eclipse.edc.connector.api.management.asset.model;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
import jakarta.validation.constraints.AssertTrue;

import java.util.Map;

Expand All @@ -29,18 +27,6 @@ public class AssetUpdateRequestDto extends AssetRequestDto {
private AssetUpdateRequestDto() {
}

@JsonIgnore
@AssertTrue(message = "no empty property keys and no duplicate keys")
public boolean isValid() {
return mapKeysValid();
}

@JsonIgnore
@AssertTrue(message = "no duplicate keys in properties and private properties")
public boolean isDistinctKeysValid() {
return checkDistinctKeys();
}

public Map<String, Object> getProperties() {
return properties;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,15 @@
package org.eclipse.edc.connector.api.management.asset.validation;

import jakarta.json.JsonObject;
import org.eclipse.edc.api.validation.DataAddressDtoValidator;
import org.eclipse.edc.validator.jsonobject.JsonObjectValidator;
import org.eclipse.edc.validator.jsonobject.validators.MandatoryObject;
import org.eclipse.edc.validator.jsonobject.validators.MandatoryValue;
import org.eclipse.edc.validator.jsonobject.validators.OptionalIdNotBlank;
import org.eclipse.edc.validator.spi.ValidationResult;
import org.eclipse.edc.validator.spi.Validator;

import static org.eclipse.edc.connector.api.management.asset.model.AssetEntryNewDto.EDC_ASSET_ENTRY_DTO_ASSET;
import static org.eclipse.edc.connector.api.management.asset.model.AssetEntryNewDto.EDC_ASSET_ENTRY_DTO_DATA_ADDRESS;
import static org.eclipse.edc.spi.types.domain.DataAddress.EDC_DATA_ADDRESS_TYPE_PROPERTY;
import static org.eclipse.edc.spi.types.domain.asset.Asset.EDC_ASSET_PRIVATE_PROPERTIES;
import static org.eclipse.edc.spi.types.domain.asset.Asset.EDC_ASSET_PROPERTIES;
import static org.eclipse.edc.validator.spi.Violation.violation;
Expand All @@ -43,9 +42,7 @@ public static Validator<JsonObject> assetEntryValidator() {
.verify(path -> new AssetPropertiesUniqueness())
)
.verify(EDC_ASSET_ENTRY_DTO_DATA_ADDRESS, MandatoryObject::new)
.verifyObject(EDC_ASSET_ENTRY_DTO_DATA_ADDRESS, v -> v
.verify(EDC_DATA_ADDRESS_TYPE_PROPERTY, MandatoryValue::new)
)
.verifyObject(EDC_ASSET_ENTRY_DTO_DATA_ADDRESS, DataAddressDtoValidator::instance)
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@

import org.eclipse.edc.junit.extensions.DependencyInjectionExtension;
import org.eclipse.edc.spi.system.ServiceExtensionContext;
import org.eclipse.edc.spi.system.injection.ObjectFactory;
import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

import static org.eclipse.edc.connector.api.management.asset.model.AssetEntryNewDto.EDC_ASSET_ENTRY_DTO_TYPE;
import static org.eclipse.edc.spi.types.domain.DataAddress.EDC_DATA_ADDRESS_TYPE;
import static org.eclipse.edc.spi.types.domain.asset.Asset.EDC_ASSET_TYPE;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
Expand All @@ -33,19 +33,18 @@
class AssetApiExtensionTest {

private final JsonObjectValidatorRegistry validatorRegistry = mock();
private AssetApiExtension extension;

@BeforeEach
void setUp(ObjectFactory factory, ServiceExtensionContext context) {
void setUp(ServiceExtensionContext context) {
context.registerService(JsonObjectValidatorRegistry.class, validatorRegistry);
extension = factory.constructInstance(AssetApiExtension.class);
}

@Test
void initialize_shouldRegisterValidators(ServiceExtensionContext context) {
void initialize_shouldRegisterValidators(AssetApiExtension extension, ServiceExtensionContext context) {
extension.initialize(context);

verify(validatorRegistry).register(eq(EDC_ASSET_TYPE), any());
verify(validatorRegistry).register(eq(EDC_ASSET_ENTRY_DTO_TYPE), any());
verify(validatorRegistry).register(eq(EDC_DATA_ADDRESS_TYPE), any());
}
}
Loading

0 comments on commit 070aa11

Please sign in to comment.