Skip to content

Commit 3bf0c15

Browse files
author
Steven Hartley
authored
Client-side of the Core uP-L3 service UTwin. (#161)
The implementation uses the uP-L2 RpcClient interface to communicate with the UTwin service. #160
1 parent 34948dc commit 3bf0c15

File tree

4 files changed

+267
-2
lines changed

4 files changed

+267
-2
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/**
2+
* SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information regarding copyright ownership.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Apache License Version 2.0 which is available at
9+
* https://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* SPDX-License-Identifier: Apache-2.0
12+
*/
13+
package org.eclipse.uprotocol.client.utwin.v2;
14+
15+
import java.util.Objects;
16+
import java.util.concurrent.CompletableFuture;
17+
import java.util.concurrent.CompletionStage;
18+
import org.eclipse.uprotocol.communication.CallOptions;
19+
import org.eclipse.uprotocol.communication.RpcClient;
20+
import org.eclipse.uprotocol.communication.RpcMapper;
21+
import org.eclipse.uprotocol.communication.UPayload;
22+
import org.eclipse.uprotocol.communication.UStatusException;
23+
import org.eclipse.uprotocol.core.utwin.v2.GetLastMessagesRequest;
24+
import org.eclipse.uprotocol.core.utwin.v2.GetLastMessagesResponse;
25+
import org.eclipse.uprotocol.core.utwin.v2.UTwinProto;
26+
import org.eclipse.uprotocol.uri.factory.UriFactory;
27+
import org.eclipse.uprotocol.v1.UCode;
28+
import org.eclipse.uprotocol.v1.UStatus;
29+
import org.eclipse.uprotocol.v1.UUri;
30+
import org.eclipse.uprotocol.v1.UUriBatch;
31+
32+
import com.google.protobuf.Descriptors.ServiceDescriptor;
33+
34+
/**
35+
* The uTwin client implementation using the RpcClient uP-L2 communication layer interface.
36+
*/
37+
public class SimpleUTwinClient implements UTwinClient {
38+
private final RpcClient rpcClient;
39+
40+
private static final ServiceDescriptor UTWIN = UTwinProto.getDescriptor().getServices().get(0);
41+
42+
// TODO: The following items eventually need to be pulled from generated code
43+
private static final UUri GETLASTMESSAGE_METHOD = UriFactory.fromProto(UTWIN, 1);
44+
45+
46+
/**
47+
* Create a new instance of the uTwin client passing in the RPCClient to use for communication.
48+
*
49+
* @param rpcClient The RPC client to use for communication.
50+
*/
51+
public SimpleUTwinClient(RpcClient rpcClient) {
52+
this.rpcClient = rpcClient;
53+
}
54+
55+
56+
/**
57+
* Fetch the last messages for a batch of topics.
58+
*
59+
* @param topics {@link UUriBatch} batch of 1 or more topics to fetch the last messages for.
60+
* @param options The call options.
61+
* @return CompletionStage completes successfully with {@link GetLastMessagesResponse} if uTwin was able
62+
* to fetch the topics or completes exceptionally with {@link UStatus} with the failure reason.
63+
* such as {@code UCode.NOT_FOUND}, {@code UCode.PERMISSION_DENIED} etc...
64+
*/
65+
@Override
66+
public CompletionStage<GetLastMessagesResponse> getLastMessages(UUriBatch topics, CallOptions options) {
67+
Objects.requireNonNull(topics, "topics must not be null");
68+
Objects.requireNonNull(options, "options must not be null");
69+
70+
// Check if topics is empty
71+
if (topics.equals(UUriBatch.getDefaultInstance())) {
72+
return CompletableFuture.failedFuture(
73+
new UStatusException(UCode.INVALID_ARGUMENT, "topics must not be empty"));
74+
}
75+
76+
GetLastMessagesRequest request = GetLastMessagesRequest.newBuilder().setTopics(topics).build();
77+
return RpcMapper.mapResponse(rpcClient.invokeMethod(
78+
GETLASTMESSAGE_METHOD, UPayload.pack(request), options), GetLastMessagesResponse.class);
79+
}
80+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/**
2+
* SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information regarding copyright ownership.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Apache License Version 2.0 which is available at
9+
* https://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* SPDX-License-Identifier: Apache-2.0
12+
*/
13+
package org.eclipse.uprotocol.client.utwin.v2;
14+
15+
import java.util.concurrent.CompletionStage;
16+
17+
import org.eclipse.uprotocol.communication.CallOptions;
18+
import org.eclipse.uprotocol.core.utwin.v2.GetLastMessagesResponse;
19+
import org.eclipse.uprotocol.v1.UUriBatch;
20+
21+
/**
22+
* The uTwin client-side interface.
23+
*
24+
* UTwin is used to fetch the last published message for a given topic. This is the client-side of the
25+
* UTwin Service contract and communicates with a local uTwin service to fetch the last message for a given topic.
26+
27+
*/
28+
public interface UTwinClient {
29+
/**
30+
* Fetch the last messages for a batch of topics.
31+
*
32+
* @param topics {@link UUriBatch} batch of 1 or more topics to fetch the last messages for.
33+
* @param options The call options.
34+
* @return CompletionStage completes successfully with {@link GetLastMessagesResponse} if uTwin was able
35+
* to fetch the topics or completes exceptionally with {@link UStatus} with the failure reason.
36+
* such as {@code UCode.NOT_FOUND}, {@code UCode.PERMISSION_DENIED} etc...
37+
*/
38+
CompletionStage<GetLastMessagesResponse> getLastMessages(UUriBatch topics, CallOptions options);
39+
40+
41+
/**
42+
* Fetch the last messages for a batch of topics.
43+
*
44+
* @param topics {@link UUriBatch} batch of 1 or more topics to fetch the last messages for.
45+
* @return CompletionStage completes successfully with {@link GetLastMessagesResponse} if uTwin was able
46+
* to fetch the topics or completes exceptionally with {@link UStatus} with the failure reason.
47+
* such as {@code UCode.NOT_FOUND}, {@code UCode.PERMISSION_DENIED} etc...
48+
*/
49+
default CompletionStage<GetLastMessagesResponse> getLastMessages(UUriBatch topics) {
50+
return getLastMessages(topics, CallOptions.DEFAULT);
51+
}
52+
}

src/test/java/org/eclipse/uprotocol/client/usubscription/v3/InMemoryUSubscriptionClientTest.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@
1818
import java.util.concurrent.CompletionStage;
1919
import java.util.concurrent.Executors;
2020
import java.util.concurrent.CyclicBarrier;
21-
import java.util.concurrent.ExecutionException;
22-
2321
import org.junit.jupiter.api.BeforeEach;
2422
import org.junit.jupiter.api.DisplayName;
2523
import org.junit.jupiter.api.Test;
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/**
2+
* SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information regarding copyright ownership.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Apache License Version 2.0 which is available at
9+
* https://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* SPDX-License-Identifier: Apache-2.0
12+
*/
13+
14+
package org.eclipse.uprotocol.client.utwin.v2;
15+
16+
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
17+
import static org.junit.jupiter.api.Assertions.assertEquals;
18+
import static org.junit.jupiter.api.Assertions.assertFalse;
19+
import static org.junit.jupiter.api.Assertions.assertNotNull;
20+
import static org.junit.jupiter.api.Assertions.assertTrue;
21+
import static org.mockito.ArgumentMatchers.any;
22+
import static org.mockito.Mockito.mock;
23+
import static org.mockito.Mockito.when;
24+
25+
import java.util.concurrent.CompletableFuture;
26+
import java.util.concurrent.CompletionStage;
27+
import org.junit.jupiter.api.BeforeEach;
28+
import org.junit.jupiter.api.DisplayName;
29+
import org.junit.jupiter.api.Test;
30+
import org.junit.jupiter.api.extension.ExtendWith;
31+
import org.mockito.Mock;
32+
import org.mockito.Mockito;
33+
import org.mockito.junit.jupiter.MockitoExtension;
34+
35+
import org.eclipse.uprotocol.communication.RpcClient;
36+
import org.eclipse.uprotocol.communication.UPayload;
37+
import org.eclipse.uprotocol.communication.UStatusException;
38+
import org.eclipse.uprotocol.core.utwin.v2.GetLastMessagesResponse;
39+
import org.eclipse.uprotocol.transport.UTransport;
40+
import org.eclipse.uprotocol.v1.UCode;
41+
import org.eclipse.uprotocol.v1.UUri;
42+
import org.eclipse.uprotocol.v1.UUriBatch;
43+
44+
/**
45+
* The uTwin client implementation using RpcClient uP-L2 communication layer interface.
46+
* This is the test code for said implementation.
47+
*/
48+
@ExtendWith(MockitoExtension.class)
49+
public class SimpleUTwinClientTest {
50+
@Mock
51+
private UTransport transport;
52+
53+
54+
private final UUri topic = UUri.newBuilder().setAuthorityName("hartley").setUeId(3)
55+
.setUeVersionMajor(1).setResourceId(0x8000).build();
56+
57+
58+
@BeforeEach
59+
public void setup() {
60+
transport = mock(UTransport.class);
61+
}
62+
63+
64+
@Test
65+
@DisplayName("Test calling getLastMessages() with valid topics")
66+
void testGetLastMessages() {
67+
68+
RpcClient rpcClient = Mockito.mock(RpcClient.class);
69+
70+
UUriBatch topics = UUriBatch.newBuilder().addUris(topic).build();
71+
72+
when(rpcClient.invokeMethod(any(), any(), any())).thenReturn(
73+
CompletableFuture.completedFuture(UPayload.pack(GetLastMessagesResponse.getDefaultInstance())));
74+
75+
SimpleUTwinClient client = new SimpleUTwinClient(rpcClient);
76+
CompletionStage<GetLastMessagesResponse> response = client.getLastMessages(topics);
77+
assertNotNull(response);
78+
assertFalse(response.toCompletableFuture().isCompletedExceptionally());
79+
assertDoesNotThrow(() -> response.toCompletableFuture().get());
80+
}
81+
82+
83+
@Test
84+
@DisplayName("Test calling getLastMessages() with empty topics")
85+
void testGetLastMessagesEmptyTopics() {
86+
RpcClient rpcClient = Mockito.mock(RpcClient.class);
87+
88+
UUriBatch topics = UUriBatch.getDefaultInstance();
89+
90+
SimpleUTwinClient client = new SimpleUTwinClient(rpcClient);
91+
CompletionStage<GetLastMessagesResponse> response = client.getLastMessages(topics);
92+
assertNotNull(response);
93+
assertTrue(response.toCompletableFuture().isCompletedExceptionally());
94+
assertDoesNotThrow(() -> {
95+
response
96+
.handle((r, e) -> {
97+
assertNotNull(e);
98+
assertEquals(((UStatusException)e).getCode(), UCode.INVALID_ARGUMENT);
99+
assertEquals(((UStatusException)e).getMessage(), "topics must not be empty");
100+
return r;
101+
})
102+
.toCompletableFuture().get();
103+
});
104+
}
105+
106+
107+
@Test
108+
@DisplayName("Test calling getLastMessages() when the RpcClient completes exceptionally")
109+
void testGetLastMessagesException() {
110+
RpcClient rpcClient = Mockito.mock(RpcClient.class);
111+
112+
UUriBatch topics = UUriBatch.newBuilder().addUris(topic).build();
113+
114+
when(rpcClient.invokeMethod(any(), any(), any())).thenReturn(
115+
CompletableFuture.failedFuture(new UStatusException(UCode.NOT_FOUND, "Not found")));
116+
117+
SimpleUTwinClient client = new SimpleUTwinClient(rpcClient);
118+
CompletionStage<GetLastMessagesResponse> response = client.getLastMessages(topics);
119+
assertNotNull(response);
120+
assertTrue(response.toCompletableFuture().isCompletedExceptionally());
121+
assertDoesNotThrow(() -> {
122+
response
123+
.handle((r, e) -> {
124+
assertNotNull(e);
125+
UStatusException t = (UStatusException) e.getCause();
126+
assertNotNull(t);
127+
assertEquals(t.getCode(), UCode.NOT_FOUND);
128+
assertEquals(t.getMessage(), "Not found");
129+
return r;
130+
})
131+
.toCompletableFuture().get();
132+
});
133+
}
134+
135+
}

0 commit comments

Comments
 (0)