Skip to content

Commit

Permalink
Issue #559: Extracted class for building DittoHeaders from options fo…
Browse files Browse the repository at this point in the history
…r particular outgoing message.

Signed-off-by: Juergen Fickel <juergen.fickel@bosch.io>
  • Loading branch information
Juergen Fickel committed Sep 13, 2021
1 parent a6e9cd6 commit d61b240
Show file tree
Hide file tree
Showing 3 changed files with 337 additions and 37 deletions.
@@ -0,0 +1,149 @@
/*
* 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.client.internal;

import java.text.MessageFormat;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Locale;
import java.util.Optional;
import java.util.Set;

import org.eclipse.ditto.base.model.common.ConditionChecker;
import org.eclipse.ditto.base.model.headers.DittoHeaders;
import org.eclipse.ditto.base.model.headers.DittoHeadersBuilder;
import org.eclipse.ditto.base.model.headers.entitytag.EntityTagMatcher;
import org.eclipse.ditto.base.model.headers.entitytag.EntityTagMatchers;
import org.eclipse.ditto.base.model.json.JsonSchemaVersion;
import org.eclipse.ditto.client.options.Option;
import org.eclipse.ditto.client.options.OptionName;
import org.eclipse.ditto.client.options.internal.OptionsEvaluator;

/**
* This class provides the means to build {@link DittoHeaders} for a particular outgoing message.
*/
final class OptionsToDittoHeaders {

private static final EntityTagMatchers ASTERISK_ENTITY_TAG_MATCHER =
EntityTagMatchers.fromList(Collections.singletonList(EntityTagMatcher.asterisk()));

private final JsonSchemaVersion jsonSchemaVersion;
private final Set<? extends OptionName> allowedOptions;
private final OptionsEvaluator.Global globalOptionsEvaluator;
private final OptionsEvaluator.Modify modifyOptionsEvaluator;

private final DittoHeadersBuilder<?, ?> headersBuilder;

private OptionsToDittoHeaders(final JsonSchemaVersion jsonSchemaVersion,
final Collection<? extends OptionName> allowedOptions,
final OptionsEvaluator.Global globalOptionsEvaluator,
final OptionsEvaluator.Modify modifyOptionsEvaluator) {

this.jsonSchemaVersion = jsonSchemaVersion;
this.allowedOptions = Collections.unmodifiableSet(new HashSet<>(allowedOptions));
this.globalOptionsEvaluator = globalOptionsEvaluator;
this.modifyOptionsEvaluator = modifyOptionsEvaluator;

headersBuilder = DittoHeaders.newBuilder();
}

/**
* Returns {@link DittoHeaders} that are based on the specified arguments.
*
* @param schemaVersion the JSON schema version to be used.
* @param allowedOptions the options that are allowed for a particular outgoing message.
* @param options the user provided options.
* @return the {@code DittoHeaders}.
* @throws NullPointerException if any argument is {@code null}.
*/
static DittoHeaders getDittoHeaders(final JsonSchemaVersion schemaVersion,
final Collection<? extends OptionName> allowedOptions,
final Option<?>[] options) {

final OptionsToDittoHeaders optionsToDittoHeaders = new OptionsToDittoHeaders(
ConditionChecker.checkNotNull(schemaVersion, "schemaVersion"),
ConditionChecker.checkNotNull(allowedOptions, "allowedOptions"),
OptionsEvaluator.forGlobalOptions(options),
OptionsEvaluator.forModifyOptions(options)
);
return optionsToDittoHeaders.getDittoHeaders();
}

private DittoHeaders getDittoHeaders() {
final DittoHeaders additionalHeaders = getAdditionalHeaders();
putAdditionalHeaders(additionalHeaders);
setRandomCorrelationIdIfMissing(additionalHeaders);
setSchemaVersion();
setResponseRequired();
setEntityTagMatchers();
setCondition();
return buildDittoHeaders();
}

private void putAdditionalHeaders(final DittoHeaders additionalHeaders) {
headersBuilder.putHeaders(additionalHeaders);
}

private DittoHeaders getAdditionalHeaders() {
return globalOptionsEvaluator.getDittoHeaders().orElseGet(DittoHeaders::empty);
}

private void setRandomCorrelationIdIfMissing(final DittoHeaders additionalHeaders) {
final Optional<String> correlationIdOptional = additionalHeaders.getCorrelationId();
if (!correlationIdOptional.isPresent()) {
headersBuilder.randomCorrelationId();
}
}

private void setSchemaVersion() {
headersBuilder.schemaVersion(jsonSchemaVersion);
}

private void setResponseRequired() {
headersBuilder.responseRequired(modifyOptionsEvaluator.isResponseRequired().orElse(true));
}

private void setEntityTagMatchers() {
modifyOptionsEvaluator.exists()
.ifPresent(exists -> {
validateIfOptionIsAllowed(OptionName.Modify.EXISTS);
if (exists) {
headersBuilder.ifMatch(ASTERISK_ENTITY_TAG_MATCHER);
} else {
headersBuilder.ifNoneMatch(ASTERISK_ENTITY_TAG_MATCHER);
}
});
}

private void validateIfOptionIsAllowed(final OptionName option) {
if (!allowedOptions.contains(option)) {
final String pattern = "Option ''{0}'' is not allowed for this operation.";
final String lowerCaseOptionName = option.toString().toLowerCase(Locale.ENGLISH);
throw new IllegalArgumentException(MessageFormat.format(pattern, lowerCaseOptionName));
}
}

private void setCondition() {
modifyOptionsEvaluator.condition()
.ifPresent(condition -> {
validateIfOptionIsAllowed(OptionName.Modify.CONDITION);
headersBuilder.condition(condition);
});
}

private DittoHeaders buildDittoHeaders() {
return headersBuilder.build();
}

}
Expand Up @@ -18,21 +18,18 @@

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.UUID;
import java.util.function.Supplier;

import javax.annotation.Nullable;

import org.eclipse.ditto.base.model.headers.DittoHeaders;
import org.eclipse.ditto.base.model.headers.DittoHeadersBuilder;
import org.eclipse.ditto.base.model.headers.entitytag.EntityTagMatcher;
import org.eclipse.ditto.base.model.headers.entitytag.EntityTagMatchers;
import org.eclipse.ditto.base.model.json.JsonSchemaVersion;
Expand Down Expand Up @@ -595,40 +592,7 @@ public <T> Message<T> sendMessage(final MessageSerializerRegistry registry, fina
private DittoHeaders buildDittoHeaders(final Collection<? extends OptionName> allowedOptions,
final Option<?>... options) {

final OptionsEvaluator.Global global = OptionsEvaluator.forGlobalOptions(options);
final OptionsEvaluator.Modify modify = OptionsEvaluator.forModifyOptions(options);

final DittoHeaders additionalHeaders = global.getDittoHeaders().orElseGet(DittoHeaders::empty);
final DittoHeadersBuilder<?, ?> headersBuilder = DittoHeaders.newBuilder(additionalHeaders)
.correlationId(additionalHeaders.getCorrelationId()
.orElseGet(() -> UUID.randomUUID().toString()))
.schemaVersion(jsonSchemaVersion)
.responseRequired(modify.isResponseRequired().orElse(true));

modify.exists().ifPresent(exists -> {
validateIfOptionIsAllowed(EXISTS, allowedOptions);
if (Boolean.TRUE.equals(exists)) {
headersBuilder.ifMatch(ASTERISK);
} else {
headersBuilder.ifNoneMatch(ASTERISK);
}
});
modify.condition().ifPresent(condition -> {
validateIfOptionIsAllowed(CONDITION, allowedOptions);
headersBuilder.condition(condition);
});

return headersBuilder.build();
}

private static void validateIfOptionIsAllowed(final OptionName option,
final Collection<? extends OptionName> allowedOptions) {

if (!allowedOptions.contains(option)) {
final String pattern = "Option ''{0}'' is not allowed for this operation.";
final String lowerCaseOptionName = option.toString().toLowerCase(Locale.ENGLISH);
throw new IllegalArgumentException(MessageFormat.format(pattern, lowerCaseOptionName));
}
return OptionsToDittoHeaders.getDittoHeaders(jsonSchemaVersion, allowedOptions, options);
}

/**
Expand Down

0 comments on commit d61b240

Please sign in to comment.