diff --git a/core/common/validator-core/src/main/java/org/eclipse/edc/validator/jsonobject/JsonObjectValidator.java b/core/common/validator-core/src/main/java/org/eclipse/edc/validator/jsonobject/JsonObjectValidator.java index 6dcf5023ac4..8a64b56d697 100644 --- a/core/common/validator-core/src/main/java/org/eclipse/edc/validator/jsonobject/JsonObjectValidator.java +++ b/core/common/validator-core/src/main/java/org/eclipse/edc/validator/jsonobject/JsonObjectValidator.java @@ -18,6 +18,7 @@ import jakarta.json.JsonString; import org.eclipse.edc.validator.spi.ValidationResult; import org.eclipse.edc.validator.spi.Validator; +import org.eclipse.edc.validator.spi.Violation; import java.util.ArrayList; import java.util.List; @@ -51,6 +52,10 @@ protected JsonObjectValidator(JsonLdPath path, JsonWalker walker) { @Override public ValidationResult validate(JsonObject input) { + if (input == null) { + return ValidationResult.failure(Violation.violation("input json is null", path.toString())); + } + var violations = walker.extract(input, path) .flatMap(target -> this.validators.stream().map(validator -> validator.validate(target))) .filter(ValidationResult::failed) diff --git a/core/common/validator-core/src/test/java/org/eclipse/edc/validator/jsonobject/JsonObjectValidatorTest.java b/core/common/validator-core/src/test/java/org/eclipse/edc/validator/jsonobject/JsonObjectValidatorTest.java index 648f6f83ac1..36e3e55eb70 100644 --- a/core/common/validator-core/src/test/java/org/eclipse/edc/validator/jsonobject/JsonObjectValidatorTest.java +++ b/core/common/validator-core/src/test/java/org/eclipse/edc/validator/jsonobject/JsonObjectValidatorTest.java @@ -18,11 +18,14 @@ 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.ValidationFailure; +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.ID; import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.VALUE; import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat; @@ -164,6 +167,19 @@ void shouldValidateNestedArrayItem_failure() { }); } + @Test + void shouldFail_whenInputIsNull() { + var result = JsonObjectValidator.newValidator().build() + .validate(null); + + assertThat(result).isFailed().extracting(ValidationFailure::getViolations).asInstanceOf(list(Violation.class)) + .hasSize(1) + .first().satisfies(violation -> { + assertThat(violation.message()).contains("null"); + assertThat(violation.path()).isEmpty(); + }); + } + private JsonArrayBuilder value(String value) { return createArrayBuilder().add(createObjectBuilder().add(VALUE, value)); } diff --git a/extensions/common/api/api-core/src/main/java/org/eclipse/edc/api/ApiCoreExtension.java b/extensions/common/api/api-core/src/main/java/org/eclipse/edc/api/ApiCoreExtension.java index 8ae6cc0e0b7..db02d7dbf24 100644 --- a/extensions/common/api/api-core/src/main/java/org/eclipse/edc/api/ApiCoreExtension.java +++ b/extensions/common/api/api-core/src/main/java/org/eclipse/edc/api/ApiCoreExtension.java @@ -26,15 +26,18 @@ import org.eclipse.edc.api.transformer.JsonObjectToCallbackAddressTransformer; import org.eclipse.edc.api.transformer.JsonObjectToCriterionDtoTransformer; import org.eclipse.edc.api.transformer.QuerySpecDtoToQuerySpecTransformer; +import org.eclipse.edc.api.validation.QuerySpecDtoValidator; import org.eclipse.edc.runtime.metamodel.annotation.Extension; import org.eclipse.edc.runtime.metamodel.annotation.Inject; import org.eclipse.edc.spi.system.ServiceExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; import org.eclipse.edc.spi.types.TypeManager; import org.eclipse.edc.transform.spi.TypeTransformerRegistry; +import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry; import java.util.Map; +import static org.eclipse.edc.api.model.QuerySpecDto.EDC_QUERY_SPEC_TYPE; import static org.eclipse.edc.spi.CoreConstants.JSON_LD; @Extension(value = ApiCoreExtension.NAME) @@ -48,6 +51,9 @@ public class ApiCoreExtension implements ServiceExtension { @Inject private TypeManager typeManager; + @Inject + private JsonObjectValidatorRegistry validatorRegistry; + @Override public String name() { return NAME; @@ -71,5 +77,7 @@ public void initialize(ServiceExtensionContext context) { transformerRegistry.register(new JsonObjectToCallbackAddressTransformer()); transformerRegistry.register(new JsonObjectToCriterionDtoTransformer()); + + validatorRegistry.register(EDC_QUERY_SPEC_TYPE, QuerySpecDtoValidator.instance()); } } diff --git a/extensions/common/api/api-core/src/main/java/org/eclipse/edc/api/model/CriterionDto.java b/extensions/common/api/api-core/src/main/java/org/eclipse/edc/api/model/CriterionDto.java index 30ab82492fc..2b8bc8a7190 100644 --- a/extensions/common/api/api-core/src/main/java/org/eclipse/edc/api/model/CriterionDto.java +++ b/extensions/common/api/api-core/src/main/java/org/eclipse/edc/api/model/CriterionDto.java @@ -17,7 +17,6 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; -import jakarta.validation.constraints.NotNull; import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE; @@ -25,14 +24,12 @@ public class CriterionDto extends BaseDto { // constants for JSON-LD transformation + public static final String CRITERION_TYPE = EDC_NAMESPACE + "CriterionDto"; public static final String CRITERION_OPERAND_LEFT = EDC_NAMESPACE + "operandLeft"; public static final String CRITERION_OPERAND_RIGHT = EDC_NAMESPACE + "operandRight"; public static final String CRITERION_OPERATOR = EDC_NAMESPACE + "operator"; - public static final String CRITERION_TYPE = EDC_NAMESPACE + "CriterionDto"; - @NotNull(message = "operandLeft cannot be null") private Object operandLeft; - @NotNull(message = "operator cannot be null") private String operator; private Object operandRight; diff --git a/extensions/common/api/api-core/src/main/java/org/eclipse/edc/api/model/QuerySpecDto.java b/extensions/common/api/api-core/src/main/java/org/eclipse/edc/api/model/QuerySpecDto.java index 9d8bd2f9c5d..79c83bd9c49 100644 --- a/extensions/common/api/api-core/src/main/java/org/eclipse/edc/api/model/QuerySpecDto.java +++ b/extensions/common/api/api-core/src/main/java/org/eclipse/edc/api/model/QuerySpecDto.java @@ -15,13 +15,8 @@ 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.Positive; -import jakarta.validation.constraints.PositiveOrZero; -import jakarta.ws.rs.QueryParam; import org.eclipse.edc.spi.query.SortOrder; import java.util.ArrayList; @@ -39,21 +34,11 @@ public class QuerySpecDto extends BaseDto { public static final String EDC_QUERY_SPEC_SORT_ORDER = EDC_NAMESPACE + "sortOrder"; public static final String EDC_QUERY_SPEC_SORT_FIELD = EDC_NAMESPACE + "sortField"; - @QueryParam("offset") - @PositiveOrZero(message = "offset must be greater or equal to zero") private Integer offset = 0; - - @QueryParam("limit") - @Positive(message = "limit must be greater than 0") private Integer limit = 50; - - private final List filterExpression = new ArrayList<>(); - - @QueryParam("sort") private SortOrder sortOrder = SortOrder.ASC; - - @QueryParam("sortField") private String sortField; + private final List filterExpression = new ArrayList<>(); public Integer getOffset() { return offset; @@ -71,12 +56,6 @@ public String getSortField() { return sortField; } - @JsonIgnore - @AssertTrue - public boolean isValid() { - return sortField == null || !sortField.isBlank(); - } - public List getFilterExpression() { return filterExpression; } diff --git a/extensions/common/api/api-core/src/main/java/org/eclipse/edc/api/validation/CriterionDtoValidator.java b/extensions/common/api/api-core/src/main/java/org/eclipse/edc/api/validation/CriterionDtoValidator.java new file mode 100644 index 00000000000..2d80ff15588 --- /dev/null +++ b/extensions/common/api/api-core/src/main/java/org/eclipse/edc/api/validation/CriterionDtoValidator.java @@ -0,0 +1,65 @@ +/* + * 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.JsonLdPath; +import org.eclipse.edc.validator.jsonobject.JsonObjectValidator; +import org.eclipse.edc.validator.jsonobject.validators.MandatoryValue; +import org.eclipse.edc.validator.spi.ValidationResult; +import org.eclipse.edc.validator.spi.Validator; +import org.eclipse.edc.validator.spi.Violation; + +import java.util.Optional; + +import static java.lang.String.format; +import static org.eclipse.edc.api.model.CriterionDto.CRITERION_OPERAND_LEFT; +import static org.eclipse.edc.api.model.CriterionDto.CRITERION_OPERAND_RIGHT; +import static org.eclipse.edc.api.model.CriterionDto.CRITERION_OPERATOR; +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.VALUE; + +public class CriterionDtoValidator { + + public static Validator instance() { + return instance(JsonObjectValidator.newValidator()).build(); + } + + public static JsonObjectValidator.Builder instance(JsonObjectValidator.Builder builder) { + return builder + .verify(CRITERION_OPERAND_LEFT, MandatoryValue::new) + .verify(CRITERION_OPERATOR, MandatoryValue::new) + .verify(OperandRightValidator::new); + } + + private record OperandRightValidator(JsonLdPath path) implements Validator { + + @Override + public ValidationResult validate(JsonObject input) { + var operator = Optional.ofNullable(input.getJsonArray(CRITERION_OPERATOR)) + .map(it -> it.getJsonObject(0)) + .map(it -> it.getString(VALUE)) + .orElse(null); + + if (operator == null || "in".equals(operator)) { + return ValidationResult.success(); + } + + return Optional.ofNullable(input.getJsonArray(CRITERION_OPERAND_RIGHT)) + .filter(it -> it.size() == 1) + .map(it -> ValidationResult.success()) + .orElse(ValidationResult.failure(Violation.violation(format("%s cannot contain multiple values as the operator is not 'in'", path.toString()), CRITERION_OPERAND_RIGHT))); + } + } +} diff --git a/extensions/common/api/api-core/src/main/java/org/eclipse/edc/api/validation/QuerySpecDtoValidator.java b/extensions/common/api/api-core/src/main/java/org/eclipse/edc/api/validation/QuerySpecDtoValidator.java new file mode 100644 index 00000000000..eda9db7a817 --- /dev/null +++ b/extensions/common/api/api-core/src/main/java/org/eclipse/edc/api/validation/QuerySpecDtoValidator.java @@ -0,0 +1,121 @@ +/* + * 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.spi.query.SortOrder; +import org.eclipse.edc.validator.jsonobject.JsonLdPath; +import org.eclipse.edc.validator.jsonobject.JsonObjectValidator; +import org.eclipse.edc.validator.spi.ValidationResult; +import org.eclipse.edc.validator.spi.Validator; + +import java.util.Arrays; +import java.util.Optional; + +import static java.lang.String.format; +import static java.util.Collections.emptyList; +import static org.eclipse.edc.api.model.QuerySpecDto.EDC_QUERY_SPEC_FILTER_EXPRESSION; +import static org.eclipse.edc.api.model.QuerySpecDto.EDC_QUERY_SPEC_LIMIT; +import static org.eclipse.edc.api.model.QuerySpecDto.EDC_QUERY_SPEC_OFFSET; +import static org.eclipse.edc.api.model.QuerySpecDto.EDC_QUERY_SPEC_SORT_FIELD; +import static org.eclipse.edc.api.model.QuerySpecDto.EDC_QUERY_SPEC_SORT_ORDER; +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.VALUE; +import static org.eclipse.edc.validator.spi.Violation.violation; + +public class QuerySpecDtoValidator { + + public static Validator instance() { + return instance(JsonObjectValidator.newValidator()).build(); + } + + public static JsonObjectValidator.Builder instance(JsonObjectValidator.Builder builder) { + return builder + .verify(EDC_QUERY_SPEC_OFFSET, OptionalValueGreaterEqualZero::new) + .verify(EDC_QUERY_SPEC_LIMIT, OptionalValueGreaterZero::new) + .verify(EDC_QUERY_SPEC_SORT_ORDER, OptionalValueSortField::new) + .verify(EDC_QUERY_SPEC_SORT_FIELD, OptionalValueNotBlank::new) + .verifyObject(EDC_QUERY_SPEC_FILTER_EXPRESSION, CriterionDtoValidator::instance); + } + + private record OptionalValueGreaterEqualZero(JsonLdPath path) implements Validator { + + @Override + public ValidationResult validate(JsonObject input) { + var value = Optional.ofNullable(input.getJsonArray(path.last())) + .map(it -> it.getJsonObject(0)) + .map(it -> it.getInt(VALUE)) + .orElse(0); + + if (value < 0) { + return ValidationResult.failure(violation(format("optional value '%s' must be greater or equal to zero", path), path.toString(), value)); + } + + return ValidationResult.success(); + } + } + + private record OptionalValueGreaterZero(JsonLdPath path) implements Validator { + + @Override + public ValidationResult validate(JsonObject input) { + var value = Optional.ofNullable(input.getJsonArray(path.last())) + .map(it -> it.getJsonObject(0)) + .map(it -> it.getInt(VALUE)) + .orElse(1); + + if (value < 1) { + return ValidationResult.failure(violation(format("optional value '%s' must be greater than zero", path), path.toString(), value)); + } + + return ValidationResult.success(); + } + } + + private record OptionalValueSortField(JsonLdPath path) implements Validator { + + @Override + public ValidationResult validate(JsonObject input) { + var values = Optional.ofNullable(input.getJsonArray(path.last())) + .map(array -> array.stream().map(it -> it.asJsonObject().getString(VALUE)).toList()) + .orElse(emptyList()); + + if (values.size() > 0 && !Arrays.stream(SortOrder.values()).map(Enum::name).toList().contains(values.get(0))) { + var message = format("optional value '%s' must be one of %s", path, Arrays.toString(SortOrder.values())); + return ValidationResult.failure(violation(message, path.toString(), values.get(0))); + } + + return ValidationResult.success(); + } + } + + private record OptionalValueNotBlank(JsonLdPath path) implements Validator { + + @Override + public ValidationResult validate(JsonObject input) { + var optional = Optional.ofNullable(input.getJsonArray(path.last())) + .map(it -> it.getJsonObject(0)) + .map(it -> it.getString(VALUE)); + + if (optional.isEmpty()) { + return ValidationResult.success(); + } + + return optional + .filter(it -> !it.isBlank()) + .map(it -> ValidationResult.success()) + .orElseGet(() -> ValidationResult.failure(violation(format("optional value '%s' is blank", path), path.toString()))); + } + } +} diff --git a/extensions/common/api/api-core/src/test/java/org/eclipse/edc/api/ApiCoreExtensionTest.java b/extensions/common/api/api-core/src/test/java/org/eclipse/edc/api/ApiCoreExtensionTest.java new file mode 100644 index 00000000000..b3acacd1eb2 --- /dev/null +++ b/extensions/common/api/api-core/src/test/java/org/eclipse/edc/api/ApiCoreExtensionTest.java @@ -0,0 +1,46 @@ +/* + * 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; + +import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +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.api.model.QuerySpecDto.EDC_QUERY_SPEC_TYPE; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +@ExtendWith(DependencyInjectionExtension.class) +class ApiCoreExtensionTest { + + private final JsonObjectValidatorRegistry validatorRegistry = mock(); + + @BeforeEach + void setUp(ServiceExtensionContext context) { + context.registerService(JsonObjectValidatorRegistry.class, validatorRegistry); + } + + @Test + void initialize_shouldRegisterValidators(ApiCoreExtension extension, ServiceExtensionContext context) { + extension.initialize(context); + + verify(validatorRegistry).register(eq(EDC_QUERY_SPEC_TYPE), any()); + } +} diff --git a/extensions/common/api/api-core/src/test/java/org/eclipse/edc/api/query/QuerySpecDtoValidationTest.java b/extensions/common/api/api-core/src/test/java/org/eclipse/edc/api/query/QuerySpecDtoValidationTest.java deleted file mode 100644 index 92885eeffd9..00000000000 --- a/extensions/common/api/api-core/src/test/java/org/eclipse/edc/api/query/QuerySpecDtoValidationTest.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2022 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.query; - -import jakarta.validation.Validation; -import jakarta.validation.Validator; -import jakarta.validation.ValidatorFactory; -import org.eclipse.edc.api.model.QuerySpecDto; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -class QuerySpecDtoValidationTest { - - private Validator validator; - - @BeforeEach - void setUp() { - try (ValidatorFactory factory = Validation.buildDefaultValidatorFactory()) { - validator = factory.getValidator(); - } - } - - @Test - void defaultQuerySpecIsValid() { - var querySpec = QuerySpecDto.Builder.newInstance().build(); - - var result = validator.validate(querySpec); - - assertThat(result).isEmpty(); - } - - @Test - void limitShouldBeGreaterThanZero() { - var querySpec = QuerySpecDto.Builder.newInstance().limit(0).build(); - - var result = validator.validate(querySpec); - - assertThat(result).isNotEmpty(); - } - - @Test - void offsetShouldBeGreaterOrEqualThanZero() { - var querySpec = QuerySpecDto.Builder.newInstance().offset(-1).build(); - - var result = validator.validate(querySpec); - - assertThat(result).isNotEmpty(); - } - - @Test - void sortFieldShouldNotBeBlank() { - var querySpec = QuerySpecDto.Builder.newInstance().sortField(" ").build(); - - var result = validator.validate(querySpec); - - assertThat(result).isNotEmpty(); - } -} diff --git a/extensions/common/api/api-core/src/test/java/org/eclipse/edc/api/validation/CriterionDtoValidatorTest.java b/extensions/common/api/api-core/src/test/java/org/eclipse/edc/api/validation/CriterionDtoValidatorTest.java new file mode 100644 index 00000000000..18582d55ee9 --- /dev/null +++ b/extensions/common/api/api-core/src/test/java/org/eclipse/edc/api/validation/CriterionDtoValidatorTest.java @@ -0,0 +1,86 @@ +/* + * 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.Json; +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.api.model.CriterionDto.CRITERION_OPERAND_LEFT; +import static org.eclipse.edc.api.model.CriterionDto.CRITERION_OPERAND_RIGHT; +import static org.eclipse.edc.api.model.CriterionDto.CRITERION_OPERATOR; +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.VALUE; +import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat; + +class CriterionDtoValidatorTest { + + private final Validator validator = CriterionDtoValidator.instance(); + + @Test + void shouldSucceed_whenObjectIsValid() { + var input = Json.createObjectBuilder() + .add(CRITERION_OPERAND_LEFT, value("operand left")) + .add(CRITERION_OPERATOR, value("=")) + .add(CRITERION_OPERAND_RIGHT, value("operand right")) + .build(); + + var result = validator.validate(input); + + assertThat(result).isSucceeded(); + } + + @Test + void shouldFail_whenMandatoryFieldsAreMissing() { + var input = Json.createObjectBuilder() + .build(); + + var result = validator.validate(input); + + assertThat(result).isFailed().extracting(ValidationFailure::getViolations).asInstanceOf(list(Violation.class)) + .hasSize(2) + .anySatisfy(violation -> assertThat(violation.path()).isEqualTo(CRITERION_OPERAND_LEFT)) + .anySatisfy(violation -> assertThat(violation.path()).isEqualTo(CRITERION_OPERATOR)); + } + + @Test + void shouldFail_whenOperandRightHasMultipleValuesAndOperatorIsNotIn() { + var input = Json.createObjectBuilder() + .add(CRITERION_OPERAND_LEFT, value("operand left")) + .add(CRITERION_OPERATOR, value("=")) + .add(CRITERION_OPERAND_RIGHT, createArrayBuilder() + .add(createObjectBuilder().add(VALUE, "value1")) + .add(createObjectBuilder().add(VALUE, "value2")) + ) + .build(); + + var result = validator.validate(input); + + assertThat(result).isFailed().extracting(ValidationFailure::getViolations).asInstanceOf(list(Violation.class)) + .hasSize(1) + .anySatisfy(violation -> assertThat(violation.path()).isEqualTo(CRITERION_OPERAND_RIGHT)); + } + + private JsonArrayBuilder value(String value) { + return createArrayBuilder().add(createObjectBuilder().add(VALUE, value)); + } +} diff --git a/extensions/common/api/api-core/src/test/java/org/eclipse/edc/api/validation/QuerySpecDtoValidatorTest.java b/extensions/common/api/api-core/src/test/java/org/eclipse/edc/api/validation/QuerySpecDtoValidatorTest.java new file mode 100644 index 00000000000..1e8e59774c9 --- /dev/null +++ b/extensions/common/api/api-core/src/test/java/org/eclipse/edc/api/validation/QuerySpecDtoValidatorTest.java @@ -0,0 +1,134 @@ +/* + * 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.Json; +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.InstanceOfAssertFactories.list; +import static org.eclipse.edc.api.model.QuerySpecDto.EDC_QUERY_SPEC_FILTER_EXPRESSION; +import static org.eclipse.edc.api.model.QuerySpecDto.EDC_QUERY_SPEC_LIMIT; +import static org.eclipse.edc.api.model.QuerySpecDto.EDC_QUERY_SPEC_OFFSET; +import static org.eclipse.edc.api.model.QuerySpecDto.EDC_QUERY_SPEC_SORT_FIELD; +import static org.eclipse.edc.api.model.QuerySpecDto.EDC_QUERY_SPEC_SORT_ORDER; +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.VALUE; +import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat; + +class QuerySpecDtoValidatorTest { + + private final Validator validator = QuerySpecDtoValidator.instance(); + + @Test + void shouldSucceed_whenObjectIsValid() { + var input = Json.createObjectBuilder() + .build(); + + var result = validator.validate(input); + + assertThat(result).isSucceeded(); + } + + @Test + void shouldFail_whenOffsetIsNegative() { + var input = Json.createObjectBuilder() + .add(EDC_QUERY_SPEC_OFFSET, value(-1)) + .build(); + + var result = validator.validate(input); + + assertThat(result).isFailed().extracting(ValidationFailure::getViolations).asInstanceOf(list(Violation.class)) + .filteredOn(v -> v.path().equals(EDC_QUERY_SPEC_OFFSET)) + .hasSize(1) + .first() + .extracting(Violation::message) + .asString().contains("greater"); + } + + @Test + void shouldFail_whenLimitIsLessThanOne() { + var input = Json.createObjectBuilder() + .add(EDC_QUERY_SPEC_LIMIT, value(0)) + .build(); + + var result = validator.validate(input); + + assertThat(result).isFailed().extracting(ValidationFailure::getViolations).asInstanceOf(list(Violation.class)) + .filteredOn(v -> v.path().equals(EDC_QUERY_SPEC_LIMIT)) + .hasSize(1) + .first() + .extracting(Violation::message) + .asString().contains("greater"); + } + + @Test + void shouldFail_whenSortOrderNotAscOrDesc() { + var input = Json.createObjectBuilder() + .add(EDC_QUERY_SPEC_SORT_ORDER, value("any")) + .build(); + + var result = validator.validate(input); + + assertThat(result).isFailed().extracting(ValidationFailure::getViolations).asInstanceOf(list(Violation.class)) + .filteredOn(v -> v.path().equals(EDC_QUERY_SPEC_SORT_ORDER)) + .hasSize(1) + .first() + .extracting(Violation::message) + .asString().contains("one of"); + } + + @Test + void shouldFail_whenSortFieldIsBlank() { + var input = Json.createObjectBuilder() + .add(EDC_QUERY_SPEC_SORT_FIELD, value(" ")) + .build(); + + var result = validator.validate(input); + + assertThat(result).isFailed().extracting(ValidationFailure::getViolations).asInstanceOf(list(Violation.class)) + .filteredOn(v -> v.path().equals(EDC_QUERY_SPEC_SORT_FIELD)) + .hasSize(1) + .first() + .extracting(Violation::message) + .asString().contains("blank"); + } + + @Test + void shouldFail_whenFilterExpressionNotValid() { + var input = Json.createObjectBuilder() + .add(EDC_QUERY_SPEC_FILTER_EXPRESSION, createArrayBuilder().add(createObjectBuilder().add("a", "a"))) + .build(); + + var result = validator.validate(input); + + assertThat(result).isFailed().extracting(ValidationFailure::getViolations).asInstanceOf(list(Violation.class)) + .filteredOn(v -> v.path().startsWith(EDC_QUERY_SPEC_FILTER_EXPRESSION)) + .hasSizeGreaterThan(0); + } + + private JsonArrayBuilder value(int value) { + return createArrayBuilder().add(createObjectBuilder().add(VALUE, value)); + } + + private JsonArrayBuilder value(String value) { + return createArrayBuilder().add(createObjectBuilder().add(VALUE, value)); + } +} diff --git a/extensions/control-plane/api/management-api/catalog-api/build.gradle.kts b/extensions/control-plane/api/management-api/catalog-api/build.gradle.kts index 5107e3bfe41..4466436546c 100644 --- a/extensions/control-plane/api/management-api/catalog-api/build.gradle.kts +++ b/extensions/control-plane/api/management-api/catalog-api/build.gradle.kts @@ -21,6 +21,7 @@ dependencies { api(project(":spi:control-plane:control-plane-spi")) implementation(project(":extensions:common:api:api-core")) implementation(project(":extensions:common:api:management-api-configuration")) + implementation(project(":core:common:validator-core")) implementation(libs.jakarta.rsApi) diff --git a/extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/api/management/catalog/CatalogApi.java b/extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/api/management/catalog/CatalogApi.java index 38712b1bf46..4358c4d73e2 100644 --- a/extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/api/management/catalog/CatalogApi.java +++ b/extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/api/management/catalog/CatalogApi.java @@ -22,8 +22,6 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.json.JsonObject; -import jakarta.validation.Valid; -import jakarta.validation.constraints.NotNull; import jakarta.ws.rs.container.AsyncResponse; import jakarta.ws.rs.container.Suspended; import org.eclipse.edc.catalog.spi.Catalog; @@ -42,5 +40,5 @@ public interface CatalogApi { ), description = "Gets contract offers (=catalog) of a single connector") } ) - void requestCatalog(@Valid @NotNull JsonObject requestDto, @Suspended AsyncResponse response); + void requestCatalog(JsonObject requestDto, @Suspended AsyncResponse response); } diff --git a/extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/api/management/catalog/CatalogApiController.java b/extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/api/management/catalog/CatalogApiController.java index 668eb90fe66..ea7f7d5a9a5 100644 --- a/extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/api/management/catalog/CatalogApiController.java +++ b/extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/api/management/catalog/CatalogApiController.java @@ -26,10 +26,13 @@ import org.eclipse.edc.connector.spi.catalog.CatalogService; import org.eclipse.edc.spi.EdcException; import org.eclipse.edc.transform.spi.TypeTransformerRegistry; +import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry; import org.eclipse.edc.web.spi.exception.BadGatewayException; import org.eclipse.edc.web.spi.exception.InvalidRequestException; +import org.eclipse.edc.web.spi.exception.ValidationFailureException; import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; +import static org.eclipse.edc.catalog.spi.CatalogRequest.EDC_CATALOG_REQUEST_TYPE; @Path("/v2/catalog") @Consumes(APPLICATION_JSON) @@ -38,16 +41,21 @@ public class CatalogApiController implements CatalogApi { private final CatalogService service; private final TypeTransformerRegistry transformerRegistry; + private final JsonObjectValidatorRegistry validatorRegistry; - public CatalogApiController(CatalogService service, TypeTransformerRegistry transformerRegistry) { + public CatalogApiController(CatalogService service, TypeTransformerRegistry transformerRegistry, + JsonObjectValidatorRegistry validatorRegistry) { this.service = service; this.transformerRegistry = transformerRegistry; + this.validatorRegistry = validatorRegistry; } @Override @POST @Path("/request") public void requestCatalog(JsonObject requestBody, @Suspended AsyncResponse response) { + validatorRegistry.validate(EDC_CATALOG_REQUEST_TYPE, requestBody).orElseThrow(ValidationFailureException::new); + var request = transformerRegistry.transform(requestBody, CatalogRequestDto.class) .compose(dto -> transformerRegistry.transform(dto, CatalogRequest.class)) .orElseThrow(InvalidRequestException::new); diff --git a/extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/api/management/catalog/CatalogApiExtension.java b/extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/api/management/catalog/CatalogApiExtension.java index 4a21f13de88..74b22f6035b 100644 --- a/extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/api/management/catalog/CatalogApiExtension.java +++ b/extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/api/management/catalog/CatalogApiExtension.java @@ -14,9 +14,11 @@ package org.eclipse.edc.connector.api.management.catalog; +import org.eclipse.edc.catalog.spi.CatalogRequest; import org.eclipse.edc.connector.api.management.catalog.transform.CatalogRequestDtoToCatalogRequestTransformer; import org.eclipse.edc.connector.api.management.catalog.transform.JsonObjectToCatalogRequestDtoTransformer; import org.eclipse.edc.connector.api.management.catalog.transform.JsonObjectToQuerySpecDtoTransformer; +import org.eclipse.edc.connector.api.management.catalog.validation.CatalogRequestValidator; import org.eclipse.edc.connector.api.management.configuration.ManagementApiConfiguration; import org.eclipse.edc.connector.spi.catalog.CatalogService; import org.eclipse.edc.runtime.metamodel.annotation.Extension; @@ -24,11 +26,14 @@ import org.eclipse.edc.spi.system.ServiceExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; import org.eclipse.edc.transform.spi.TypeTransformerRegistry; +import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry; import org.eclipse.edc.web.spi.WebService; @Extension(value = CatalogApiExtension.NAME) public class CatalogApiExtension implements ServiceExtension { + public static final String NAME = "Management API: Catalog"; + @Inject private WebService webService; @@ -41,6 +46,9 @@ public class CatalogApiExtension implements ServiceExtension { @Inject private CatalogService service; + @Inject + private JsonObjectValidatorRegistry validatorRegistry; + @Override public String name() { return NAME; @@ -52,6 +60,8 @@ public void initialize(ServiceExtensionContext context) { transformerRegistry.register(new JsonObjectToCatalogRequestDtoTransformer()); transformerRegistry.register(new JsonObjectToQuerySpecDtoTransformer()); - webService.registerResource(config.getContextAlias(), new CatalogApiController(service, transformerRegistry)); + webService.registerResource(config.getContextAlias(), new CatalogApiController(service, transformerRegistry, validatorRegistry)); + + validatorRegistry.register(CatalogRequest.EDC_CATALOG_REQUEST_TYPE, CatalogRequestValidator.instance()); } } diff --git a/extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/api/management/catalog/model/CatalogRequestDto.java b/extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/api/management/catalog/model/CatalogRequestDto.java index 3b314ced934..850dc83c0bb 100644 --- a/extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/api/management/catalog/model/CatalogRequestDto.java +++ b/extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/api/management/catalog/model/CatalogRequestDto.java @@ -17,7 +17,6 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; -import jakarta.validation.constraints.NotNull; import org.eclipse.edc.api.model.BaseDto; import org.eclipse.edc.api.model.QuerySpecDto; @@ -27,7 +26,6 @@ public class CatalogRequestDto extends BaseDto { private QuerySpecDto querySpec; - @NotNull private String providerUrl; private String protocol; diff --git a/extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/api/management/catalog/validation/CatalogRequestValidator.java b/extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/api/management/catalog/validation/CatalogRequestValidator.java new file mode 100644 index 00000000000..e10c9ba66b1 --- /dev/null +++ b/extensions/control-plane/api/management-api/catalog-api/src/main/java/org/eclipse/edc/connector/api/management/catalog/validation/CatalogRequestValidator.java @@ -0,0 +1,36 @@ +/* + * 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.api.management.catalog.validation; + +import jakarta.json.JsonObject; +import org.eclipse.edc.api.validation.QuerySpecDtoValidator; +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.catalog.spi.CatalogRequest.EDC_CATALOG_REQUEST_PROTOCOL; +import static org.eclipse.edc.catalog.spi.CatalogRequest.EDC_CATALOG_REQUEST_PROVIDER_URL; +import static org.eclipse.edc.catalog.spi.CatalogRequest.EDC_CATALOG_REQUEST_QUERY_SPEC; + +public class CatalogRequestValidator { + + public static Validator instance() { + return JsonObjectValidator.newValidator() + .verify(EDC_CATALOG_REQUEST_PROVIDER_URL, MandatoryValue::new) + .verify(EDC_CATALOG_REQUEST_PROTOCOL, MandatoryValue::new) + .verifyObject(EDC_CATALOG_REQUEST_QUERY_SPEC, QuerySpecDtoValidator::instance) + .build(); + } +} diff --git a/extensions/control-plane/api/management-api/catalog-api/src/test/java/org/eclipse/edc/connector/api/management/catalog/CatalogApiControllerTest.java b/extensions/control-plane/api/management-api/catalog-api/src/test/java/org/eclipse/edc/connector/api/management/catalog/CatalogApiControllerTest.java index c51672ec452..25912ac8a5d 100644 --- a/extensions/control-plane/api/management-api/catalog-api/src/test/java/org/eclipse/edc/connector/api/management/catalog/CatalogApiControllerTest.java +++ b/extensions/control-plane/api/management-api/catalog-api/src/test/java/org/eclipse/edc/connector/api/management/catalog/CatalogApiControllerTest.java @@ -24,6 +24,9 @@ import org.eclipse.edc.spi.response.StatusResult; import org.eclipse.edc.spi.result.Result; import org.eclipse.edc.transform.spi.TypeTransformerRegistry; +import org.eclipse.edc.validator.spi.JsonObjectValidatorRegistry; +import org.eclipse.edc.validator.spi.ValidationResult; +import org.eclipse.edc.validator.spi.Violation; import org.eclipse.edc.web.jersey.testfixtures.RestControllerTestBase; import org.junit.jupiter.api.Test; @@ -44,18 +47,20 @@ @ApiTest class CatalogApiControllerTest extends RestControllerTestBase { - private final CatalogService service = mock(CatalogService.class); - private final TypeTransformerRegistry transformerRegistry = mock(TypeTransformerRegistry.class); + private final CatalogService service = mock(); + private final TypeTransformerRegistry transformerRegistry = mock(); + private final JsonObjectValidatorRegistry validatorRegistry = mock(); @Override protected Object controller() { - return new CatalogApiController(service, transformerRegistry); + return new CatalogApiController(service, transformerRegistry, validatorRegistry); } @Test void requestCatalog() { var dto = CatalogRequestDto.Builder.newInstance().providerUrl("http://url").build(); var request = CatalogRequest.Builder.newInstance().providerUrl("http://url").build(); + when(validatorRegistry.validate(any(), any())).thenReturn(ValidationResult.success()); when(transformerRegistry.transform(any(), eq(CatalogRequestDto.class))).thenReturn(Result.success(dto)); when(transformerRegistry.transform(any(), eq(CatalogRequest.class))).thenReturn(Result.success(request)); when(service.request(any(), any(), any())).thenReturn(completedFuture(StatusResult.success("{}".getBytes()))); @@ -85,8 +90,8 @@ void requestCatalog() { } @Test - void catalogRequest_shouldReturnBadRequest_whenTransformFails() { - when(transformerRegistry.transform(any(), eq(CatalogRequestDto.class))).thenReturn(Result.failure("error")); + void catalogRequest_shouldReturnBadRequest_whenValidationFails() { + when(validatorRegistry.validate(any(), any())).thenReturn(ValidationResult.failure(Violation.violation("error", "path"))); var requestDto = CatalogRequestDto.Builder.newInstance() .protocol("protocol") @@ -107,23 +112,42 @@ void catalogRequest_shouldReturnBadRequest_whenTransformFails() { .post("/v2/catalog/request") .then() .statusCode(400); - verifyNoInteractions(service); + verify(validatorRegistry).validate(eq(CatalogRequest.EDC_CATALOG_REQUEST_TYPE), any()); + verifyNoInteractions(transformerRegistry, service); } @Test - void requestCatalog_shouldReturnBadRequest_whenNoBody() { + void catalogRequest_shouldReturnBadRequest_whenTransformFails() { + when(validatorRegistry.validate(any(), any())).thenReturn(ValidationResult.success()); + when(transformerRegistry.transform(any(), eq(CatalogRequestDto.class))).thenReturn(Result.failure("error")); + + var requestDto = CatalogRequestDto.Builder.newInstance() + .protocol("protocol") + .querySpec(QuerySpecDto.Builder.newInstance() + .limit(29) + .offset(13) + .filterExpression(List.of(TestFunctions.createCriterionDto("fooProp", "", "bar"), TestFunctions.createCriterionDto("bazProp", "in", List.of("blip", "blup", "blop")))) + .sortField("someField") + .sortOrder(SortOrder.DESC).build()) + .providerUrl("some.provider.url") + + .build(); + given() .port(port) .contentType(JSON) + .body(requestDto) .post("/v2/catalog/request") .then() .statusCode(400); + verifyNoInteractions(service); } @Test void requestCatalog_shouldReturnBadGateway_whenServiceFails() { var dto = CatalogRequestDto.Builder.newInstance().providerUrl("http://url").build(); var request = CatalogRequest.Builder.newInstance().providerUrl("http://url").build(); + when(validatorRegistry.validate(any(), any())).thenReturn(ValidationResult.success()); when(transformerRegistry.transform(any(), eq(CatalogRequestDto.class))).thenReturn(Result.success(dto)); when(transformerRegistry.transform(any(), eq(CatalogRequest.class))).thenReturn(Result.success(request)); when(service.request(any(), any(), any())).thenReturn(completedFuture(StatusResult.failure(FATAL_ERROR, "error"))); @@ -153,6 +177,7 @@ void requestCatalog_shouldReturnBadGateway_whenServiceFails() { void requestCatalog_shouldReturnBadGateway_whenServiceThrowsException() { var dto = CatalogRequestDto.Builder.newInstance().providerUrl("http://url").build(); var request = CatalogRequest.Builder.newInstance().providerUrl("http://url").build(); + when(validatorRegistry.validate(any(), any())).thenReturn(ValidationResult.success()); when(transformerRegistry.transform(any(), eq(CatalogRequestDto.class))).thenReturn(Result.success(dto)); when(transformerRegistry.transform(any(), eq(CatalogRequest.class))).thenReturn(Result.success(request)); when(service.request(any(), any(), any())).thenReturn(failedFuture(new EdcException("error"))); diff --git a/extensions/control-plane/api/management-api/catalog-api/src/test/java/org/eclipse/edc/connector/api/management/catalog/CatalogApiExtensionTest.java b/extensions/control-plane/api/management-api/catalog-api/src/test/java/org/eclipse/edc/connector/api/management/catalog/CatalogApiExtensionTest.java new file mode 100644 index 00000000000..7a02d31a2c1 --- /dev/null +++ b/extensions/control-plane/api/management-api/catalog-api/src/test/java/org/eclipse/edc/connector/api/management/catalog/CatalogApiExtensionTest.java @@ -0,0 +1,46 @@ +/* + * 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.api.management.catalog; + +import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +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.catalog.spi.CatalogRequest.EDC_CATALOG_REQUEST_TYPE; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +@ExtendWith(DependencyInjectionExtension.class) +class CatalogApiExtensionTest { + + private final JsonObjectValidatorRegistry validatorRegistry = mock(); + + @BeforeEach + void setUp(ServiceExtensionContext context) { + context.registerService(JsonObjectValidatorRegistry.class, validatorRegistry); + } + + @Test + void initiate_shouldRegisterValidator(CatalogApiExtension extension, ServiceExtensionContext context) { + extension.initialize(context); + + verify(validatorRegistry).register(eq(EDC_CATALOG_REQUEST_TYPE), any()); + } +} diff --git a/extensions/control-plane/api/management-api/catalog-api/src/test/java/org/eclipse/edc/connector/api/management/catalog/validation/CatalogRequestValidatorTest.java b/extensions/control-plane/api/management-api/catalog-api/src/test/java/org/eclipse/edc/connector/api/management/catalog/validation/CatalogRequestValidatorTest.java new file mode 100644 index 00000000000..fa0f2616957 --- /dev/null +++ b/extensions/control-plane/api/management-api/catalog-api/src/test/java/org/eclipse/edc/connector/api/management/catalog/validation/CatalogRequestValidatorTest.java @@ -0,0 +1,83 @@ +/* + * 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.api.management.catalog.validation; + +import jakarta.json.Json; +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.api.model.QuerySpecDto.EDC_QUERY_SPEC_SORT_FIELD; +import static org.eclipse.edc.catalog.spi.CatalogRequest.EDC_CATALOG_REQUEST_PROTOCOL; +import static org.eclipse.edc.catalog.spi.CatalogRequest.EDC_CATALOG_REQUEST_PROVIDER_URL; +import static org.eclipse.edc.catalog.spi.CatalogRequest.EDC_CATALOG_REQUEST_QUERY_SPEC; +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.VALUE; +import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat; + +class CatalogRequestValidatorTest { + + private final Validator validator = CatalogRequestValidator.instance(); + + @Test + void shouldSucceed_whenInputIsValid() { + var input = Json.createObjectBuilder() + .add(EDC_CATALOG_REQUEST_PROVIDER_URL, value("http://any")) + .add(EDC_CATALOG_REQUEST_PROTOCOL, value("protocol")) + .build(); + + var result = validator.validate(input); + + assertThat(result).isSucceeded(); + } + + @Test + void shouldFail_whenMandatoryFieldsAreMissing() { + var input = Json.createObjectBuilder().build(); + + var result = validator.validate(input); + + assertThat(result).isFailed().extracting(ValidationFailure::getViolations).asInstanceOf(list(Violation.class)) + .hasSize(2) + .anySatisfy(v -> assertThat(v.path()).isEqualTo(EDC_CATALOG_REQUEST_PROVIDER_URL)) + .anySatisfy(v -> assertThat(v.path()).isEqualTo(EDC_CATALOG_REQUEST_PROTOCOL)); + } + + @Test + void shouldFail_whenOptionalQuerySpecIsInvalid() { + var input = Json.createObjectBuilder() + .add(EDC_CATALOG_REQUEST_PROVIDER_URL, value("http://any")) + .add(EDC_CATALOG_REQUEST_PROTOCOL, value("protocol")) + .add(EDC_CATALOG_REQUEST_QUERY_SPEC, createArrayBuilder().add(createObjectBuilder() + .add(EDC_QUERY_SPEC_SORT_FIELD, value(" ")))) + .build(); + + var result = validator.validate(input); + + assertThat(result).isFailed().extracting(ValidationFailure::getViolations).asInstanceOf(list(Violation.class)) + .hasSize(1) + .anySatisfy(v -> assertThat(v.path()).startsWith(EDC_CATALOG_REQUEST_QUERY_SPEC)); + } + + private JsonArrayBuilder value(String value) { + return createArrayBuilder().add(createObjectBuilder().add(VALUE, value)); + } +} diff --git a/extensions/control-plane/api/management-api/contract-agreement-api/src/main/java/org/eclipse/edc/connector/api/management/contractagreement/model/ContractAgreementDto.java b/extensions/control-plane/api/management-api/contract-agreement-api/src/main/java/org/eclipse/edc/connector/api/management/contractagreement/model/ContractAgreementDto.java index 1c440d8916c..546f5a53986 100644 --- a/extensions/control-plane/api/management-api/contract-agreement-api/src/main/java/org/eclipse/edc/connector/api/management/contractagreement/model/ContractAgreementDto.java +++ b/extensions/control-plane/api/management-api/contract-agreement-api/src/main/java/org/eclipse/edc/connector/api/management/contractagreement/model/ContractAgreementDto.java @@ -15,8 +15,6 @@ package org.eclipse.edc.connector.api.management.contractagreement.model; import com.fasterxml.jackson.annotation.JsonProperty; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Positive; import org.eclipse.edc.api.model.BaseDto; import org.eclipse.edc.policy.model.Policy; @@ -33,17 +31,11 @@ public class ContractAgreementDto extends BaseDto { public static final String CONTRACT_AGREEMENT_POLICY = EDC_NAMESPACE + "policy"; @JsonProperty(value = ID) - @NotNull(message = "id cannot be null") private String id; - @NotNull(message = "providerId cannot be null") private String providerId; - @NotNull(message = "consumerId cannot be null") private String consumerId; - @Positive(message = "contractSigningDate must be greater than 0") private long contractSigningDate; - @NotNull(message = "assetId Id cannot be null") private String assetId; - @NotNull(message = "policy cannot be null") private Policy policy; public String getId() { @@ -66,7 +58,7 @@ public String getAssetId() { return assetId; } - public @NotNull Policy getPolicy() { + public Policy getPolicy() { return policy; } diff --git a/extensions/control-plane/api/management-api/contract-agreement-api/src/test/java/org/eclipse/edc/connector/api/management/contractagreement/model/ContractAgreementDtoValidationTest.java b/extensions/control-plane/api/management-api/contract-agreement-api/src/test/java/org/eclipse/edc/connector/api/management/contractagreement/model/ContractAgreementDtoValidationTest.java deleted file mode 100644 index 183d80ca17f..00000000000 --- a/extensions/control-plane/api/management-api/contract-agreement-api/src/test/java/org/eclipse/edc/connector/api/management/contractagreement/model/ContractAgreementDtoValidationTest.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2020 - 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 - * - */ - -package org.eclipse.edc.connector.api.management.contractagreement.model; - -import jakarta.validation.Validation; -import jakarta.validation.Validator; -import jakarta.validation.ValidatorFactory; -import org.eclipse.edc.policy.model.Policy; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtensionContext; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.ArgumentsProvider; -import org.junit.jupiter.params.provider.ArgumentsSource; - -import java.time.Instant; -import java.util.stream.Stream; - -import static org.assertj.core.api.Assertions.assertThat; - -class ContractAgreementDtoValidationTest { - - private Validator validator; - - private static long now() { - return Instant.now().toEpochMilli(); - } - - private static long later(int i) { - return Instant.now().plusMillis(i).toEpochMilli(); - } - - @BeforeEach - void setUp() { - try (ValidatorFactory factory = Validation.buildDefaultValidatorFactory()) { - validator = factory.getValidator(); - } - } - - @ParameterizedTest - @ArgumentsSource(InvalidArgsProvider.class) - void validation_invalidProperty(String id, String assetId, String consumerId, String providerId, long signingDate) { - var agreement = ContractAgreementDto.Builder.newInstance() - .assetId(assetId) - .policy(Policy.Builder.newInstance().build()) - .consumerId(consumerId) - .providerId(providerId) - .id(id) - .contractSigningDate(signingDate) - .build(); - - assertThat(validator.validate(agreement)).hasSizeGreaterThan(0); - } - - @Test - void validation_validDto() { - var agreement = ContractAgreementDto.Builder.newInstance() - .assetId("Asset") - .policy(Policy.Builder.newInstance().build()) - .consumerId("cons") - .providerId("prov") - .id("someId") - .contractSigningDate(14) - .build(); - - assertThat(validator.validate(agreement)).isEmpty(); - } - - private static class InvalidArgsProvider implements ArgumentsProvider { - @Override - public Stream provideArguments(ExtensionContext context) throws Exception { - return Stream.of( - Arguments.of(null, "asset", "consumer", "provider", now()), - Arguments.of("id", null, "consumer", "provider", now()), - Arguments.of("id", "asset", null, "provider", now()), - Arguments.of("id", "asset", "consumer", null, now()), - Arguments.of("id", "asset", "consumer", "provider", 0) - ); - } - } -} diff --git a/extensions/control-plane/api/management-api/contract-definition-api/src/main/java/org/eclipse/edc/connector/api/management/contractdefinition/validation/ContractDefinitionRequestDtoValidator.java b/extensions/control-plane/api/management-api/contract-definition-api/src/main/java/org/eclipse/edc/connector/api/management/contractdefinition/validation/ContractDefinitionRequestDtoValidator.java index 4ba84970b4d..4f37381bed5 100644 --- a/extensions/control-plane/api/management-api/contract-definition-api/src/main/java/org/eclipse/edc/connector/api/management/contractdefinition/validation/ContractDefinitionRequestDtoValidator.java +++ b/extensions/control-plane/api/management-api/contract-definition-api/src/main/java/org/eclipse/edc/connector/api/management/contractdefinition/validation/ContractDefinitionRequestDtoValidator.java @@ -15,6 +15,7 @@ package org.eclipse.edc.connector.api.management.contractdefinition.validation; import jakarta.json.JsonString; +import org.eclipse.edc.api.validation.CriterionDtoValidator; import org.eclipse.edc.validator.jsonobject.JsonLdPath; import org.eclipse.edc.validator.jsonobject.JsonObjectValidator; import org.eclipse.edc.validator.jsonobject.validators.MandatoryValue; @@ -23,8 +24,6 @@ import org.eclipse.edc.validator.spi.Validator; import static java.lang.String.format; -import static org.eclipse.edc.api.model.CriterionDto.CRITERION_OPERAND_LEFT; -import static org.eclipse.edc.api.model.CriterionDto.CRITERION_OPERATOR; import static org.eclipse.edc.connector.api.management.contractdefinition.model.ContractDefinitionRequestDto.CONTRACT_DEFINITION_ACCESSPOLICY_ID; import static org.eclipse.edc.connector.api.management.contractdefinition.model.ContractDefinitionRequestDto.CONTRACT_DEFINITION_ASSETS_SELECTOR; import static org.eclipse.edc.connector.api.management.contractdefinition.model.ContractDefinitionRequestDto.CONTRACT_DEFINITION_CONTRACTPOLICY_ID; @@ -38,10 +37,7 @@ public static JsonObjectValidator instance() { .verifyId(IdCannotContainColon::new) .verify(CONTRACT_DEFINITION_ACCESSPOLICY_ID, MandatoryValue::new) .verify(CONTRACT_DEFINITION_CONTRACTPOLICY_ID, MandatoryValue::new) - .verifyArrayItem(CONTRACT_DEFINITION_ASSETS_SELECTOR, v -> v - .verify(CRITERION_OPERAND_LEFT, MandatoryValue::new) - .verify(CRITERION_OPERATOR, MandatoryValue::new) - ) + .verifyArrayItem(CONTRACT_DEFINITION_ASSETS_SELECTOR, CriterionDtoValidator::instance) .build(); } diff --git a/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/api/management/contractnegotiation/model/ContractAgreementDto.java b/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/api/management/contractnegotiation/model/ContractAgreementDto.java index d02c94e50d8..938d76e0769 100644 --- a/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/api/management/contractnegotiation/model/ContractAgreementDto.java +++ b/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/api/management/contractnegotiation/model/ContractAgreementDto.java @@ -14,34 +14,25 @@ package org.eclipse.edc.connector.api.management.contractnegotiation.model; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Positive; import org.eclipse.edc.policy.model.Policy; import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE; public class ContractAgreementDto { public static final String TYPE = EDC_NAMESPACE + "ContractAgreementDto"; - public static final String CONTRACT_AGREEMENT_ASSETID = EDC_NAMESPACE + "assetId"; + public static final String CONTRACT_AGREEMENT_ASSET_ID = EDC_NAMESPACE + "assetId"; public static final String CONTRACT_AGREEMENT_PROVIDER_ID = EDC_NAMESPACE + "providerId"; public static final String CONTRACT_AGREEMENT_CONSUMER_ID = EDC_NAMESPACE + "consumerId"; public static final String CONTRACT_AGREEMENT_SIGNING_DATE = EDC_NAMESPACE + "contractSigningDate"; public static final String CONTRACT_AGREEMENT_POLICY = EDC_NAMESPACE + "policy"; - @NotNull(message = "id cannot be null") private String id; - @NotNull(message = "providerId cannot be null") private String providerId; - @NotNull(message = "consumerId cannot be null") private String consumerId; - @Positive(message = "contractSigningDate must be greater than 0") private long contractSigningDate; - @NotNull(message = "assetId cannot be null") private String assetId; - @NotNull(message = "policy cannot be null") private Policy policy; - public String getId() { return id; } diff --git a/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/api/management/contractnegotiation/transform/JsonObjectFromContractAgreementDtoTransformer.java b/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/api/management/contractnegotiation/transform/JsonObjectFromContractAgreementDtoTransformer.java index 7b571d1fc71..511cd4e9f69 100644 --- a/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/api/management/contractnegotiation/transform/JsonObjectFromContractAgreementDtoTransformer.java +++ b/extensions/control-plane/api/management-api/contract-negotiation-api/src/main/java/org/eclipse/edc/connector/api/management/contractnegotiation/transform/JsonObjectFromContractAgreementDtoTransformer.java @@ -22,7 +22,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import static org.eclipse.edc.connector.api.management.contractnegotiation.model.ContractAgreementDto.CONTRACT_AGREEMENT_ASSETID; +import static org.eclipse.edc.connector.api.management.contractnegotiation.model.ContractAgreementDto.CONTRACT_AGREEMENT_ASSET_ID; import static org.eclipse.edc.connector.api.management.contractnegotiation.model.ContractAgreementDto.CONTRACT_AGREEMENT_CONSUMER_ID; import static org.eclipse.edc.connector.api.management.contractnegotiation.model.ContractAgreementDto.CONTRACT_AGREEMENT_POLICY; import static org.eclipse.edc.connector.api.management.contractnegotiation.model.ContractAgreementDto.CONTRACT_AGREEMENT_PROVIDER_ID; @@ -44,7 +44,7 @@ public JsonObjectFromContractAgreementDtoTransformer(JsonBuilderFactory jsonFact var bldr = jsonFactory.createObjectBuilder(); bldr.add(TYPE, ContractAgreementDto.TYPE) .add(ID, dto.getId()) - .add(CONTRACT_AGREEMENT_ASSETID, dto.getAssetId()) + .add(CONTRACT_AGREEMENT_ASSET_ID, dto.getAssetId()) .add(CONTRACT_AGREEMENT_POLICY, context.transform(dto.getPolicy(), JsonObject.class)) .add(CONTRACT_AGREEMENT_SIGNING_DATE, dto.getContractSigningDate()) .add(CONTRACT_AGREEMENT_CONSUMER_ID, dto.getConsumerId()) diff --git a/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/api/management/contractnegotiation/transform/JsonObjectFromContractAgreementDtoTransformerTest.java b/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/api/management/contractnegotiation/transform/JsonObjectFromContractAgreementDtoTransformerTest.java index 3039ab4d59a..d08b84e7bd1 100644 --- a/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/api/management/contractnegotiation/transform/JsonObjectFromContractAgreementDtoTransformerTest.java +++ b/extensions/control-plane/api/management-api/contract-negotiation-api/src/test/java/org/eclipse/edc/connector/api/management/contractnegotiation/transform/JsonObjectFromContractAgreementDtoTransformerTest.java @@ -26,7 +26,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.eclipse.edc.connector.api.management.contractnegotiation.model.ContractAgreementDto.Builder; -import static org.eclipse.edc.connector.api.management.contractnegotiation.model.ContractAgreementDto.CONTRACT_AGREEMENT_ASSETID; +import static org.eclipse.edc.connector.api.management.contractnegotiation.model.ContractAgreementDto.CONTRACT_AGREEMENT_ASSET_ID; import static org.eclipse.edc.connector.api.management.contractnegotiation.model.ContractAgreementDto.CONTRACT_AGREEMENT_CONSUMER_ID; import static org.eclipse.edc.connector.api.management.contractnegotiation.model.ContractAgreementDto.CONTRACT_AGREEMENT_POLICY; import static org.eclipse.edc.connector.api.management.contractnegotiation.model.ContractAgreementDto.CONTRACT_AGREEMENT_PROVIDER_ID; @@ -58,9 +58,9 @@ void transform() { var jobj = transformer.transform(agreement, context); assertThat(jobj).isNotNull(); - assertThat(jobj.getJsonString(CONTRACT_AGREEMENT_ASSETID)).extracting(JsonString::getString).isEqualTo("test-asset"); + assertThat(jobj.getJsonString(CONTRACT_AGREEMENT_ASSET_ID)).extracting(JsonString::getString).isEqualTo("test-asset"); assertThat(jobj.getJsonString(CONTRACT_AGREEMENT_PROVIDER_ID)).extracting(JsonString::getString).isEqualTo("test-provider"); assertThat(jobj.getJsonString(CONTRACT_AGREEMENT_CONSUMER_ID)).extracting(JsonString::getString).isEqualTo("test-consumer"); assertThat(jobj.getJsonObject(CONTRACT_AGREEMENT_POLICY)).isNotNull(); } -} \ No newline at end of file +} diff --git a/resources/openapi/openapi.yaml b/resources/openapi/openapi.yaml index 68908ef23a5..415ec0e36f3 100644 --- a/resources/openapi/openapi.yaml +++ b/resources/openapi/openapi.yaml @@ -1629,7 +1629,7 @@ components: type: object properties: invalidValue: - type: string + type: object example: null message: type: string @@ -1775,8 +1775,6 @@ components: example: null example: null CatalogRequestDto: - required: - - providerUrl type: object properties: '@context': @@ -1806,12 +1804,6 @@ components: discriminator: propertyName: edctype ContractAgreementDto: - required: - - '@id' - - assetId - - consumerId - - policy - - providerId type: object properties: '@context': @@ -1966,9 +1958,6 @@ components: $ref: '#/components/schemas/Policy' example: null CriterionDto: - required: - - operandLeft - - operator type: object properties: '@context': diff --git a/resources/openapi/yaml/control-api/control-plane-api.yaml b/resources/openapi/yaml/control-api/control-plane-api.yaml index 26ffc150529..3ed6f5b6edc 100644 --- a/resources/openapi/yaml/control-api/control-plane-api.yaml +++ b/resources/openapi/yaml/control-api/control-plane-api.yaml @@ -63,7 +63,7 @@ components: example: null properties: invalidValue: - type: string + type: object example: null message: type: string diff --git a/resources/openapi/yaml/management-api/asset-api.yaml b/resources/openapi/yaml/management-api/asset-api.yaml index f52d939cac2..910ea359cb4 100644 --- a/resources/openapi/yaml/management-api/asset-api.yaml +++ b/resources/openapi/yaml/management-api/asset-api.yaml @@ -271,7 +271,7 @@ components: example: null properties: invalidValue: - type: string + type: object example: null message: type: string @@ -383,9 +383,6 @@ components: operator: type: string example: null - required: - - operandLeft - - operator DataAddress: type: object example: null diff --git a/resources/openapi/yaml/management-api/catalog-api.yaml b/resources/openapi/yaml/management-api/catalog-api.yaml index 64263151d15..05d1702ffd7 100644 --- a/resources/openapi/yaml/management-api/catalog-api.yaml +++ b/resources/openapi/yaml/management-api/catalog-api.yaml @@ -77,8 +77,6 @@ components: example: null querySpec: $ref: '#/components/schemas/QuerySpecDto' - required: - - providerUrl Constraint: type: object discriminator: @@ -124,9 +122,6 @@ components: operator: type: string example: null - required: - - operandLeft - - operator DataService: type: object example: null diff --git a/resources/openapi/yaml/management-api/contract-agreement-api.yaml b/resources/openapi/yaml/management-api/contract-agreement-api.yaml index b56650bec9b..68f0b49b557 100644 --- a/resources/openapi/yaml/management-api/contract-agreement-api.yaml +++ b/resources/openapi/yaml/management-api/contract-agreement-api.yaml @@ -87,7 +87,7 @@ components: example: null properties: invalidValue: - type: string + type: object example: null message: type: string @@ -137,12 +137,6 @@ components: providerId: type: string example: null - required: - - '@id' - - assetId - - consumerId - - policy - - providerId CriterionDto: type: object example: null @@ -162,9 +156,6 @@ components: operator: type: string example: null - required: - - operandLeft - - operator Duty: type: object example: null diff --git a/resources/openapi/yaml/management-api/contract-definition-api.yaml b/resources/openapi/yaml/management-api/contract-definition-api.yaml index 9fef5fd5ac0..d9cc4d96c23 100644 --- a/resources/openapi/yaml/management-api/contract-definition-api.yaml +++ b/resources/openapi/yaml/management-api/contract-definition-api.yaml @@ -181,7 +181,7 @@ components: example: null properties: invalidValue: - type: string + type: object example: null message: type: string @@ -263,9 +263,6 @@ components: operator: type: string example: null - required: - - operandLeft - - operator IdResponseDto: type: object example: null diff --git a/resources/openapi/yaml/management-api/contract-negotiation-api.yaml b/resources/openapi/yaml/management-api/contract-negotiation-api.yaml index f25ac23224b..b863d70d2d8 100644 --- a/resources/openapi/yaml/management-api/contract-negotiation-api.yaml +++ b/resources/openapi/yaml/management-api/contract-negotiation-api.yaml @@ -283,7 +283,7 @@ components: example: null properties: invalidValue: - type: string + type: object example: null message: type: string @@ -350,12 +350,6 @@ components: providerId: type: string example: null - required: - - assetId - - consumerId - - id - - policy - - providerId ContractNegotiationDto: type: object example: null @@ -434,9 +428,6 @@ components: operator: type: string example: null - required: - - operandLeft - - operator Duty: type: object example: null diff --git a/resources/openapi/yaml/management-api/policy-definition-api.yaml b/resources/openapi/yaml/management-api/policy-definition-api.yaml index 793e187f478..ae6a6031f9c 100644 --- a/resources/openapi/yaml/management-api/policy-definition-api.yaml +++ b/resources/openapi/yaml/management-api/policy-definition-api.yaml @@ -210,7 +210,7 @@ components: example: null properties: invalidValue: - type: string + type: object example: null message: type: string @@ -251,9 +251,6 @@ components: operator: type: string example: null - required: - - operandLeft - - operator Duty: type: object example: null diff --git a/resources/openapi/yaml/management-api/transfer-process-api.yaml b/resources/openapi/yaml/management-api/transfer-process-api.yaml index 49bc7176444..e97ff272679 100644 --- a/resources/openapi/yaml/management-api/transfer-process-api.yaml +++ b/resources/openapi/yaml/management-api/transfer-process-api.yaml @@ -245,7 +245,7 @@ components: example: null properties: invalidValue: - type: string + type: object example: null message: type: string @@ -298,9 +298,6 @@ components: operator: type: string example: null - required: - - operandLeft - - operator DataAddress: type: object example: null diff --git a/spi/common/validator-spi/src/main/java/org/eclipse/edc/validator/spi/Violation.java b/spi/common/validator-spi/src/main/java/org/eclipse/edc/validator/spi/Violation.java index 9e181d888d4..80e3ab3aa54 100644 --- a/spi/common/validator-spi/src/main/java/org/eclipse/edc/validator/spi/Violation.java +++ b/spi/common/validator-spi/src/main/java/org/eclipse/edc/validator/spi/Violation.java @@ -21,12 +21,12 @@ * @param path the path in the object. * @param value the actual value. */ -public record Violation(String message, String path, String value) { +public record Violation(String message, String path, Object value) { public static Violation violation(String message, String path) { return new Violation(message, path, null); } - public static Violation violation(String message, String path, String value) { + public static Violation violation(String message, String path, Object value) { return new Violation(message, path, value); } } diff --git a/spi/common/web-spi/src/main/java/org/eclipse/edc/web/spi/ApiErrorDetail.java b/spi/common/web-spi/src/main/java/org/eclipse/edc/web/spi/ApiErrorDetail.java index 41cdc7b1e79..e18a656d811 100644 --- a/spi/common/web-spi/src/main/java/org/eclipse/edc/web/spi/ApiErrorDetail.java +++ b/spi/common/web-spi/src/main/java/org/eclipse/edc/web/spi/ApiErrorDetail.java @@ -28,7 +28,7 @@ public class ApiErrorDetail { @JsonProperty private String path; @JsonProperty - private String invalidValue; + private Object invalidValue; private ApiErrorDetail() { @@ -68,7 +68,7 @@ public String getPath() { * * @return the invalid value */ - public String getInvalidValue() { + public Object getInvalidValue() { return invalidValue; } @@ -98,7 +98,7 @@ public Builder path(String path) { return this; } - public Builder value(String value) { + public Builder value(Object value) { apiError.invalidValue = value; return this; }