Skip to content

Adding Simple Validators

Gal Koren edited this page Nov 12, 2020 · 1 revision

FieldValidator (most simple one)

Let’s validate the ads headline is no longer than 256 chars.

class HeadlineLengthValidator implements FieldValidator<AdEntity, String> {

   @Override
   public EntityField<AdEntity, String> validatedField() {
       return AdEntity.HEADLINE;
   }

   @Override
   public ValidationError validate(String value) {
       return value != null && value.length() > 256
           ? new ValidationError("headline cannot exceed 256 chars")
           : null;
   }
}

Performance notes:

  • The fields validator is only triggered if the command contains the validated field (HEADLINE) and if the old value (currently in DB) is different than the new value (in the command).
  • The old value is fetched from DB only if the command contains the validated field.
  • To minimize DB access, fetching from DB is done in a single query at the beginning of the flow and not one command at a time.

Add validators to the flow

Let's go back to our AdPersistence class where we left an empty lonely flow.

private ChangeFlowConfig.Builder<AdEntity> flowBuilder() {
    return changeFlowConfigBuilder.newInstance(AdEntity.INSTANCE)
           .withValidator(buildChangesValidator(AdEntity.INSTANCE, asList(
                //
                // list of validators comes here
                //
                new HeadlineLengthValidator()
            )));

}

FieldCombinationValidator

Now let's have a validation rule based on multiple fields, HEADLINE and URL

class WhenUrlContainsKenshooThenHeadlineMustContainKenshoo implements FieldsCombinationValidator<AdEntity> {

    @Override
    public Stream<EntityField<AdEntity, ?>> validatedFields() {
        return Stream.of(AdEntity.HEADLINE, AdEntity.URL);
    }

    @Override
    public ValidationError validate(FieldsValueMap<AdEntity> values) {
        return values.get(URL).contains("kenshoo") && !values.get(HEADLINE).contains("kenshoo")
            ? new ValidationError("URL contains kenshoo but HEADLINE doesn't");
            : null;
    }
}

FieldComplexValidator

Examples become more complicated as we go :)
Now we also want fields from ancestor entities. Suppose our Ad entity belongs to a Campaign entity and it has a CAMPAIGN_ID field referencing the campaign (more about relations in later chapters). Suppose Campaign has a CAMPAIGN_TYPE field. Let's create some complex validation rule:

class UrlMustStartWithHttpsForVideoCampaigns implements FieldComplexValidator<AdEntity, String> {

    @Override
    public EntityField<EntityForTest, String> validatedField() {
        return AdEntity.URL;
    }

    @Override
    public Stream<EntityField<?, ?>> fetchFields() {
        return Stream.of(CampaignEntity.CAMPAIGN_TYPE);
    }

    @Override
    public ValidationError validate(String url, CurrentEntityState otherValues) {
        return otherValues.get(CAMPAIGN_TYPE) == CampaignType.VIDEO && !url.startsWith("https")
            ? new ValidationError("an Ad URL must start with https on a video campaigns");
            : null;
    }
}