Skip to content

Commit

Permalink
feat: implement ContractDefinitionRequestDto JsonLD validator (#3159)
Browse files Browse the repository at this point in the history
feat: contract definition validation
  • Loading branch information
ndr-brt authored Jun 12, 2023
1 parent 9a8722a commit 4b935ca
Show file tree
Hide file tree
Showing 22 changed files with 654 additions and 368 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,20 @@
package org.eclipse.edc.validator.jsonobject;

import jakarta.json.JsonObject;
import jakarta.json.JsonString;
import org.eclipse.edc.validator.spi.ValidationResult;
import org.eclipse.edc.validator.spi.Validator;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.function.UnaryOperator;
import java.util.stream.Stream;

import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.ID;
import static org.eclipse.edc.validator.jsonobject.JsonLdPath.path;
import static org.eclipse.edc.validator.jsonobject.JsonWalkers.ARRAY_ITEMS;
import static org.eclipse.edc.validator.jsonobject.JsonWalkers.NESTED_OBJECT;
import static org.eclipse.edc.validator.jsonobject.JsonWalkers.ROOT_OBJECT;

/**
* The {@link JsonObject} {@link Validator} implementation.
Expand All @@ -34,24 +38,20 @@ public class JsonObjectValidator implements Validator<JsonObject> {

private final List<Validator<JsonObject>> validators = new ArrayList<>();
private final JsonLdPath path;
private final Navigator navigator;
private final JsonWalker walker;

public static JsonObjectValidator newValidator() {
return new JsonObjectValidator();
public static JsonObjectValidator.Builder newValidator() {
return JsonObjectValidator.Builder.newInstance(path(), ROOT_OBJECT);
}

private JsonObjectValidator() {
this(path(), new RootObjectNavigator());
}

protected JsonObjectValidator(JsonLdPath path, Navigator navigator) {
protected JsonObjectValidator(JsonLdPath path, JsonWalker walker) {
this.path = path;
this.navigator = navigator;
this.walker = walker;
}

@Override
public ValidationResult validate(JsonObject input) {
var violations = navigator.extract(input, path)
var violations = walker.extract(input, path)
.flatMap(target -> this.validators.stream().map(validator -> validator.validate(target)))
.filter(ValidationResult::failed)
.flatMap(it -> it.getFailure().getViolations().stream())
Expand All @@ -64,47 +64,51 @@ public ValidationResult validate(JsonObject input) {
}
}

public JsonObjectValidator verify(Function<JsonLdPath, Validator<JsonObject>> validatorProvider) {
var validator = validatorProvider.apply(path);
this.validators.add(validator);
return this;
}
public static class Builder {

public JsonObjectValidator verify(String fieldName, Function<JsonLdPath, Validator<JsonObject>> validatorProvider) {
var newPath = path.append(fieldName);
var validator = validatorProvider.apply(newPath);
this.validators.add(validator);
return this;
}
private final JsonObjectValidator validator;

public JsonObjectValidator verifyObject(String fieldName, UnaryOperator<JsonObjectValidator> provider) {
var newPath = path.append(fieldName);
this.validators.add(provider.apply(new JsonObjectValidator(newPath, new NestedObjectNavigator())));
return this;
}
private Builder(JsonObjectValidator validator) {
this.validator = validator;
}

private static class RootObjectNavigator implements Navigator {
public static Builder newInstance(JsonLdPath path, JsonWalker walker) {
return new Builder(new JsonObjectValidator(path, walker));
}

@Override
public Stream<JsonObject> extract(JsonObject object, JsonLdPath path) {
return Stream.of(object);
public Builder verify(Function<JsonLdPath, Validator<JsonObject>> validatorProvider) {
validator.validators.add(validatorProvider.apply(validator.path));
return this;
}
}

private static class NestedObjectNavigator implements Navigator {
@Override
public Stream<JsonObject> extract(JsonObject o, JsonLdPath p) {
var array = o.getJsonArray(p.last());
public Builder verify(String fieldName, Function<JsonLdPath, Validator<JsonObject>> validatorProvider) {
var newPath = validator.path.append(fieldName);
validator.validators.add(validatorProvider.apply(newPath));
return this;
}

if (array == null) {
return Stream.empty();
} else {
return Stream.of(array.getJsonObject(0));
}
public Builder verifyId(Function<JsonLdPath, Validator<JsonString>> idValidatorProvider) {
var newPath = validator.path.append(ID);
validator.validators.add(input -> idValidatorProvider.apply(newPath).validate(input.getJsonString(ID)));
return this;
}

public Builder verifyObject(String fieldName, UnaryOperator<JsonObjectValidator.Builder> provider) {
var newPath = validator.path.append(fieldName);
var builder = JsonObjectValidator.Builder.newInstance(newPath, NESTED_OBJECT);
validator.validators.add(provider.apply(builder).build());
return this;
}
}

interface Navigator {
Stream<JsonObject> extract(JsonObject object, JsonLdPath path);
public Builder verifyArrayItem(String fieldName, UnaryOperator<JsonObjectValidator.Builder> provider) {
var newPath = validator.path.append(fieldName);
var builder = JsonObjectValidator.Builder.newInstance(newPath, ARRAY_ITEMS);
validator.validators.add(provider.apply(builder).build());
return this;
}

public JsonObjectValidator build() {
return validator;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* 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.jsonobject;

import jakarta.json.JsonObject;

import java.util.stream.Stream;

/**
* Extract objects from JsonObject sub-path.
*/
interface JsonWalker {

/**
* Extract a {@link Stream} of {@link JsonObject} from the path passed that can then be validated.
*
* @param object the {@link JsonObject}.
* @param path the {@link JsonLdPath}.
* @return a {@link Stream} of {@link JsonObject} can never be null.
*/
Stream<JsonObject> extract(JsonObject object, JsonLdPath path);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* 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.jsonobject;

import jakarta.json.JsonObject;
import jakarta.json.JsonValue;

import java.util.stream.Stream;

public enum JsonWalkers implements JsonWalker {

ROOT_OBJECT {
@Override
public Stream<JsonObject> extract(JsonObject object, JsonLdPath path) {
return Stream.of(object);
}
},

NESTED_OBJECT {
@Override
public Stream<JsonObject> extract(JsonObject object, JsonLdPath path) {
var array = object.getJsonArray(path.last());

if (array == null) {
return Stream.empty();
} else {
return Stream.of(array.getJsonObject(0));
}
}
},

ARRAY_ITEMS {
@Override
public Stream<JsonObject> extract(JsonObject object, JsonLdPath path) {
var array = object.getJsonArray(path.last());

if (array == null) {
return Stream.empty();
} else {
return array.stream().map(JsonValue::asJsonObject);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,32 +12,35 @@
*
*/

package org.eclipse.edc.validator.jsonobject;
package org.eclipse.edc.validator.jsonobject.validators;

import jakarta.json.JsonObject;
import org.eclipse.edc.validator.jsonobject.JsonLdPath;
import org.eclipse.edc.validator.spi.ValidationResult;
import org.eclipse.edc.validator.spi.Validator;

import java.util.Optional;

import static java.lang.String.format;
import static org.eclipse.edc.validator.spi.Violation.violation;

/**
* Verify that a specific object exists
* Verifies that a single object is present.
*/
public class MandatoryField implements Validator<JsonObject> {
public class MandatoryObject implements Validator<JsonObject> {

private final JsonLdPath path;

public MandatoryField(JsonLdPath path) {
public MandatoryObject(JsonLdPath path) {
this.path = path;
}

@Override
public ValidationResult validate(JsonObject input) {
if (input.containsKey(path.last())) {
return ValidationResult.success();
} else {
return ValidationResult.failure(violation(format("mandatory object '%s' is missing", path), path.toString()));
}
return Optional.ofNullable(input.getJsonArray(path.last()))
.filter(it -> it.size() > 0)
.map(it -> it.getJsonObject(0))
.map(it -> ValidationResult.success())
.orElseGet(() -> ValidationResult.failure(violation(format("mandatory object '%s' is missing", path), path.toString())));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* 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.jsonobject.validators;

import jakarta.json.JsonObject;
import org.eclipse.edc.validator.jsonobject.JsonLdPath;
import org.eclipse.edc.validator.spi.ValidationResult;
import org.eclipse.edc.validator.spi.Validator;

import java.util.Optional;

import static java.lang.String.format;
import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.VALUE;
import static org.eclipse.edc.validator.spi.Violation.violation;

/**
* Verifies that a @value is present and not blank.
*/
public class MandatoryValue implements Validator<JsonObject> {
private final JsonLdPath path;

public MandatoryValue(JsonLdPath path) {
this.path = path;
}

@Override
public ValidationResult validate(JsonObject input) {
return Optional.ofNullable(input.getJsonArray(path.last()))
.map(it -> it.getJsonObject(0))
.map(it -> it.getString(VALUE))
.filter(it -> !it.isBlank())
.map(it -> ValidationResult.success())
.orElseGet(() -> ValidationResult.failure(violation(format("mandatory value '%s' is missing or it is blank", path), path.toString())));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,20 @@
*
*/

package org.eclipse.edc.validator.jsonobject;
package org.eclipse.edc.validator.jsonobject.validators;

import jakarta.json.JsonObject;
import jakarta.json.JsonString;
import org.eclipse.edc.validator.jsonobject.JsonLdPath;
import org.eclipse.edc.validator.spi.ValidationResult;
import org.eclipse.edc.validator.spi.Validator;

import static java.lang.String.format;
import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.ID;
import static org.eclipse.edc.validator.spi.Violation.violation;

/**
* Verify that the @id field is not blank. It can be null, though.
*/
public class OptionalIdNotBlank implements Validator<JsonObject> {
public class OptionalIdNotBlank implements Validator<JsonString> {

private final JsonLdPath path;

Expand All @@ -34,9 +34,9 @@ public OptionalIdNotBlank(JsonLdPath path) {
}

@Override
public ValidationResult validate(JsonObject input) {
if (input.containsKey(ID) && input.getString(ID).isBlank()) {
return ValidationResult.failure(violation(format("%s optional %s cannot be blank", path, ID), path.toString()));
public ValidationResult validate(JsonString id) {
if (id != null && id.getString().isBlank()) {
return ValidationResult.failure(violation(format("%s cannot be blank", path), path.toString()));
} else {
return ValidationResult.success();
}
Expand Down
Loading

0 comments on commit 4b935ca

Please sign in to comment.