Skip to content

Commit

Permalink
Change ImplicitThingCreation PayloadMapper to use option for desired …
Browse files Browse the repository at this point in the history
…ErrorResponse headers

Signed-off-by: David Schwilk <david.schwilk@bosch.io>
  • Loading branch information
DerSchwilk committed Sep 10, 2020
1 parent ac73cf7 commit f6bd382
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,29 @@
import static org.eclipse.ditto.model.base.exceptions.DittoJsonException.wrapJsonRuntimeException;

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

import javax.annotation.Nullable;

import org.eclipse.ditto.json.JsonFactory;
import org.eclipse.ditto.json.JsonField;
import org.eclipse.ditto.json.JsonObject;
import org.eclipse.ditto.json.JsonValue;
import org.eclipse.ditto.model.base.common.Placeholders;
import org.eclipse.ditto.model.base.entity.id.NamespacedEntityIdInvalidException;
import org.eclipse.ditto.model.base.headers.DittoHeaders;
import org.eclipse.ditto.model.connectivity.MessageMapperConfigurationInvalidException;
import org.eclipse.ditto.model.messages.MessageDirection;
import org.eclipse.ditto.model.messages.MessageHeaders;
import org.eclipse.ditto.model.messages.MessagesModelFactory;
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.placeholders.PlaceholderFunctionUnknownException;
import org.eclipse.ditto.model.policies.Policy;
import org.eclipse.ditto.model.policies.PolicyId;
import org.eclipse.ditto.model.things.Thing;
Expand Down Expand Up @@ -71,18 +78,31 @@ public final class ImplicitThingCreationMessageMapper extends AbstractMessageMap
private static final DittoProtocolAdapter DITTO_PROTOCOL_ADAPTER = DittoProtocolAdapter.newInstance();
private static final HeadersPlaceholder HEADERS_PLACEHOLDER = PlaceholderFactory.newHeadersPlaceholder();
private static final String THING_TEMPLATE = "thing";
private static final String COMMAND_HEADERS = "commandHeaders";
private static final String THING_ID = "thingId";
private static final String THING_ID_CONFIGURATION_PROPERTY = THING_TEMPLATE + "/" + THING_ID;
private static final String POLICY_ID = "policyId";
private static final String POLICY_ID_CONFIGURATION_PROPERTY = THING_TEMPLATE + "/" + POLICY_ID;

private String thingTemplate;
private Map<String, String> commandHeaders;

@Override
protected void doConfigure(final MappingConfig mappingConfig, final MessageMapperConfiguration configuration) {
thingTemplate = configuration.findProperty(THING_TEMPLATE).orElseThrow(
() -> MessageMapperConfigurationInvalidException.newBuilder(THING_TEMPLATE).build());

configuration.findProperty(COMMAND_HEADERS, JsonValue::isObject, JsonValue::asObject)
.ifPresent(configuredHeaders -> {
if (!configuredHeaders.isEmpty()) {
commandHeaders = new HashMap<>();
for (final JsonField field : configuredHeaders) {
commandHeaders.put(field.getKeyName(), field.getValue().formatAsString());
}
commandHeaders = Collections.unmodifiableMap(commandHeaders);
}
});

final JsonObject thingJson = JsonObject.of(thingTemplate);

thingJson.getValue(THING_ID)
Expand Down Expand Up @@ -140,6 +160,10 @@ public List<Adaptable> map(final ExternalMessage message) {
resolvedTemplate = thingTemplate;
}

if (Placeholders.containsAnyPlaceholder(thingTemplate)) {
commandHeaders = evaluateCommandHeaders(message, commandHeaders);
}

final Signal<CreateThing> createThing = getCreateThingSignal(message, resolvedTemplate);
final Adaptable adaptable = DITTO_PROTOCOL_ADAPTER.toAdaptable(createThing);

Expand All @@ -165,11 +189,26 @@ private Signal<CreateThing> getCreateThingSignal(final ExternalMessage message,
final String copyPolicyFrom = getCopyPolicyFrom(thingJson);
final DittoHeaders dittoHeaders = message.getInternalHeaders().toBuilder()
.contentType(DITTO_PROTOCOL_CONTENT_TYPE)
.responseRequired(false)
.putHeaders(commandHeaders)
.build();
return CreateThing.of(newThing, inlinePolicyJson, copyPolicyFrom, dittoHeaders);
}

private static Map<String, String> evaluateCommandHeaders(final ExternalMessage externalMessage,
final Map<String, String> errorResponseHeaders) {
final ExpressionResolver resolver =
getExpressionResolver(externalMessage.getHeaders());
final Map<String, String> resolvedHeaders = new HashMap<>();
errorResponseHeaders.forEach((key, value) ->
resolver.resolve(value).toOptional().ifPresent(resolvedHeaderValue ->
resolvedHeaders.put(key, resolvedHeaderValue)
)
);
// throws IllegalArgumentException or SubjectInvalidException
// if resolved headers are missing mandatory headers or the resolved subject is invalid.
return resolvedHeaders;
}

@Nullable
private static JsonObject createInlinePolicyJson(final JsonObject thingJson) {
return thingJson.getValue(Policy.INLINED_FIELD_NAME)
Expand All @@ -188,7 +227,8 @@ public List<ExternalMessage> map(final Adaptable adaptable) {
if (TopicPath.Criterion.ERRORS.equals(adaptable.getTopicPath().getCriterion())) {
final String jsonString = ProtocolFactory.wrapAsJsonifiableAdaptable(adaptable).toJsonString();
final boolean isResponse = adaptable.getPayload().getStatus().isPresent();
return Collections.singletonList(ExternalMessageFactory.newExternalMessageBuilder(Collections.emptyMap())

return Collections.singletonList(ExternalMessageFactory.newExternalMessageBuilder(commandHeaders)
.withTopicPath(adaptable.getTopicPath())
.withText(jsonString)
.asResponse(isResponse)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ public final class ImplicitThingCreationMessageMapperTest {
.build())
.build();

private static final JsonObject COMMAND_HEADERS = JsonObject.newBuilder()
.set("test-header", "this-is-a-test-header")
.set("other-test-header", "{{ header:gateway_id }}")
.set("empty-test-header", "{{ header:gateway_id | fn:filter(header:foobar, 'eq', 'bar') }}")
.build();

private static final JsonObject INITIAL_POLICY = Policy.newBuilder()
.forLabel("DEFAULT")
.setSubject(SubjectIssuer.INTEGRATION, "solutionId:connectionId")
Expand Down Expand Up @@ -108,10 +114,32 @@ public void setUp() {
underTest = new ImplicitThingCreationMessageMapper();
}

@Test
public void doForwardMappingContextWithCommandHeaderPlaceholder() {
final Map<String, String> headers = createValidHeaders();
underTest.configure(mappingConfig, createMapperConfig(THING_TEMPLATE, COMMAND_HEADERS));

final ExternalMessage externalMessage = ExternalMessageFactory.newExternalMessageBuilder(headers).build();
final List<Adaptable> mappingResult = underTest.map(externalMessage);

final Signal<?> firstMappedSignal = getFirstMappedSignal(mappingResult);
assertThat(firstMappedSignal).isInstanceOf(CreateThing.class);
final CreateThing createThing = (CreateThing) firstMappedSignal;

final Thing expectedThing = createExpectedThing(DEVICE_ID, DEVICE_ID, GATEWAY_ID);
assertThat(createThing.getThing().getEntityId()).isEqualTo(expectedThing.getEntityId());
assertThat(createThing.getThing().getPolicyEntityId()).isEmpty();
assertThat(createThing.getThing().getAttributes()).isEqualTo(expectedThing.getAttributes());
assertThat(createThing.getDittoHeaders().get("other-test-header")).isEqualTo(GATEWAY_ID);
assertThat(createThing.getDittoHeaders().get("test-header")).isEqualTo("this-is-a-test-header");
assertThat(createThing.getDittoHeaders().getContentType()).contains(DITTO_PROTOCOL_CONTENT_TYPE);
assertThat(createThing.getPolicyIdOrPlaceholder()).contains(GATEWAY_ID);
}

@Test
public void doForwardMappingContextWithDeviceIdPlaceholder() {
final Map<String, String> headers = createValidHeaders();
underTest.configure(mappingConfig, createMapperConfig(THING_TEMPLATE));
underTest.configure(mappingConfig, createMapperConfig(THING_TEMPLATE, COMMAND_HEADERS));

final ExternalMessage externalMessage = ExternalMessageFactory.newExternalMessageBuilder(headers).build();
final List<Adaptable> mappingResult = underTest.map(externalMessage);
Expand All @@ -131,7 +159,7 @@ public void doForwardMappingContextWithDeviceIdPlaceholder() {
@Test
public void doForwardMappingContextWithPolicyPlaceholder() {
final Map<String, String> headers = createValidHeaders();
underTest.configure(mappingConfig, createMapperConfig(THING_TEMPLATE_WITH_POLICY));
underTest.configure(mappingConfig, createMapperConfig(THING_TEMPLATE_WITH_POLICY, COMMAND_HEADERS));

final ExternalMessage externalMessage = ExternalMessageFactory.newExternalMessageBuilder(headers).build();
final List<Adaptable> mappingResult = underTest.map(externalMessage);
Expand All @@ -151,7 +179,7 @@ public void doForwardMappingContextWithPolicyPlaceholder() {

@Test
public void doForwardMappingTwice() {
underTest.configure(mappingConfig, createMapperConfig(THING_TEMPLATE_WITH_POLICY));
underTest.configure(mappingConfig, createMapperConfig(THING_TEMPLATE_WITH_POLICY, COMMAND_HEADERS));

final Map<String, String> headers1 = new HashMap<>();
headers1.put(HEADER_HONO_DEVICE_ID, "headerNamespace:headerDeviceId1");
Expand Down Expand Up @@ -196,7 +224,7 @@ public void doForwardMappingTwice() {
@Test
public void doForwardWithoutPlaceholders() {
final Map<String, String> headers = createValidHeaders();
underTest.configure(mappingConfig, createMapperConfig(THING_TEMPLATE_WITHOUT_PLACEHOLDERS));
underTest.configure(mappingConfig, createMapperConfig(THING_TEMPLATE_WITHOUT_PLACEHOLDERS, COMMAND_HEADERS));

final ExternalMessage externalMessage = ExternalMessageFactory.newExternalMessageBuilder(headers).build();
final List<Adaptable> mappingResult = underTest.map(externalMessage);
Expand All @@ -213,7 +241,8 @@ public void doForwardWithoutPlaceholders() {

@Test
public void throwErrorIfMappingConfigIsMissing() {
final DefaultMessageMapperConfiguration invalidMapperConfig = createMapperConfig(JsonObject.empty());
final DefaultMessageMapperConfiguration invalidMapperConfig = createMapperConfig(JsonObject.empty(),
COMMAND_HEADERS);

assertThatExceptionOfType(MessageMapperConfigurationInvalidException.class)
.isThrownBy(() -> underTest.configure(mappingConfig, invalidMapperConfig));
Expand All @@ -231,15 +260,16 @@ public void throwErrorIfThingIdIsMissingInConfig() {
.build())
.build();

final DefaultMessageMapperConfiguration invalidMapperConfig = createMapperConfig(templateMissingThingId);
final DefaultMessageMapperConfiguration invalidMapperConfig = createMapperConfig(templateMissingThingId,
COMMAND_HEADERS);

assertThatExceptionOfType(MessageMapperConfigurationInvalidException.class)
.isThrownBy(() -> underTest.configure(mappingConfig, invalidMapperConfig));
}

@Test
public void throwErrorIfHeaderForPlaceholderIsMissing() {
underTest.configure(mappingConfig, createMapperConfig(THING_TEMPLATE));
underTest.configure(mappingConfig, createMapperConfig(THING_TEMPLATE, COMMAND_HEADERS));

final Map<String, String> missingEntityHeader = new HashMap<>();
missingEntityHeader.put(HEADER_HONO_DEVICE_ID, DEVICE_ID);
Expand Down Expand Up @@ -277,8 +307,10 @@ private Thing createExpectedThing(final String thingId, final String policyId, f
"}");
}

private DefaultMessageMapperConfiguration createMapperConfig(final JsonValue thingTemplate) {
final Map<String, JsonValue> options = Collections.singletonMap("thing", thingTemplate);
private DefaultMessageMapperConfiguration createMapperConfig(final JsonValue thingTemplate, final JsonValue commandHeaders) {
final Map<String, JsonValue> options = new HashMap<>();
options.put("thing", thingTemplate);
options.put("commandHeaders", commandHeaders);
final Map<String, String> incomingConditions = Collections.singletonMap("implicitThingCreation",
"{{ header:hono_registration_status | fn:filter(header:hono_registration_status,'eq','NEW') }}");
return DefaultMessageMapperConfiguration.of("valid", options, incomingConditions, Collections.emptyMap());
Expand Down

0 comments on commit f6bd382

Please sign in to comment.