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

com.kenshoo.pl.entity.spi.helpers.UniquenessValidator Maven / Gradle / Ivy

Go to download

A Java persistence layer based on JOOQ for high performance and business flow support.

There is a newer version: 0.1.121-jooq-3.16.3
Show newest version
package com.kenshoo.pl.entity.spi.helpers;

import com.kenshoo.pl.entity.*;
import com.kenshoo.pl.entity.internal.EntitiesFetcher;
import com.kenshoo.pl.entity.internal.EntityWithNullForMissingField;
import com.kenshoo.pl.entity.spi.ChangesValidator;
import org.apache.commons.lang3.ArrayUtils;
import org.jooq.lambda.Seq;

import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.function.BinaryOperator;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.kenshoo.pl.entity.ChangeOperation.UPDATE;
import static com.kenshoo.pl.entity.SupportedChangeOperation.CREATE;
import static java.util.Objects.requireNonNull;
import static java.util.function.Function.identity;
import static java.util.stream.Collectors.toMap;


public class UniquenessValidator> implements ChangesValidator {

    private final String errorCode;
    private final EntitiesFetcher fetcher;
    private final UniqueKey uniqueKey;
    private final PLCondition condition;
    private final SupportedChangeOperation operation;

    private UniquenessValidator(EntitiesFetcher fetcher, UniqueKey uniqueKey, SupportedChangeOperation operation, PLCondition condition, String errorCode) {
        this.errorCode = errorCode;
        this.fetcher = requireNonNull(fetcher, "entity fetcher must be provided");
        this.uniqueKey = requireNonNull(uniqueKey, "unique key must be provided");
        this.condition = requireNonNull(condition, "condition must be provided");
        this.operation = requireNonNull(operation, "operation must be provided");
    }

    @Override
    public SupportedChangeOperation getSupportedChangeOperation() {
        return operation;
    }

    @Override
    public void validate(Collection> commands, ChangeOperation op, ChangeContext ctx) {

        final var commandsToValidate = commands.stream().filter(hasChangeInUniqueKeyField()).collect(Collectors.toList());
        Map, ? extends EntityChange> commandsByIds = markDuplicatesInCollectionWithErrors(commandsToValidate, ctx);
        if (commandsByIds.isEmpty())
            return;

        UniqueKey pk = uniqueKey.getEntityType().getPrimaryKey();
        EntityField[] uniqueKeyAndPK = ArrayUtils.addAll(uniqueKey.getFields(), pk.getFields());

        Map, CurrentEntityState> duplicates = fetcher.fetch(uniqueKey.getEntityType(), commandsByIds.keySet(), condition, uniqueKeyAndPK)
                .stream()
                .collect(toMap(e -> createKeyValue(e, uniqueKey), identity()));

        duplicates.forEach((dupKey, dupEntity) -> ctx.addValidationError(commandsByIds.get(dupKey), errorForDatabaseConflict(dupEntity, pk)));
    }

    private Predicate> hasChangeInUniqueKeyField() {
        return cmd -> !UPDATE.equals(cmd.getChangeOperation()) || Arrays.stream(uniqueKey.getFields()).anyMatch(cmd::isFieldChanged);
    }

    private Map, EntityChange> markDuplicatesInCollectionWithErrors(Collection> commands, ChangeContext ctx) {
        return commands
                .stream()
                .filter(command -> condition.getPostFetchCondition().test(ctx.getFinalEntity(command)))
                .collect(toMap(cmd -> createKeyValue(cmd, ctx, uniqueKey), identity(), fail2ndConflictingCommand(ctx)));
    }

    private ValidationError errorForDatabaseConflict(CurrentEntityState dupEntity, UniqueKey pk) {
        return new ValidationError(errorCode, Seq.of(pk.getFields()).toMap(Object::toString, field -> String.valueOf(dupEntity.get(field))));
    }

    private BinaryOperator> fail2ndConflictingCommand(ChangeContext ctx) {
        return (cmd1, cmd2) -> {
            ctx.addValidationError(cmd2, new ValidationError(errorCode));
            return cmd1;
        };
    }

    private static > Identifier createKeyValue(EntityChange cmd, ChangeContext ctx, UniqueKey key) {
        return key.createIdentifier(new EntityWithNullForMissingField(ctx.getFinalEntity(cmd)));
    }

    private Identifier createKeyValue(CurrentEntityState currentEntityState, UniqueKey key) {
        return key.createIdentifier(currentEntityState);
    }

    @Override
    public Stream> requiredFields(Collection> fieldsToUpdate, ChangeOperation op) {
        return Stream.concat(Stream.of(uniqueKey.getFields()), Stream.of(uniqueKey.getEntityType().getPrimaryKey().getFields()));
    }

    public static class Builder> {

        private String errorCode = "DUPLICATE_ENTITY";
        private final EntitiesFetcher fetcher;
        private final UniqueKey uniqueKey;
        private PLCondition condition = PLCondition.trueCondition();
        private SupportedChangeOperation operation = CREATE;

        public Builder(EntitiesFetcher fetcher, UniqueKey uniqueKey) {
            this.fetcher = fetcher;
            this.uniqueKey = uniqueKey;
        }

        public Builder setOperation(SupportedChangeOperation operation) {
            this.operation = operation;
            return this;
        }

        public Builder setCondition(PLCondition condition) {
            this.condition = condition;
            return this;
        }

        public Builder setErrorCode(String errorCode) {
            this.errorCode = errorCode;
            return this;
        }

        public UniquenessValidator build() {
            return new UniquenessValidator<>(fetcher, uniqueKey, operation, condition, errorCode);
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy