Skip to content

Commit

Permalink
Decoupe ConnectionsRoute from authorization
Browse files Browse the repository at this point in the history
Signed-off-by: Stanchev Aleksandar <aleksandar.stanchev@bosch.io>
  • Loading branch information
Stanchev Aleksandar committed Sep 21, 2022
1 parent 45611ad commit 5a3dc44
Show file tree
Hide file tree
Showing 16 changed files with 516 additions and 103 deletions.
Expand Up @@ -12,16 +12,9 @@
*/
package org.eclipse.ditto.connectivity.model.signals.commands.query;

import static org.eclipse.ditto.base.model.common.ConditionChecker.checkNotNull;

import java.time.Duration;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
Expand All @@ -31,16 +24,13 @@
import org.eclipse.ditto.base.model.json.JsonParsableCommand;
import org.eclipse.ditto.base.model.json.JsonSchemaVersion;
import org.eclipse.ditto.base.model.signals.commands.AbstractCommand;
import org.eclipse.ditto.connectivity.model.ConnectionId;
import org.eclipse.ditto.connectivity.model.signals.commands.ConnectivityCommand;
import org.eclipse.ditto.json.JsonArray;
import org.eclipse.ditto.json.JsonCollectors;
import org.eclipse.ditto.json.JsonFactory;
import org.eclipse.ditto.json.JsonField;
import org.eclipse.ditto.json.JsonFieldDefinition;
import org.eclipse.ditto.json.JsonObject;
import org.eclipse.ditto.json.JsonObjectBuilder;
import org.eclipse.ditto.json.JsonValue;
import org.eclipse.ditto.json.JsonPointer;

/**
* Command that retrieves several {@link org.eclipse.ditto.connectivity.model.Connection}s based on the passed in list
Expand All @@ -62,41 +52,31 @@ public final class RetrieveConnections extends AbstractCommand<RetrieveConnectio
* Type of this command.
*/
public static final String TYPE = ConnectivityCommand.TYPE_PREFIX + NAME;

static final JsonFieldDefinition<JsonArray> JSON_CONNECTION_IDS =
JsonFactory.newJsonArrayFieldDefinition("connectionIds", FieldType.REGULAR,
JsonSchemaVersion.V_2);
static final JsonFieldDefinition<Boolean> JSON_IDS_ONLY =
JsonFactory.newBooleanFieldDefinition("idsOnly", FieldType.REGULAR, JsonSchemaVersion.V_2);
static final JsonFieldDefinition<Long> JSON_DEFAULT_TIMEOUT =
JsonFactory.newLongFieldDefinition("timeoutMs", FieldType.REGULAR, JsonSchemaVersion.V_2);

private final Set<ConnectionId> connectionIds;
private final boolean idsOnly;

private final Duration timeout;

private RetrieveConnections(final Set<ConnectionId> connectionIds, final boolean idsOnly,
final Duration defaultTimeout, final DittoHeaders dittoHeaders) {
private RetrieveConnections(final boolean idsOnly, final Duration defaultTimeout, final DittoHeaders dittoHeaders) {
super(TYPE, dittoHeaders);
this.connectionIds = Collections.unmodifiableSet(connectionIds);
this.idsOnly = idsOnly;
this.timeout = dittoHeaders.getTimeout().orElse(defaultTimeout);
}

/**
* Returns a new instance of the retrieve connections command.
*
* @param connectionIds the IDs of the connections to be retrieved.
* @param dittoHeaders provide additional information regarding connections retrieval like a correlation ID.
* @return the instance.
* @throws NullPointerException if any argument is {@code null}.
*/
public static RetrieveConnections newInstance(final Collection<ConnectionId> connectionIds,
final boolean idsOnly, final Duration defaultTimeout, final DittoHeaders dittoHeaders) {
public static RetrieveConnections newInstance(final boolean idsOnly, final Duration defaultTimeout,
final DittoHeaders dittoHeaders) {

return new RetrieveConnections(new LinkedHashSet<>(checkNotNull(connectionIds, "connectionIds")),
idsOnly, defaultTimeout, dittoHeaders);
return new RetrieveConnections(idsOnly, defaultTimeout, dittoHeaders);
}

/**
Expand Down Expand Up @@ -125,19 +105,10 @@ public static RetrieveConnections fromJson(final String jsonString, final DittoH
* format.
*/
public static RetrieveConnections fromJson(final JsonObject jsonObject, final DittoHeaders dittoHeaders) {
final Set<ConnectionId> extractedConnectionIds = jsonObject.getValueOrThrow(JSON_CONNECTION_IDS).stream()
.filter(JsonValue::isString)
.map(JsonValue::asString)
.map(ConnectionId::of)
.collect(Collectors.toCollection(LinkedHashSet::new));
final boolean idsOnly = jsonObject.getValueOrThrow(JSON_IDS_ONLY);
final Duration defaultTimeout = Duration.ofMillis(jsonObject.getValueOrThrow(JSON_DEFAULT_TIMEOUT));

return new RetrieveConnections(extractedConnectionIds, idsOnly, defaultTimeout, dittoHeaders);
}

public Set<ConnectionId> getConnectionIds() {
return connectionIds;
return new RetrieveConnections(idsOnly, defaultTimeout, dittoHeaders);
}

public boolean getIdsOnly() {
Expand All @@ -155,22 +126,21 @@ public Category getCategory() {

@Override
public RetrieveConnections setDittoHeaders(final DittoHeaders dittoHeaders) {
return new RetrieveConnections(connectionIds, idsOnly, timeout, dittoHeaders);
return new RetrieveConnections(idsOnly, timeout, dittoHeaders);
}

@Override
public JsonPointer getResourcePath() {
return JsonPointer.of("/connections");
}

@Override
protected void appendPayload(final JsonObjectBuilder jsonObjectBuilder, final JsonSchemaVersion jsonSchemaVersion,
final Predicate<JsonField> thePredicate) {

final Predicate<JsonField> predicate = jsonSchemaVersion.and(thePredicate);
final JsonArray connectionIdsArray = connectionIds.stream()
.map(String::valueOf)
.map(JsonFactory::newValue)
.collect(JsonCollectors.valuesToArray());

jsonObjectBuilder.set(JSON_CONNECTION_IDS, connectionIdsArray, predicate);
jsonObjectBuilder.set(JSON_IDS_ONLY, idsOnly);
jsonObjectBuilder.set(JSON_DEFAULT_TIMEOUT, timeout.toMillis());
jsonObjectBuilder.set(JSON_IDS_ONLY, idsOnly, predicate);
jsonObjectBuilder.set(JSON_DEFAULT_TIMEOUT, timeout.toMillis(), predicate);
}

@Override
Expand All @@ -185,21 +155,19 @@ public boolean equals(@Nullable final Object o) {
return false;
}
final RetrieveConnections that = (RetrieveConnections) o;
return Objects.equals(connectionIds, that.connectionIds) &&
Objects.equals(idsOnly, that.idsOnly) &&
return Objects.equals(idsOnly, that.idsOnly) &&
Objects.equals(timeout, that.timeout);
}

@Override
public int hashCode() {
return Objects.hash(super.hashCode(), connectionIds, idsOnly, timeout);
return Objects.hash(super.hashCode(), idsOnly, timeout);
}

@Override
public String toString() {
return getClass().getSimpleName() + " [" +
super.toString() +
", connectionIds=" + connectionIds +
", idsOnly=" + idsOnly +
", timeout=" + timeout +
"]";
Expand Down
Expand Up @@ -42,6 +42,7 @@
import org.eclipse.ditto.json.JsonFieldSelector;
import org.eclipse.ditto.json.JsonObject;
import org.eclipse.ditto.json.JsonObjectBuilder;
import org.eclipse.ditto.json.JsonPointer;
import org.eclipse.ditto.json.JsonValue;

/**
Expand Down Expand Up @@ -202,6 +203,11 @@ public List<Connection> getConnections() {
.collect(Collectors.toList());
}

@Override
public JsonPointer getResourcePath() {
return JsonPointer.of("/connections");
}

@Override
public RetrieveConnectionsResponse setDittoHeaders(final DittoHeaders dittoHeaders) {
return of(connections, dittoHeaders);
Expand Down
@@ -0,0 +1,166 @@
/*
* Copyright (c) 2022 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
*/

/*
* Copyright Bosch.IO GmbH 2021
*
* All rights reserved, also regarding any disposal, exploitation,
* reproduction, editing, distribution, as well as in the event of
* applications for industrial property rights.
*
* This software is the confidential and proprietary information
* of Bosch.IO GmbH. You shall not disclose
* such Confidential Information and shall use it only in
* accordance with the terms of the license agreement you
* entered into with Bosch.IO GmbH.
*/
package org.eclipse.ditto.connectivity.model.signals.commands.query;

import static org.assertj.core.api.Assertions.assertThatNullPointerException;
import static org.eclipse.ditto.json.assertions.DittoJsonAssertions.assertThat;
import static org.mutabilitydetector.unittesting.AllowedReason.provided;
import static org.mutabilitydetector.unittesting.MutabilityAssert.assertInstancesOf;
import static org.mutabilitydetector.unittesting.MutabilityMatchers.areImmutable;

import java.util.Collections;
import java.util.List;

import org.eclipse.ditto.base.model.auth.AuthorizationContext;
import org.eclipse.ditto.base.model.auth.AuthorizationSubject;
import org.eclipse.ditto.base.model.auth.DittoAuthorizationContextType;
import org.eclipse.ditto.base.model.common.HttpStatus;
import org.eclipse.ditto.base.model.headers.DittoHeaders;
import org.eclipse.ditto.base.model.json.FieldType;
import org.eclipse.ditto.base.model.signals.Signal;
import org.eclipse.ditto.base.model.signals.commands.GlobalCommandResponseRegistry;
import org.eclipse.ditto.connectivity.model.Connection;
import org.eclipse.ditto.connectivity.model.ConnectionId;
import org.eclipse.ditto.connectivity.model.ConnectionType;
import org.eclipse.ditto.connectivity.model.ConnectivityModelFactory;
import org.eclipse.ditto.connectivity.model.ConnectivityStatus;
import org.eclipse.ditto.connectivity.model.Topic;
import org.eclipse.ditto.connectivity.model.signals.commands.ConnectivityCommandResponse;
import org.eclipse.ditto.json.JsonArray;
import org.eclipse.ditto.json.JsonObject;
import org.eclipse.ditto.json.JsonPointer;
import org.junit.Test;

import nl.jqno.equalsverifier.EqualsVerifier;

/**
* Unit test for {@link RetrieveConnectionsResponse}.
*/
public final class RetrieveConnectionsResponseTest {

private static final DittoHeaders DITTO_HEADERS = DittoHeaders.empty();
private static final Connection CONNECTION = createConnection(ConnectionId.of("uuid-1"));

private static final JsonObject KNOWN_JSON = JsonObject.newBuilder()
.set(ConnectivityCommandResponse.JsonFields.TYPE, RetrieveConnectionsResponse.TYPE)
.set(ConnectivityCommandResponse.JsonFields.STATUS, HttpStatus.OK.getCode())
.set(RetrieveConnectionsResponse.JSON_CONNECTIONS, JsonArray.of(CONNECTION.toJson()))
.build();

@Test
public void assertImmutability() {
assertInstancesOf(RetrieveConnectionsResponse.class,
areImmutable(),
provided(JsonArray.class).isAlsoImmutable());
}

@Test
public void testHashCodeAndEquals() {
EqualsVerifier.forClass(RetrieveConnectionsResponse.class)
.withRedefinedSuperclass()
.usingGetClass()
.verify();
}

@Test
public void tryToCreateInstanceWithNullThings() {
assertThatNullPointerException()
.isThrownBy(() -> RetrieveConnectionsResponse.of(null, FieldType.notHidden(), DITTO_HEADERS))
.withMessage("The Connections must not be null!");
}

@Test
public void tryToCreateInstanceWithNullJsonArray() {
assertThatNullPointerException()
.isThrownBy(() -> RetrieveConnectionsResponse.of((JsonArray) null, DITTO_HEADERS))
.withMessage("The Connections must not be null!");
}

@Test
public void toJsonReturnsExpected() {
final RetrieveConnectionsResponse underTest = RetrieveConnectionsResponse.of(Collections.singletonList(CONNECTION),
FieldType.notHidden(),
DITTO_HEADERS);
final JsonObject actualJson = underTest.toJson();

assertThat(actualJson).isEqualTo(KNOWN_JSON);
}

@Test
public void getResourcePathReturnsExpected() {
final JsonPointer expectedResourcePath = JsonPointer.of("/connections");

final RetrieveConnectionsResponse underTest = RetrieveConnectionsResponse.of(Collections.singletonList(CONNECTION),
FieldType.notHidden(),
DITTO_HEADERS);

assertThat(underTest.getResourcePath()).isEqualTo(expectedResourcePath);
}

@Test
public void createInstanceFromValidJson() {
final RetrieveConnectionsResponse underTest = RetrieveConnectionsResponse.fromJson(KNOWN_JSON.toString(), DITTO_HEADERS);

assertThat(underTest).isNotNull();

List<Connection> connections = underTest.getConnections();

assertThat(connections).hasSize(1);
assertThat(connections.get(0).toJson()).isEqualTo(CONNECTION.toJson());
}

@Test
public void deserializeRetrieveConnectionsResponse() {
final GlobalCommandResponseRegistry globalCommandResponseRegistry = GlobalCommandResponseRegistry.getInstance();
final Signal<?> actual = globalCommandResponseRegistry.parse(RetrieveConnectionsResponseTest.KNOWN_JSON,
RetrieveConnectionsResponseTest.DITTO_HEADERS);
final Signal<?> expected =
RetrieveConnectionsResponse.of(Collections.singletonList(RetrieveConnectionsResponseTest.CONNECTION),
FieldType.notHidden(),
RetrieveConnectionsResponseTest.DITTO_HEADERS);

assertThat(actual).isEqualTo(expected);
}

private static Connection createConnection(final ConnectionId id) {
final String subjectId = "integration:mySolution:test";
final AuthorizationContext authorizationContext = AuthorizationContext.newInstance(DittoAuthorizationContextType.UNSPECIFIED,
AuthorizationSubject.newInstance(subjectId));
return ConnectivityModelFactory.newConnectionBuilder(id,
ConnectionType.AMQP_091,
ConnectivityStatus.OPEN,
"amqp://user:password@host:1234")
.name("myConnection")
.targets(Collections.singletonList(ConnectivityModelFactory.newTargetBuilder()
.address("test")
.authorizationContext(authorizationContext)
.topics(Topic.TWIN_EVENTS)
.build()))
.build();
}

}

0 comments on commit 5a3dc44

Please sign in to comment.