Skip to content

Commit

Permalink
Issue #106: Added validator for ditto channel header.
Browse files Browse the repository at this point in the history
Signed-off-by: Juergen Fickel <juergen.fickel@bosch.io>
  • Loading branch information
Juergen Fickel committed Sep 17, 2021
1 parent 0473d49 commit 2446cd7
Show file tree
Hide file tree
Showing 4 changed files with 195 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright (c) 2021 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.ditto.base.model.headers;

import java.text.MessageFormat;
import java.util.Locale;

import javax.annotation.concurrent.Immutable;

import org.eclipse.ditto.base.model.exceptions.DittoHeaderInvalidException;

/**
* This validator checks if a normalized CharSequence is equal to {@value #DITTO_CHANNEL_TWIN} or
* {@value #DITTO_CHANNEL_LIVE}.
* Normalized in this context means trimmed and converted to lower case.
* Normalization is temporarily conducted by this class for validation only.
*
* @since 2.1.0
*/
@Immutable
final class DittoChannelValueValidator extends AbstractHeaderValueValidator {

static final String DITTO_CHANNEL_TWIN = "twin";
static final String DITTO_CHANNEL_LIVE = "live";

private DittoChannelValueValidator() {
super(String.class::equals);
}

/**
* Returns an instance of {@code DittoChannelValueValidator}.
*
* @return the instance.
*/
static DittoChannelValueValidator getInstance() {
return new DittoChannelValueValidator();
}

@Override
protected void validateValue(final HeaderDefinition definition, final CharSequence value) {
final String normalizedValue = normalize(value);

if (!DITTO_CHANNEL_TWIN.equals(normalizedValue) && !DITTO_CHANNEL_LIVE.equals(normalizedValue)) {
throw DittoHeaderInvalidException.newInvalidTypeBuilder(definition, value, "ditto-channel")
.description(MessageFormat.format("The value must either be <{0}> or <{1}>.",
DITTO_CHANNEL_TWIN,
DITTO_CHANNEL_LIVE))
.build();
}
}

private static String normalize(final CharSequence charSequence) {
return charSequence.toString().trim().toLowerCase(Locale.ENGLISH);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ public enum DittoHeaderDefinition implements HeaderDefinition {
* Key: {@code "ditto-channel"}, Java type: {@link String}.
* </p>
*/
CHANNEL("ditto-channel", String.class, false, false, HeaderValueValidators.getNoOpValidator()),
CHANNEL("ditto-channel", String.class, false, false, HeaderValueValidators.getDittoChannelValidator()),

/**
* Header definition for origin value that is set to the id of the originating session.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import javax.annotation.concurrent.Immutable;

import org.eclipse.ditto.base.model.headers.entitytag.EntityTagMatchers;
import org.eclipse.ditto.base.model.headers.metadata.MetadataHeaders;

/**
* Provides validators for header values.
Expand Down Expand Up @@ -172,4 +171,17 @@ static ValueValidator getMetadataHeadersValidator() {
return MetadataHeadersValueValidator.getInstance();
}

/**
* Returns a validator for checking if a normalized CharSequence is equal to
* {@value DittoChannelValueValidator#DITTO_CHANNEL_TWIN} or {@value DittoChannelValueValidator#DITTO_CHANNEL_LIVE}.
* Normalized in this context means trimmed and converted to lower case.
* Normalization is temporarily conducted by the returned validator for validation only.
*
* @return the validator.
* @since 2.1.0
*/
static ValueValidator getDittoChannelValidator() {
return DittoChannelValueValidator.getInstance();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
* Copyright (c) 2021 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.ditto.base.model.headers;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatNoException;
import static org.mutabilitydetector.unittesting.MutabilityAssert.assertInstancesOf;
import static org.mutabilitydetector.unittesting.MutabilityMatchers.areImmutable;

import java.util.Arrays;
import java.util.List;

import org.assertj.core.api.Assertions;
import org.eclipse.ditto.base.model.exceptions.DittoHeaderInvalidException;
import org.junit.Test;
import org.junit.experimental.runners.Enclosed;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

/**
* Unit test for {@link DittoChannelValueValidator}.
*/
@RunWith(Enclosed.class)
public final class DittoChannelValueValidatorTest {

public static final class GeneralFunctionalityTest {

@Test
public void assertImmutability() {
assertInstancesOf(DittoDurationValueValidator.class, areImmutable());
}

@Test
public void getInstanceReturnsSomething() {
final DittoChannelValueValidator instance = DittoChannelValueValidator.getInstance();

assertThat(instance).isInstanceOf(DittoChannelValueValidator.class);
}

@Test
public void acceptNullDefinition() {
final DittoChannelValueValidator underTest = DittoChannelValueValidator.getInstance();

Assertions.assertThatNullPointerException()
.isThrownBy(() -> underTest.accept(null, "covfefe"))
.withMessage("The definition must not be null!")
.withNoCause();
}

@Test
public void acceptNullCharSequence() {
final DittoChannelValueValidator underTest = DittoChannelValueValidator.getInstance();
final DittoHeaderDefinition channel = DittoHeaderDefinition.CHANNEL;

Assertions.assertThatExceptionOfType(DittoHeaderInvalidException.class)
.isThrownBy(() -> underTest.accept(channel, null))
.withMessage("The value 'null' of the header '%s' is not a valid String.", channel.getKey())
.withNoCause();
}

}

@RunWith(Parameterized.class)
public static final class ParameterizedValueValidationTest {

@Parameterized.Parameter
public CharSequence value;

@Parameterized.Parameter(1)
public boolean expectedValueToBeValid;

@Parameterized.Parameters(name = "value: {0}, expected valid: {1}")
public static List<Object[]> parameters() {
return Arrays.asList(new Object[][]{
{DittoChannelValueValidator.DITTO_CHANNEL_TWIN, true},
{DittoChannelValueValidator.DITTO_CHANNEL_LIVE, true},
{"foo", false},
{" twin", true},
{"live ", true},
{" TWIN", true},
{"livE", true},
{"twins", false},
{"alive", false},
});
}

@Test
public void acceptChannelValue() {
final DittoChannelValueValidator underTest = DittoChannelValueValidator.getInstance();
final DittoHeaderDefinition channel = DittoHeaderDefinition.CHANNEL;

if (expectedValueToBeValid) {
assertThatNoException()
.isThrownBy(() -> underTest.accept(channel, value));
} else {
assertThatExceptionOfType(DittoHeaderInvalidException.class)
.isThrownBy(() -> underTest.accept(channel, value))
.withNoCause();
}
}

}

}

0 comments on commit 2446cd7

Please sign in to comment.