Skip to content

Commit

Permalink
Implement mapping context options evaluation. Add validation for mapp…
Browse files Browse the repository at this point in the history
…ing context options

Signed-off-by: Vadim Guenther <vadim.guenther@bosch-si.com>
  • Loading branch information
VadimGue committed Sep 25, 2019
1 parent 7334fe9 commit 2327a1b
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 55 deletions.
Expand Up @@ -6,7 +6,10 @@
import java.util.Optional;

import org.eclipse.ditto.model.base.headers.DittoHeaders;
import org.eclipse.ditto.model.placeholders.ExpressionResolver;
import org.eclipse.ditto.model.placeholders.HeadersPlaceholder;
import org.eclipse.ditto.model.placeholders.PlaceholderFactory;
import org.eclipse.ditto.model.placeholders.PlaceholderFilter;
import org.eclipse.ditto.model.things.Feature;
import org.eclipse.ditto.model.things.FeatureDefinition;
import org.eclipse.ditto.model.things.FeatureProperties;
Expand All @@ -27,6 +30,9 @@ public class ConnectionStatusMessageMapper implements MessageMapper {
public static final String HEADER_HUB_TTD = "ttd";
public static final String HEADER_HUB_CREATION_TIME = "creation-time";

public static final String MAPPING_OPTIONS_PROPERTIES_THINGID = "thingId";
public static final String MAPPING_OPTIONS_PROPERTIES_FEATUREID = "featureId";

public static final String DEFAULT_FEATURE_ID = "ConnectionStatus";
public static final String FEATURE_DEFINITION = "com.bosch.iot.suite.standard:ConnectionStatus:1.0.0";
public static final String FEATURE_PROPERTIE_READY_SINCE = "readySince";
Expand All @@ -52,24 +58,25 @@ public ConnectionStatusMessageMapper() {
@Override
public void configure(final MappingConfig mappingConfig,
final MessageMapperConfiguration messageMapperConfiguration) {
featureId = messageMapperConfiguration.findProperty("featureId").orElse(DEFAULT_FEATURE_ID);

mappingOptionThingId = PlaceholderFactory.newHeadersPlaceholder()
.resolve(messageMapperConfiguration.getProperties(), "thingId");
if (mappingOptionThingId.equals(Optional.empty())) {
noErrorOccurred = false;
LOGGER.info("Could not find thingId in your mapping options");
}
mappingOptionThingId = messageMapperConfiguration.findProperty(
MAPPING_OPTIONS_PROPERTIES_THINGID); //returns Optional.empty() if option is not set
featureId = messageMapperConfiguration.findProperty(MAPPING_OPTIONS_PROPERTIES_FEATUREID)
.orElse(DEFAULT_FEATURE_ID);
}

@Override
public Optional<Adaptable> map(final ExternalMessage externalMessage) {
//Validate
extractedHeader =
extractHeader(externalMessage, HEADER_HUB_TTD, HEADER_HUB_CREATION_TIME, HEADER_HUB_DEVICE_ID);

checkIfEntriesSet(extractedHeader);

checkMappingOption(mappingOptionThingId);
if (!mappingOptionThingId.isPresent()) {
noErrorOccurred = false;
LOGGER.info("Mapping option \"{}\" is not set", MAPPING_OPTIONS_PROPERTIES_THINGID);
}

//Check if time is convertible
long creationTime = 0;
Expand All @@ -78,17 +85,34 @@ public Optional<Adaptable> map(final ExternalMessage externalMessage) {
creationTime = Long.parseLong(extractedHeader.get(HEADER_HUB_CREATION_TIME));
ttd = Long.parseLong(extractedHeader.get(HEADER_HUB_TTD));
} catch (NumberFormatException e) {
LOGGER.info("Header {} or {} is not convertible to type long", HEADER_HUB_CREATION_TIME, HEADER_HUB_TTD);
LOGGER.info("Header \"{}\" or \"{}\" is not convertible to type long", HEADER_HUB_CREATION_TIME,
HEADER_HUB_TTD);
noErrorOccurred = false;
}

if (creationTime < 0 || ttd < -1) {
LOGGER.info("Undefined value in {} or {}", HEADER_HUB_CREATION_TIME, HEADER_HUB_TTD);
if (creationTime < 0) {
LOGGER.info("Undefined value in \"{}\"", HEADER_HUB_CREATION_TIME);
noErrorOccurred = false;
}

if (ttd < -1) {
LOGGER.info("Undefined value in \"{}\"", HEADER_HUB_CREATION_TIME);
noErrorOccurred = false;
}

//Execute
if (noErrorOccurred) {
final ThingId thingId = ThingId.of(mappingConnectionOptions(mappingOptionThingId));

//Read thingId
final ThingId thingId;
if (mappingOptionThingId.get().equals("{{ header:device_id }}")) {
final HeadersPlaceholder headersPlaceholder = PlaceholderFactory.newHeadersPlaceholder();
final ExpressionResolver expressionResolver = PlaceholderFactory.newExpressionResolver(
PlaceholderFactory.newPlaceholderResolver(headersPlaceholder, extractedHeader));
thingId = ThingId.of(PlaceholderFilter.apply("{{ header:device_id }}", expressionResolver, false));
} else {
thingId = ThingId.of(mappingOptionThingId.get());
}

//Set time to ISO-8601 UTC
String readyUntil;
Expand Down Expand Up @@ -116,51 +140,14 @@ public Optional<Adaptable> map(final ExternalMessage externalMessage) {
.build();

final ModifyFeature modifyFeature = ModifyFeature.of(thingId, feature, DittoHeaders.empty());
LOGGER.info("modifyFeature: {} ", modifyFeature);
final Adaptable adaptable = DittoProtocolAdapter.newInstance().toAdaptable(modifyFeature);

LOGGER.info("Feature {} of Thing {} is modified", featureId, extractedHeader.get(HEADER_HUB_DEVICE_ID));
LOGGER.info("Feature \"{}\" of Thing \"{}\" is modified", featureId, thingId.toString());

return Optional.of(adaptable);
} else { return Optional.empty();}
}

private void checkMappingOption(final Optional<String> mappingOption) {
final String extractedValue = mappingOption.get();

if (extractedValue.startsWith("{{") && extractedValue.endsWith("}}")) {

final String mappingContext = extractedValue
.substring(extractedValue.indexOf(' ') + 1, extractedValue.indexOf(':'));

if (!mappingContext.equals(PlaceholderFactory.newHeadersPlaceholder().getPrefix())) {
noErrorOccurred = false;
}
}
}

private CharSequence mappingConnectionOptions(final Optional<String> mappingOption) {
final String extractedValue = mappingOption.get();

if (extractedValue.startsWith("{{") && extractedValue.endsWith("}}")) {

final String mappingValue =
extractedValue.substring(extractedValue.indexOf(":") + 1, extractedValue.lastIndexOf(" "));
return extractedHeader.get(mappingValue);
}
return extractedValue;
}


private void checkIfEntriesSet(final HashMap<String, String> extractedHeader) {
for (Map.Entry<String, String> entry : extractedHeader.entrySet()) {
if (entry.getValue() == null || "".equals(entry.getValue())) {
LOGGER.info("Header {} is not set", entry.getKey());
noErrorOccurred = false;
}
}
}

private HashMap<String, String> extractHeader(final ExternalMessage externalMessage,
final String... headerToExtract) {

Expand All @@ -174,6 +161,15 @@ private HashMap<String, String> extractHeader(final ExternalMessage externalMess
return extractedHeader;
}

private void checkIfEntriesSet(final HashMap<String, String> extractedHeader) {
for (Map.Entry<String, String> entry : extractedHeader.entrySet()) {
if ("".equals(entry.getValue())) {
LOGGER.info("Header \"{}\" is not set", entry.getKey());
noErrorOccurred = false;
}
}
}

@Override
public Optional<ExternalMessage> map(final Adaptable adaptable) {
return Optional.empty();
Expand Down
@@ -1,10 +1,10 @@
package org.eclipse.ditto.services.connectivity.mapping.custom;

import java.util.Collections;
import java.util.HashMap;
import java.util.Optional;

import org.assertj.core.api.Assertions;
import org.eclipse.ditto.model.things.ThingId;
import org.eclipse.ditto.protocoladapter.Adaptable;
import org.eclipse.ditto.services.connectivity.mapping.DefaultMappingConfig;
import org.eclipse.ditto.services.connectivity.mapping.DefaultMessageMapperConfiguration;
Expand All @@ -26,30 +26,39 @@ public class ConnectionStatusMessageMapperTest {
public static final String HEADER_HUB_TTD = "ttd";
public static final String HEADER_HUB_CREATION_TIME = "creation-time";

public static final String MAPPING_OPTIONS_PROPERTIES_THINGID = "thingId";
public static final String MAPPING_OPTIONS_PROPERTIES_FEATUREID = "featureId";

private static HashMap<String, String> validHeader;
private static HashMap<String, String> validConfigProps;

@Before
public void setUp() {
mappingConfig = DefaultMappingConfig.of(ConfigFactory.empty());
underTest = new ConnectionStatusMessageMapper();
underTest.configure(mappingConfig, DefaultMessageMapperConfiguration.of(Collections.emptyMap()));

validConfigProps = new HashMap<>();
validConfigProps.put(MAPPING_OPTIONS_PROPERTIES_THINGID, "configNamespace:configDeviceId");

validHeader = new HashMap<>();
validHeader.put(HEADER_HUB_DEVICE_ID, "namespace:deviceId");
validHeader.put(HEADER_HUB_TTD, "-1");
validHeader.put(HEADER_HUB_DEVICE_ID, "headerNamespace:headerDeviceId");
validHeader.put(HEADER_HUB_TTD, "12");
validHeader.put(HEADER_HUB_CREATION_TIME, "1568816054");
}

@Test
public void doForwardMapWithValidUseCase() {
underTest.configure(mappingConfig, DefaultMessageMapperConfiguration.of(validConfigProps));
final ExternalMessage externalMessage = ExternalMessageFactory.newExternalMessageBuilder(validHeader).build();
final Optional<Adaptable> mappingResult = underTest.map(externalMessage);

Assertions.assertThat(mappingResult).isNotEmpty();
}

//Validate external message header
@Test
public void doForwardMapWithMissingHeaderTTD() {
underTest.configure(mappingConfig, DefaultMessageMapperConfiguration.of(validConfigProps));
final HashMap<String, String> invalidHeader = validHeader;
invalidHeader.remove(HEADER_HUB_TTD);
final ExternalMessage externalMessage = ExternalMessageFactory.newExternalMessageBuilder(invalidHeader).build();
Expand All @@ -59,6 +68,7 @@ public void doForwardMapWithMissingHeaderTTD() {

@Test
public void doForwardMapWithMissingHeaderCreationTime() {
underTest.configure(mappingConfig, DefaultMessageMapperConfiguration.of(validConfigProps));
final HashMap<String, String> invalidHeader = validHeader;
invalidHeader.remove(HEADER_HUB_CREATION_TIME);
final ExternalMessage externalMessage = ExternalMessageFactory.newExternalMessageBuilder(invalidHeader).build();
Expand All @@ -68,6 +78,7 @@ public void doForwardMapWithMissingHeaderCreationTime() {

@Test
public void doForwardMapWithMissingHeaderDeviceID() {
underTest.configure(mappingConfig, DefaultMessageMapperConfiguration.of(validConfigProps));
final HashMap<String, String> invalidHeader = validHeader;
invalidHeader.remove(HEADER_HUB_DEVICE_ID);
final ExternalMessage externalMessage = ExternalMessageFactory.newExternalMessageBuilder(invalidHeader).build();
Expand All @@ -77,6 +88,7 @@ public void doForwardMapWithMissingHeaderDeviceID() {

@Test
public void doForwardMapWithInvalidHeaderTTD() {
underTest.configure(mappingConfig, DefaultMessageMapperConfiguration.of(validConfigProps));
final HashMap<String, String> invalidHeader = validHeader;
invalidHeader.replace(HEADER_HUB_TTD, "Invalid Value");
final ExternalMessage externalMessage = ExternalMessageFactory.newExternalMessageBuilder(invalidHeader).build();
Expand All @@ -86,6 +98,7 @@ public void doForwardMapWithInvalidHeaderTTD() {

@Test
public void doForwardMapWithInvalidHeaderCreationTime() {
underTest.configure(mappingConfig, DefaultMessageMapperConfiguration.of(validConfigProps));
final HashMap<String, String> invalidHeader = validHeader;
invalidHeader.replace(HEADER_HUB_CREATION_TIME, "Invalid Value");
final ExternalMessage externalMessage = ExternalMessageFactory.newExternalMessageBuilder(invalidHeader).build();
Expand All @@ -95,10 +108,53 @@ public void doForwardMapWithInvalidHeaderCreationTime() {

@Test
public void doForwardMapWithInvalidTTDValue() {
underTest.configure(mappingConfig, DefaultMessageMapperConfiguration.of(validConfigProps));
final HashMap<String, String> invalidHeader = validHeader;
invalidHeader.replace(HEADER_HUB_TTD, "-5625");
final String invalidTTDValue = "-5625";
invalidHeader.replace(HEADER_HUB_TTD, invalidTTDValue);
final ExternalMessage externalMessage = ExternalMessageFactory.newExternalMessageBuilder(invalidHeader).build();
final Optional<Adaptable> mappingResult = underTest.map(externalMessage);
Assertions.assertThat(mappingResult).isEmpty();
}

//Validate mapping context options
@Test
public void doForwardMappingContextWithoutFeatureId() {
underTest.configure(mappingConfig, DefaultMessageMapperConfiguration.of(validConfigProps));
final ExternalMessage externalMessage = ExternalMessageFactory.newExternalMessageBuilder(validHeader).build();
final Optional<Adaptable> mappingResult = underTest.map(externalMessage);
Assertions.assertThat(mappingResult.get().getPayload().getPath().getFeatureId().get())
.isEqualTo("ConnectionStatus");
}

@Test
public void doForwardMappingContextWithIndividualFeatureId() {
final String individualFeatureId = "individualFeatureId";
validConfigProps.put(MAPPING_OPTIONS_PROPERTIES_FEATUREID, individualFeatureId);
underTest.configure(mappingConfig, DefaultMessageMapperConfiguration.of(validConfigProps));
final ExternalMessage externalMessage = ExternalMessageFactory.newExternalMessageBuilder(validHeader).build();
final Optional<Adaptable> mappingResult = underTest.map(externalMessage);
Assertions.assertThat(mappingResult.get().getPayload().getPath().getFeatureId().get())
.isEqualTo(individualFeatureId);
}

@Test
public void doForwardMappingContextWithoutThingId() {
validConfigProps.remove(MAPPING_OPTIONS_PROPERTIES_THINGID);
underTest.configure(mappingConfig, DefaultMessageMapperConfiguration.of(validConfigProps));
final ExternalMessage externalMessage = ExternalMessageFactory.newExternalMessageBuilder(validHeader).build();
final Optional<Adaptable> mappingResult = underTest.map(externalMessage);

Assertions.assertThat(mappingResult).isEmpty();
}

@Test
public void doForwardMappingContextWithThingIdMapping() {
validConfigProps.replace(MAPPING_OPTIONS_PROPERTIES_THINGID, "{{ header:device_id }}");
underTest.configure(mappingConfig, DefaultMessageMapperConfiguration.of(validConfigProps));
final ExternalMessage externalMessage = ExternalMessageFactory.newExternalMessageBuilder(validHeader).build();
final Optional<Adaptable> mappingResult = underTest.map(externalMessage);
Assertions.assertThat(mappingResult.get().getTopicPath().getId())
.isEqualTo(ThingId.of(validHeader.get(HEADER_HUB_DEVICE_ID)).getName());
}
}

0 comments on commit 2327a1b

Please sign in to comment.