Skip to content

Commit

Permalink
Client-side of the Core uP-L3 service UTwin. (#161)
Browse files Browse the repository at this point in the history
The implementation uses the uP-L2 RpcClient interface to communicate with the UTwin service.

#160
  • Loading branch information
Steven Hartley committed Jul 25, 2024
1 parent 34948dc commit 3bf0c15
Show file tree
Hide file tree
Showing 4 changed files with 267 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
* SPDX-FileCopyrightText: 2024 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 Apache License Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.eclipse.uprotocol.client.utwin.v2;

import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import org.eclipse.uprotocol.communication.CallOptions;
import org.eclipse.uprotocol.communication.RpcClient;
import org.eclipse.uprotocol.communication.RpcMapper;
import org.eclipse.uprotocol.communication.UPayload;
import org.eclipse.uprotocol.communication.UStatusException;
import org.eclipse.uprotocol.core.utwin.v2.GetLastMessagesRequest;
import org.eclipse.uprotocol.core.utwin.v2.GetLastMessagesResponse;
import org.eclipse.uprotocol.core.utwin.v2.UTwinProto;
import org.eclipse.uprotocol.uri.factory.UriFactory;
import org.eclipse.uprotocol.v1.UCode;
import org.eclipse.uprotocol.v1.UStatus;
import org.eclipse.uprotocol.v1.UUri;
import org.eclipse.uprotocol.v1.UUriBatch;

import com.google.protobuf.Descriptors.ServiceDescriptor;

/**
* The uTwin client implementation using the RpcClient uP-L2 communication layer interface.
*/
public class SimpleUTwinClient implements UTwinClient {
private final RpcClient rpcClient;

private static final ServiceDescriptor UTWIN = UTwinProto.getDescriptor().getServices().get(0);

// TODO: The following items eventually need to be pulled from generated code
private static final UUri GETLASTMESSAGE_METHOD = UriFactory.fromProto(UTWIN, 1);


/**
* Create a new instance of the uTwin client passing in the RPCClient to use for communication.
*
* @param rpcClient The RPC client to use for communication.
*/
public SimpleUTwinClient(RpcClient rpcClient) {
this.rpcClient = rpcClient;
}


/**
* Fetch the last messages for a batch of topics.
*
* @param topics {@link UUriBatch} batch of 1 or more topics to fetch the last messages for.
* @param options The call options.
* @return CompletionStage completes successfully with {@link GetLastMessagesResponse} if uTwin was able
* to fetch the topics or completes exceptionally with {@link UStatus} with the failure reason.
* such as {@code UCode.NOT_FOUND}, {@code UCode.PERMISSION_DENIED} etc...
*/
@Override
public CompletionStage<GetLastMessagesResponse> getLastMessages(UUriBatch topics, CallOptions options) {
Objects.requireNonNull(topics, "topics must not be null");
Objects.requireNonNull(options, "options must not be null");

// Check if topics is empty
if (topics.equals(UUriBatch.getDefaultInstance())) {
return CompletableFuture.failedFuture(
new UStatusException(UCode.INVALID_ARGUMENT, "topics must not be empty"));
}

GetLastMessagesRequest request = GetLastMessagesRequest.newBuilder().setTopics(topics).build();
return RpcMapper.mapResponse(rpcClient.invokeMethod(
GETLASTMESSAGE_METHOD, UPayload.pack(request), options), GetLastMessagesResponse.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* SPDX-FileCopyrightText: 2024 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 Apache License Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.eclipse.uprotocol.client.utwin.v2;

import java.util.concurrent.CompletionStage;

import org.eclipse.uprotocol.communication.CallOptions;
import org.eclipse.uprotocol.core.utwin.v2.GetLastMessagesResponse;
import org.eclipse.uprotocol.v1.UUriBatch;

/**
* The uTwin client-side interface.
*
* UTwin is used to fetch the last published message for a given topic. This is the client-side of the
* UTwin Service contract and communicates with a local uTwin service to fetch the last message for a given topic.
*/
public interface UTwinClient {
/**
* Fetch the last messages for a batch of topics.
*
* @param topics {@link UUriBatch} batch of 1 or more topics to fetch the last messages for.
* @param options The call options.
* @return CompletionStage completes successfully with {@link GetLastMessagesResponse} if uTwin was able
* to fetch the topics or completes exceptionally with {@link UStatus} with the failure reason.
* such as {@code UCode.NOT_FOUND}, {@code UCode.PERMISSION_DENIED} etc...
*/
CompletionStage<GetLastMessagesResponse> getLastMessages(UUriBatch topics, CallOptions options);


/**
* Fetch the last messages for a batch of topics.
*
* @param topics {@link UUriBatch} batch of 1 or more topics to fetch the last messages for.
* @return CompletionStage completes successfully with {@link GetLastMessagesResponse} if uTwin was able
* to fetch the topics or completes exceptionally with {@link UStatus} with the failure reason.
* such as {@code UCode.NOT_FOUND}, {@code UCode.PERMISSION_DENIED} etc...
*/
default CompletionStage<GetLastMessagesResponse> getLastMessages(UUriBatch topics) {
return getLastMessages(topics, CallOptions.DEFAULT);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Executors;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutionException;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/**
* SPDX-FileCopyrightText: 2024 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 Apache License Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*/

package org.eclipse.uprotocol.client.utwin.v2;

import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;

import org.eclipse.uprotocol.communication.RpcClient;
import org.eclipse.uprotocol.communication.UPayload;
import org.eclipse.uprotocol.communication.UStatusException;
import org.eclipse.uprotocol.core.utwin.v2.GetLastMessagesResponse;
import org.eclipse.uprotocol.transport.UTransport;
import org.eclipse.uprotocol.v1.UCode;
import org.eclipse.uprotocol.v1.UUri;
import org.eclipse.uprotocol.v1.UUriBatch;

/**
* The uTwin client implementation using RpcClient uP-L2 communication layer interface.
* This is the test code for said implementation.
*/
@ExtendWith(MockitoExtension.class)
public class SimpleUTwinClientTest {
@Mock
private UTransport transport;


private final UUri topic = UUri.newBuilder().setAuthorityName("hartley").setUeId(3)
.setUeVersionMajor(1).setResourceId(0x8000).build();


@BeforeEach
public void setup() {
transport = mock(UTransport.class);
}


@Test
@DisplayName("Test calling getLastMessages() with valid topics")
void testGetLastMessages() {

RpcClient rpcClient = Mockito.mock(RpcClient.class);

UUriBatch topics = UUriBatch.newBuilder().addUris(topic).build();

when(rpcClient.invokeMethod(any(), any(), any())).thenReturn(
CompletableFuture.completedFuture(UPayload.pack(GetLastMessagesResponse.getDefaultInstance())));

SimpleUTwinClient client = new SimpleUTwinClient(rpcClient);
CompletionStage<GetLastMessagesResponse> response = client.getLastMessages(topics);
assertNotNull(response);
assertFalse(response.toCompletableFuture().isCompletedExceptionally());
assertDoesNotThrow(() -> response.toCompletableFuture().get());
}


@Test
@DisplayName("Test calling getLastMessages() with empty topics")
void testGetLastMessagesEmptyTopics() {
RpcClient rpcClient = Mockito.mock(RpcClient.class);

UUriBatch topics = UUriBatch.getDefaultInstance();

SimpleUTwinClient client = new SimpleUTwinClient(rpcClient);
CompletionStage<GetLastMessagesResponse> response = client.getLastMessages(topics);
assertNotNull(response);
assertTrue(response.toCompletableFuture().isCompletedExceptionally());
assertDoesNotThrow(() -> {
response
.handle((r, e) -> {
assertNotNull(e);
assertEquals(((UStatusException)e).getCode(), UCode.INVALID_ARGUMENT);
assertEquals(((UStatusException)e).getMessage(), "topics must not be empty");
return r;
})
.toCompletableFuture().get();
});
}


@Test
@DisplayName("Test calling getLastMessages() when the RpcClient completes exceptionally")
void testGetLastMessagesException() {
RpcClient rpcClient = Mockito.mock(RpcClient.class);

UUriBatch topics = UUriBatch.newBuilder().addUris(topic).build();

when(rpcClient.invokeMethod(any(), any(), any())).thenReturn(
CompletableFuture.failedFuture(new UStatusException(UCode.NOT_FOUND, "Not found")));

SimpleUTwinClient client = new SimpleUTwinClient(rpcClient);
CompletionStage<GetLastMessagesResponse> response = client.getLastMessages(topics);
assertNotNull(response);
assertTrue(response.toCompletableFuture().isCompletedExceptionally());
assertDoesNotThrow(() -> {
response
.handle((r, e) -> {
assertNotNull(e);
UStatusException t = (UStatusException) e.getCause();
assertNotNull(t);
assertEquals(t.getCode(), UCode.NOT_FOUND);
assertEquals(t.getMessage(), "Not found");
return r;
})
.toCompletableFuture().get();
});
}

}

0 comments on commit 3bf0c15

Please sign in to comment.