Skip to content
This repository has been archived by the owner on Apr 14, 2023. It is now read-only.

Commit

Permalink
fix(#1675): Update granularity and offset validation checks
Browse files Browse the repository at this point in the history
  • Loading branch information
matthewdunsdon committed Jul 17, 2020
1 parent 4e83adb commit 1facc82
Show file tree
Hide file tree
Showing 13 changed files with 707 additions and 118 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@

package com.scottlogic.datahelix.generator.common.profile;

import com.scottlogic.datahelix.generator.common.RandomNumberGenerator;
import com.scottlogic.datahelix.generator.common.ValidationException;
import com.scottlogic.datahelix.generator.common.util.NumberUtils;

import com.scottlogic.datahelix.generator.common.util.defaults.NumericDefaults;
import com.scottlogic.datahelix.generator.common.RandomNumberGenerator;
import com.scottlogic.datahelix.generator.common.validators.ValidationResult;

import java.math.BigDecimal;
import java.math.RoundingMode;
Expand All @@ -41,7 +41,7 @@ public static NumericGranularity create(Object granularity)
BigDecimal asNumber = NumberUtils.coerceToBigDecimal(granularity);
if (asNumber == null)
{
throw new ValidationException("Can't interpret granularity expression: " + granularity);
throw new ValidationException(String.format("Can't interpret numeric granularity expression: %s", ValidationResult.quote(granularity)));
}
if (asNumber.compareTo(BigDecimal.ONE) > 0)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,23 @@
import com.scottlogic.datahelix.generator.profile.dtos.constraints.grammatical.AnyOfConstraintDTO;
import com.scottlogic.datahelix.generator.profile.dtos.constraints.grammatical.ConditionalConstraintDTO;
import com.scottlogic.datahelix.generator.profile.dtos.constraints.grammatical.NotConstraintDTO;
import com.scottlogic.datahelix.generator.profile.dtos.constraints.relations.EqualToFieldConstraintDTO;
import com.scottlogic.datahelix.generator.profile.dtos.constraints.relations.InMapConstraintDTO;
import com.scottlogic.datahelix.generator.profile.dtos.constraints.relations.RelationalConstraintDTO;
import com.scottlogic.datahelix.generator.profile.validators.profile.constraints.atomic.*;
import com.scottlogic.datahelix.generator.profile.validators.profile.constraints.capabilities.DateTimeGranularityValidator;
import com.scottlogic.datahelix.generator.profile.validators.profile.constraints.capabilities.NumericGranularityValidator;
import com.scottlogic.datahelix.generator.profile.validators.profile.constraints.grammatical.AllOfConstraintValidator;
import com.scottlogic.datahelix.generator.profile.validators.profile.constraints.grammatical.AnyOfConstraintValidator;
import com.scottlogic.datahelix.generator.profile.validators.profile.constraints.grammatical.ConditionalConstraintValidator;
import com.scottlogic.datahelix.generator.profile.validators.profile.constraints.grammatical.NotConstraintValidator;
import com.scottlogic.datahelix.generator.profile.validators.profile.constraints.relations.EqualToFieldConstraintValidator;
import com.scottlogic.datahelix.generator.profile.validators.profile.constraints.relations.InMapConstraintValidator;
import com.scottlogic.datahelix.generator.profile.validators.profile.constraints.relations.RelationalConstraintValidator;
import com.scottlogic.datahelix.generator.profile.validators.profile.constraints.relations.NumericRelationalConstraintValidator;
import com.scottlogic.datahelix.generator.profile.validators.profile.constraints.relations.TemporalRelationalConstraintValidator;

import java.util.List;
import java.util.Optional;

public abstract class ConstraintValidator<T extends ConstraintDTO> implements Validator<T>
{
Expand Down Expand Up @@ -76,22 +81,24 @@ protected static ValidationResult validateConstraint(ConstraintDTO dto, List<Fie
case IN_MAP:
return new InMapConstraintValidator(fields).validate((InMapConstraintDTO) dto);
case IS_NULL:
return new IsNullConstraintValidator(fields).validate((IsNullConstraintDTO)dto);
return new IsNullConstraintValidator(fields).validate((IsNullConstraintDTO) dto);
case GRANULAR_TO:
return new GranularToConstraintValidator(fields).validate((GranularToConstraintDTO) dto);
case MATCHES_REGEX:
case CONTAINS_REGEX:
return new RegexConstraintValidator(fields).validate((RegexConstraintDTO) dto);
case EQUAL_TO_FIELD:
return new EqualToFieldConstraintValidator(fields).validate((EqualToFieldConstraintDTO) dto);
case GREATER_THAN_FIELD:
case GREATER_THAN_OR_EQUAL_TO_FIELD:
case LESS_THAN_FIELD:
case LESS_THAN_OR_EQUAL_TO_FIELD:
return new NumericRelationalConstraintValidator(fields).validate((RelationalConstraintDTO) dto);
case AFTER_FIELD:
case AFTER_OR_AT_FIELD:
case BEFORE_FIELD:
case BEFORE_OR_AT_FIELD:
return new RelationalConstraintValidator<>(fields).validate((RelationalConstraintDTO) dto);
return new TemporalRelationalConstraintValidator(fields).validate((RelationalConstraintDTO) dto);
case OF_LENGTH:
return new OfLengthConstraintValidator(fields, FieldType.STRING).validate((OfLengthConstraintDTO) dto);
case LONGER_THAN:
Expand Down Expand Up @@ -140,11 +147,14 @@ private static ValidationResult constraintMustBeSpecified(ConstraintDTO dto)
return ValidationResult.success();
}

protected Optional<FieldDTO> findField(String field)
{
return fields.stream().filter(f -> f.name.equals(field)).findFirst();
}

protected FieldDTO getField(String field)
{
return fields.stream()
.filter(f -> f.name.equals(field))
.findFirst()
return findField(field)
.orElseThrow(() -> new IllegalStateException(String.format("Field '%s' referenced but can not be found", field)));
}

Expand All @@ -159,6 +169,8 @@ protected ValidationResult validateGranularity(T dto, String field, Object value
return ValidationResult.failure(String.format("Granularity %s is not supported for string fields%s", ValidationResult.quote(value), getErrorInfo(dto)));
case DATETIME:
return new DateTimeGranularityValidator(getErrorInfo(dto)).validate((String) value);
case NUMERIC:
return new NumericGranularityValidator(getErrorInfo(dto)).validate(value);
}
return ValidationResult.success();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ ValidationResult fieldMustBeValid(T dto)
{
return ValidationResult.failure("Field must be specified" + getErrorInfo(dto));
}
Optional<FieldDTO> field = fields.stream().filter(f -> f.name.equals(fieldName)).findFirst();
Optional<FieldDTO> field = findField(fieldName);
if (!field.isPresent())
{
return ValidationResult.failure(String.format("%s must be defined in fields%s", ValidationResult.quote(fieldName), getErrorInfo(dto)));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright 2019 Scott Logic Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.scottlogic.datahelix.generator.profile.validators.profile.constraints.capabilities;

import com.scottlogic.datahelix.generator.common.ValidationException;
import com.scottlogic.datahelix.generator.common.profile.NumericGranularity;
import com.scottlogic.datahelix.generator.common.validators.ValidationResult;
import com.scottlogic.datahelix.generator.common.validators.Validator;

public class NumericGranularityValidator implements Validator<Object>
{
private final String errorInfo;

public NumericGranularityValidator(String errorInfo)
{
this.errorInfo = errorInfo;
}

@Override
public final ValidationResult validate(Object granularity)
{
try
{
NumericGranularity.create(granularity);
return ValidationResult.success();
} catch (ValidationException e)
{
return ValidationResult.failure(String.format("%s%s", e.getMessage(), errorInfo));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright 2019 Scott Logic Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.scottlogic.datahelix.generator.profile.validators.profile.constraints.relations;

import com.scottlogic.datahelix.generator.common.profile.FieldType;
import com.scottlogic.datahelix.generator.common.validators.ValidationResult;
import com.scottlogic.datahelix.generator.profile.dtos.FieldDTO;
import com.scottlogic.datahelix.generator.profile.dtos.constraints.relations.EqualToFieldConstraintDTO;
import com.scottlogic.datahelix.generator.profile.validators.profile.FieldValidator;

import java.util.List;

public class EqualToFieldConstraintValidator extends RelationalConstraintValidator<EqualToFieldConstraintDTO>
{

public EqualToFieldConstraintValidator(List<FieldDTO> fields)
{
super(fields);
}

@Override
public ValidationResult validate(EqualToFieldConstraintDTO dto)
{
ValidationResult fieldsAndOffsetValid = ValidationResult.combine(
fieldMustBeValid(dto, dto.field, FIELD_DESCRIPTION),
fieldMustBeValid(dto, dto.getOtherField(), RELATED_FIELD_DESCRIPTION),
offsetMustBeValid(dto)
);
return fieldsAndOffsetValid.isSuccess ? fieldAndOtherFieldMustHaveType(dto) : fieldsAndOffsetValid;
}

private ValidationResult fieldAndOtherFieldMustHaveType(EqualToFieldConstraintDTO dto)
{
FieldType fieldType = FieldValidator.getSpecificFieldType(getField(dto.field)).getFieldType();
FieldType otherFieldType = FieldValidator.getSpecificFieldType(getField(dto.getOtherField())).getFieldType();

return fieldType == otherFieldType
? ValidationResult.success()
: ValidationResult.failure(String.format("Field type %s doesn't match related field type %s%s", ValidationResult.quote(dto.field), ValidationResult.quote(dto.getOtherField()), getErrorInfo(dto)));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright 2019 Scott Logic Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.scottlogic.datahelix.generator.profile.validators.profile.constraints.relations;

import com.scottlogic.datahelix.generator.common.profile.FieldType;
import com.scottlogic.datahelix.generator.common.validators.ValidationResult;
import com.scottlogic.datahelix.generator.profile.dtos.FieldDTO;
import com.scottlogic.datahelix.generator.profile.dtos.constraints.relations.RelationalConstraintDTO;
import com.scottlogic.datahelix.generator.profile.validators.profile.FieldValidator;

import java.util.List;
import java.util.Optional;

import static com.scottlogic.datahelix.generator.common.validators.ValidationResult.quote;

public class NumericRelationalConstraintValidator<T extends RelationalConstraintDTO> extends RelationalConstraintValidator<T>
{
public NumericRelationalConstraintValidator(List<FieldDTO> fields)
{
super(fields);
}

@Override
public ValidationResult validate(T dto)
{
return ValidationResult.combine(
fieldMustBeValid(dto, dto.field, FIELD_DESCRIPTION),
fieldMustBeValid(dto, dto.getOtherField(), RELATED_FIELD_DESCRIPTION),
fieldTypeMustBeNumeric(dto, dto.field, FIELD_DESCRIPTION),
fieldTypeMustBeNumeric(dto, dto.getOtherField(), RELATED_FIELD_DESCRIPTION),
offsetMustBeValid(dto)
);
}

private ValidationResult fieldTypeMustBeNumeric(T dto, String fieldName, String fieldDescription)
{
Optional<FieldType> fieldType = findField(fieldName)
.map(f -> FieldValidator.getSpecificFieldType(f).getFieldType());

return !fieldType.isPresent() || fieldType.get() == FieldType.NUMERIC
? ValidationResult.success()
: ValidationResult.failure(String.format("%s must be numeric however it has type %s%s", fieldDescription, quote(fieldType.get()), getErrorInfo(dto)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,50 +22,55 @@
import com.scottlogic.datahelix.generator.profile.dtos.constraints.relations.RelationalConstraintDTO;
import com.scottlogic.datahelix.generator.profile.validators.profile.ConstraintValidator;
import com.scottlogic.datahelix.generator.profile.validators.profile.FieldValidator;
import com.scottlogic.datahelix.generator.profile.validators.profile.constraints.capabilities.DateTimeGranularityValidator;
import com.scottlogic.datahelix.generator.profile.validators.profile.constraints.capabilities.NumericGranularityValidator;

import java.util.List;
import java.util.Optional;

public class RelationalConstraintValidator<T extends RelationalConstraintDTO> extends ConstraintValidator<T>
import static com.scottlogic.datahelix.generator.common.validators.ValidationResult.quote;

abstract public class RelationalConstraintValidator<T extends RelationalConstraintDTO> extends ConstraintValidator<T>
{
protected static final String FIELD_DESCRIPTION = "Field";
protected static final String RELATED_FIELD_DESCRIPTION = "Related field";

public RelationalConstraintValidator(List<FieldDTO> fields)
{
super(fields);
}

@Override
public ValidationResult validate(T dto)
protected ValidationResult offsetMustBeValid(T dto)
{
String fieldName = dto.field;
String otherFieldName = dto.getOtherField();
if (fieldName == null || fieldName.isEmpty())
{
return ValidationResult.failure("Field must be specified" + getErrorInfo(dto));
}
if (otherFieldName == null || otherFieldName.isEmpty())
{
return ValidationResult.failure("Related field must be specified" + getErrorInfo(dto));
}
Optional<FieldDTO> field = fields.stream().filter(f -> f.name.equals(fieldName)).findFirst();
if (!field.isPresent())
{
return ValidationResult.failure(String.format("%s must be defined in fields%s", ValidationResult.quote(fieldName), getErrorInfo(dto)));
}
Optional<FieldDTO> otherField = fields.stream().filter(f -> f.name.equals(otherFieldName)).findFirst();
if (!otherField.isPresent())
{
return ValidationResult.failure(String.format("%s must be defined in fields%s", ValidationResult.quote(otherFieldName), getErrorInfo(dto)));
}
FieldType fieldType = FieldValidator.getSpecificFieldType(field.get()).getFieldType();
FieldType otherFieldType = FieldValidator.getSpecificFieldType(otherField.get()).getFieldType();
if (fieldType != otherFieldType)
boolean withoutOffsetUnit = dto.offsetUnit == null || dto.offsetUnit.isEmpty();
Optional<FieldType> fieldType = findField(dto.field)
.map(f -> FieldValidator.getSpecificFieldType(f).getFieldType());

if (!fieldType.isPresent() || withoutOffsetUnit) return ValidationResult.success();

switch (fieldType.get())
{
return ValidationResult.failure(String.format("Field type %s doesn't match related field type %s%s", ValidationResult.quote(fieldName), ValidationResult.quote(otherFieldName), getErrorInfo(dto)));
case BOOLEAN:
return ValidationResult.failure(String.format("Offset is not supported for boolean fields%s", getErrorInfo(dto)));
case STRING:
return ValidationResult.failure(String.format("Offset is not supported for string fields%s", getErrorInfo(dto)));
case DATETIME:
return new DateTimeGranularityValidator(getErrorInfo(dto)).validate(dto.offsetUnit);
case NUMERIC:
return new NumericGranularityValidator(getErrorInfo(dto)).validate(dto.offsetUnit);
}
if (dto.offsetUnit != null && !dto.offsetUnit.isEmpty())
return ValidationResult.success();
}

protected ValidationResult fieldMustBeValid(T dto, String fieldName, String fieldDescription)
{
if (fieldName == null || fieldName.isEmpty())
{
return validateGranularity(dto, dto.field, dto.offsetUnit);
return ValidationResult.failure(String.format("%s must be specified%s", fieldDescription, getErrorInfo(dto)));
}
return ValidationResult.success();

return findField(fieldName).isPresent()
? ValidationResult.success()
: ValidationResult.failure(String.format("%s must be defined in fields%s", quote(fieldName), getErrorInfo(dto)));
}
}

0 comments on commit 1facc82

Please sign in to comment.