All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.belgif.rest.problem.api.InputValidationIssues Maven / Gradle / Ivy

package io.github.belgif.rest.problem.api;

import java.net.URI;
import java.time.temporal.Temporal;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Helper class for creating InputValidationIssues for common request validations.
 *
 * @see InputValidationIssue
 */
public class InputValidationIssues {

    // Belgif input-validation issue types

    public static final URI ISSUE_TYPE_SCHEMA_VIOLATION =
            URI.create("urn:problem-type:belgif:input-validation:schemaViolation");
    public static final URI ISSUE_TYPE_UNKNOWN_INPUT =
            URI.create("urn:problem-type:belgif:input-validation:unknownInput");

    // CBSS input-validation issue types for Belgif openapi types
    // TODO: standardize @ Belgif: https://github.com/belgif/rest-guide/issues/126

    public static final URI ISSUE_TYPE_INVALID_STRUCTURE =
            URI.create("urn:problem-type:cbss:input-validation:invalidStructure");
    public static final URI ISSUE_TYPE_OUT_OF_RANGE =
            URI.create("urn:problem-type:cbss:input-validation:outOfRange");
    public static final URI ISSUE_TYPE_REFERENCED_RESOURCE_NOT_FOUND =
            URI.create("urn:problem-type:cbss:input-validation:referencedResourceNotFound");
    public static final URI ISSUE_TYPE_REJECTED_INPUT =
            URI.create("urn:problem-type:cbss:input-validation:rejectedInput");
    public static final URI ISSUE_TYPE_REQUIRED_INPUT =
            URI.create("urn:problem-type:cbss:input-validation:requiredInput");

    public static final URI ISSUE_TYPE_REPLACED_SSIN =
            URI.create("urn:problem-type:cbss:input-validation:replacedSsin");
    public static final URI ISSUE_TYPE_CANCELED_SSIN =
            URI.create("urn:problem-type:cbss:input-validation:canceledSsin");
    public static final URI ISSUE_TYPE_INVALID_PERIOD =
            URI.create("urn:problem-type:cbss:input-validation:invalidPeriod");

    // CBSS input-validation issue types for cross-parameter validation
    // TODO: standardize @ Belgif: https://github.com/belgif/rest-guide/issues/113

    public static final URI ISSUE_TYPE_EXACTLY_ONE_OF_EXPECTED =
            URI.create("urn:problem-type:cbss:input-validation:exactlyOneOfExpected");
    public static final URI ISSUE_TYPE_ANY_OF_EXPECTED =
            URI.create("urn:problem-type:cbss:input-validation:anyOfExpected");
    public static final URI ISSUE_TYPE_ZERO_OR_EXACTLY_ONE_OF_EXPECTED =
            URI.create("urn:problem-type:cbss:input-validation:zeroOrExactlyOneOfExpected");
    public static final URI ISSUE_TYPE_ZERO_OR_ALL_OF_EXPECTED =
            URI.create("urn:problem-type:cbss:input-validation:zeroOrAllOfExpected");
    public static final URI ISSUE_TYPE_EQUAL_EXPECTED =
            URI.create("urn:problem-type:cbss:input-validation:equalExpected");

    private InputValidationIssues() {
    }

    public static InputValidationIssue schemaViolation(InEnum in, String name, Object value, String detail) {
        return new InputValidationIssue(ISSUE_TYPE_SCHEMA_VIOLATION,
                "Input value is invalid with respect to the schema")
                        .detail(detail)
                        .in(in, name, value);
    }

    public static InputValidationIssue unknownInput(InEnum in, String name, Object value) {
        return new InputValidationIssue(ISSUE_TYPE_UNKNOWN_INPUT, "Unknown input")
                .detail(String.format("Input %s is unknown", name))
                .in(in, name, value);
    }

    public static InputValidationIssue invalidStructure(InEnum in, String name, Object value, String detail) {
        return new InputValidationIssue(ISSUE_TYPE_INVALID_STRUCTURE,
                "Input value has invalid structure")
                        .detail(detail)
                        .in(in, name, value);
    }

    public static > InputValidationIssue outOfRange(InEnum in, String name, T value, T min,
            T max) {
        if (min == null && max == null) {
            throw new IllegalArgumentException("At least one of min, max must be non-null");
        }
        InputValidationIssue issue =
                new InputValidationIssue(ISSUE_TYPE_OUT_OF_RANGE, "Input value is out of range")
                        .in(in, name, value);
        if (min != null && max != null) {
            issue.detail(String.format("Input value %s = %s is out of range [%s, %s]", name, value, min, max))
                    .additionalProperty("minimum", min.toString())
                    .additionalProperty("maximum", max.toString());
        } else if (min != null) {
            issue.detail(String.format("Input value %s = %s should be at least %s", name, value, min))
                    .additionalProperty("minimum", min.toString());
        } else {
            issue.detail(String.format("Input value %s = %s should not exceed %s", name, value, max))
                    .additionalProperty("maximum", max.toString());
        }
        return issue;
    }

    public static InputValidationIssue referencedResourceNotFound(InEnum in, String name, Object value) {
        return new InputValidationIssue(ISSUE_TYPE_REFERENCED_RESOURCE_NOT_FOUND, "Referenced resource not found")
                .detail(String.format("Referenced resource %s = '%s' does not exist", name, value))
                .in(in, name, value);
    }

    public static InputValidationIssue rejectedInput(InEnum in, String name, Object value) {
        return new InputValidationIssue(ISSUE_TYPE_REJECTED_INPUT, "Input is not allowed in this context")
                .detail(String.format("Input %s is not allowed in this context", name))
                .in(in, name, value);
    }

    public static InputValidationIssue requiredInput(InEnum in, String name) {
        return new InputValidationIssue(ISSUE_TYPE_REQUIRED_INPUT, "Input is required in this context")
                .detail(String.format("Input %s is required in this context", name))
                .in(in, name, null);
    }

    public static InputValidationIssue requiredInputsIfPresent(Input target, List> inputs) {
        InputValidationIssue issue =
                new InputValidationIssue(ISSUE_TYPE_REQUIRED_INPUT, "Input is required in this context")
                        .detail(String.format("All of these inputs must be present if %s is present: %s",
                                target.getName(), getJoinedNames(inputs)));
        issue.addInput(target);

        if (inputs != null) {
            inputs.forEach(issue::addInput);
        }

        return issue;
    }

    public static InputValidationIssue replacedSsin(InEnum in, String name, String ssin, String newSsin) {
        return new InputValidationIssue(ISSUE_TYPE_REPLACED_SSIN, "SSIN has been replaced, use new SSIN")
                .detail(String.format("SSIN %s has been replaced by %s", ssin, newSsin))
                .in(in, name, ssin)
                .additionalProperty("replacedBy", newSsin);
    }

    public static InputValidationIssue replacedSsin(InEnum in, String name, String ssin, String newSsin, URI newHref) {
        return replacedSsin(in, name, ssin, newSsin).additionalProperty("replacedByHref", String.valueOf(newHref));
    }

    public static InputValidationIssue canceledSsin(InEnum in, String name, String ssin) {
        return new InputValidationIssue(ISSUE_TYPE_CANCELED_SSIN, "SSIN has been canceled")
                .detail(String.format("SSIN %s has been canceled", ssin))
                .in(in, name, ssin);
    }

    public static InputValidationIssue invalidSsin(InEnum in, String name, String ssin) {
        return invalidStructure(in, name, ssin, String.format("SSIN %s is invalid", ssin));
    }

    public static InputValidationIssue unknownSsin(InEnum in, String name, String ssin) {
        return referencedResourceNotFound(in, name, ssin).detail(String.format("SSIN %s does not exist", ssin));
    }

    public static InputValidationIssue invalidPeriod(InEnum in, String name, Object period) {
        return new InputValidationIssue(ISSUE_TYPE_INVALID_PERIOD, "Period is invalid")
                .detail("endDate should not precede startDate")
                .in(in, name, period);
    }

    public static > InputValidationIssue invalidPeriod(
            Input start, Input end) {
        return new InputValidationIssue(ISSUE_TYPE_INVALID_PERIOD, "Period is invalid")
                .detail(String.format("%s should not precede %s", end.getName(), start.getName()))
                .inputs(Arrays.asList(start, end));
    }

    public static InputValidationIssue invalidIncompleteDate(InEnum in, String name, String incompleteDate) {
        return invalidStructure(in, name, incompleteDate,
                String.format("Incomplete date %s is invalid", incompleteDate));
    }

    public static InputValidationIssue invalidYearMonth(InEnum in, String name, String yearMonth) {
        return invalidStructure(in, name, yearMonth, String.format("Year month %s is invalid", yearMonth));
    }

    public static InputValidationIssue invalidEnterpriseNumber(InEnum in, String name, String enterpriseNumber) {
        return invalidStructure(in, name, enterpriseNumber,
                String.format("Enterprise number %s is invalid", enterpriseNumber));
    }

    public static InputValidationIssue invalidEstablishmentUnitNumber(InEnum in, String name,
            String establishmentUnitNumber) {
        return invalidStructure(in, name, establishmentUnitNumber,
                String.format("Establishment unit number %s is invalid", establishmentUnitNumber));
    }

    public static InputValidationIssue exactlyOneOfExpected(List> inputs) {
        return new InputValidationIssue(ISSUE_TYPE_EXACTLY_ONE_OF_EXPECTED,
                "Exactly one of these inputs must be present")
                        .detail(String.format("Exactly one of these inputs must be present: %s",
                                getJoinedNames(inputs)))
                        .inputs(inputs);
    }

    public static InputValidationIssue anyOfExpected(List> inputs) {
        return new InputValidationIssue(ISSUE_TYPE_ANY_OF_EXPECTED, "Any of these inputs must be present")
                .detail(String.format("Any of these inputs must be present: %s", getJoinedNames(inputs)))
                .inputs(inputs);
    }

    public static InputValidationIssue zeroOrExactlyOneOfExpected(List> inputs) {
        return new InputValidationIssue(ISSUE_TYPE_ZERO_OR_EXACTLY_ONE_OF_EXPECTED,
                "Exactly one or none of these inputs must be present")
                        .detail(String.format("Exactly one or none of these inputs must be present: %s",
                                getJoinedNames(inputs)))
                        .inputs(inputs);
    }

    public static InputValidationIssue zeroOrAllOfExpected(List> inputs) {
        return new InputValidationIssue(ISSUE_TYPE_ZERO_OR_ALL_OF_EXPECTED,
                "All or none of these inputs must be present")
                        .detail(String.format("All or none of these inputs must be present: %s",
                                getJoinedNames(inputs)))
                        .inputs(inputs);
    }

    public static InputValidationIssue equalExpected(List> inputs) {
        return new InputValidationIssue(ISSUE_TYPE_EQUAL_EXPECTED, "These inputs must be equal")
                .detail(String.format("These inputs must be equal: %s", getJoinedNames(inputs)))
                .inputs(inputs);
    }

    private static String getJoinedNames(List> inputs) {
        return inputs == null || inputs.isEmpty() ? ""
                : inputs.stream().map(Input::getName).collect(Collectors.joining(", "));
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy