From fd19478ed667aa381e9e57ac656f4e3c218bca92 Mon Sep 17 00:00:00 2001 From: tahaKh Date: Thu, 15 Oct 2026 11:34:03 +0200 Subject: [PATCH] userStream api --- symphony-bdk-core/build.gradle | 9 ++- .../com/symphony/bdk/core/ServiceFactory.java | 5 +- .../bdk/core/client/ApiClientFactory.java | 5 ++ .../core/service/stream/OboStreamService.java | 68 +++++++++++++------ .../core/service/stream/StreamService.java | 22 +++++- .../symphony/bdk/core/ServiceFactoryTest.java | 2 + .../bdk/core/client/ApiClientFactoryTest.java | 7 ++ .../service/stream/StreamServiceTest.java | 61 +++++++++++++++-- .../spring/config/BdkApiClientsConfig.java | 7 ++ .../bdk/spring/config/BdkCoreConfig.java | 8 +++ .../spring/config/BdkOboServiceConfig.java | 5 +- .../bdk/spring/config/BdkServiceConfig.java | 5 +- .../SymphonyBdkAutoConfigurationTest.java | 8 +++ .../bdk/spring/config/BdkCoreConfigTest.java | 13 ++++ 14 files changed, 189 insertions(+), 36 deletions(-) diff --git a/symphony-bdk-core/build.gradle b/symphony-bdk-core/build.gradle index 139ce401d..e524a0d19 100644 --- a/symphony-bdk-core/build.gradle +++ b/symphony-bdk-core/build.gradle @@ -81,20 +81,25 @@ dependencies { // OpenAPI code generation def apiBaseUrl = "https://raw.githubusercontent.com/finos/symphony-api-spec/a8efe8a6193769d0657fc57758918138aa4483bb" + +def apiBaseUrlOverrides = [ + Streams: "https://raw.githubusercontent.com/Tahak-symphony/symphony-api-spec/BE-9867_user_streams" +] def generatedFolder = "${layout.buildDirectory.get()}/generated/openapi" def apisToGenerate = [ Agent: 'agent/agent-api-public-deprecated.yaml', Pod : 'pod/pod-api-public-deprecated.yaml', Auth : 'authenticator/authenticator-api-public-deprecated.yaml', Login: 'login/login-api-public.yaml', - Users: 'users/users-api-public.yaml' + Users: 'users/users-api-public.yaml', + Streams: 'streams/streams-api-public.yaml' ] sourceSets.main.java.srcDirs += "$generatedFolder/src/main/java" apisToGenerate.each { api, path -> def downloadTask = tasks.register("download$api", Download) { - src "$apiBaseUrl/$path" + src "${apiBaseUrlOverrides.getOrDefault(api, apiBaseUrl)}/$path" dest "${layout.buildDirectory.get()}/openapi/$api/${path.split("/")[1]}" overwrite false useETag "all" diff --git a/symphony-bdk-core/src/main/java/com/symphony/bdk/core/ServiceFactory.java b/symphony-bdk-core/src/main/java/com/symphony/bdk/core/ServiceFactory.java index 17f0305bb..bdc39ed9e 100644 --- a/symphony-bdk-core/src/main/java/com/symphony/bdk/core/ServiceFactory.java +++ b/symphony-bdk-core/src/main/java/com/symphony/bdk/core/ServiceFactory.java @@ -49,6 +49,7 @@ import com.symphony.bdk.gen.api.StreamsApi; import com.symphony.bdk.gen.api.SystemApi; import com.symphony.bdk.gen.api.UserApi; +import com.symphony.bdk.gen.api.UserStreamsApi; import com.symphony.bdk.gen.api.UsersApi; import com.symphony.bdk.gen.api.model.UserV2; import com.symphony.bdk.http.api.ApiClient; @@ -76,6 +77,7 @@ class ServiceFactory { private final ApiClient podClient; private final ApiClient agentClient; + private final ApiClient streamClient; private final ApiClient datafeedAgentClient; private final ApiClient datahoseAgentClient; private final AuthSession authSession; @@ -101,6 +103,7 @@ public ServiceFactory( this.config = config; this.podClient = apiClientFactory.getPodClient(); this.agentClient = apiClientFactory.getAgentClient(); + this.streamClient = apiClientFactory.getStreamClient(); this.datafeedAgentClient = apiClientFactory.getDatafeedAgentClient(); this.datahoseAgentClient = apiClientFactory.getDatahoseAgentClient(); this.authSession = authSession; @@ -149,7 +152,7 @@ public UserService getUserService() { */ public StreamService getStreamService() { return new StreamService(new StreamsApi(podClient), new RoomMembershipApi(podClient), new ShareApi(agentClient), - new StreamContextApi(agentClient), authSession, retryBuilder); + new StreamContextApi(agentClient), new UserStreamsApi(streamClient), authSession, retryBuilder); } public DisclaimerService getDisclaimerService() { diff --git a/symphony-bdk-core/src/main/java/com/symphony/bdk/core/client/ApiClientFactory.java b/symphony-bdk-core/src/main/java/com/symphony/bdk/core/client/ApiClientFactory.java index 07c1ba682..c8dd9dc45 100644 --- a/symphony-bdk-core/src/main/java/com/symphony/bdk/core/client/ApiClientFactory.java +++ b/symphony-bdk-core/src/main/java/com/symphony/bdk/core/client/ApiClientFactory.java @@ -31,6 +31,7 @@ public class ApiClientFactory { private static final String USERS_CONTEXT_PATH = ""; + private static final String STREAMS_CONTEXT_PATH = ""; private static final String LOGIN_CONTEXT_PATH = "/login"; private static final String POD_CONTEXT_PATH = "/pod"; private static final String AGENT_CONTEXT_PATH = "/agent"; @@ -83,6 +84,10 @@ public ApiClient getUsersClient() { return buildClient(USERS_CONTEXT_PATH, this.config.getPod()); } + public ApiClient getStreamClient() { + return buildClient(STREAMS_CONTEXT_PATH, this.config.getPod()); + } + /** * Returns a fully initialized {@link ApiClient} for KeyManager API. * diff --git a/symphony-bdk-core/src/main/java/com/symphony/bdk/core/service/stream/OboStreamService.java b/symphony-bdk-core/src/main/java/com/symphony/bdk/core/service/stream/OboStreamService.java index 984a0ce4a..fd28278aa 100644 --- a/symphony-bdk-core/src/main/java/com/symphony/bdk/core/service/stream/OboStreamService.java +++ b/symphony-bdk-core/src/main/java/com/symphony/bdk/core/service/stream/OboStreamService.java @@ -7,15 +7,17 @@ import com.symphony.bdk.gen.api.model.Stream; import com.symphony.bdk.gen.api.model.StreamAttributes; import com.symphony.bdk.gen.api.model.StreamFilter; +import com.symphony.bdk.gen.api.model.StreamArrayResponse; import com.symphony.bdk.gen.api.model.V2Message; import com.symphony.bdk.gen.api.model.V2RoomSearchCriteria; import com.symphony.bdk.gen.api.model.V2StreamAttributes; import com.symphony.bdk.gen.api.model.V3RoomAttributes; import com.symphony.bdk.gen.api.model.V3RoomDetail; import com.symphony.bdk.gen.api.model.V3RoomSearchResults; -import org.apiguardian.api.API; +import org.apiguardian.api.API; import org.jspecify.annotations.Nullable; + import java.util.List; /** @@ -31,7 +33,7 @@ public interface OboStreamService { * @return The created IM * @see Create IM or MIM */ - Stream create(Long... uids); + Stream create(Long... uids); /** * Create a new single or multi party instant message conversation between the caller and specified users. @@ -65,7 +67,7 @@ public interface OboStreamService { /** * {@link StreamService#getStream(String)} * - * @param streamId The stream id. + * @param streamId The stream id. * @return The information about the stream with the given id. * @see Stream Info V2 */ @@ -83,7 +85,7 @@ public interface OboStreamService { /** * {@link StreamService#listStreams(StreamFilter)} * - * @param filter The stream searching criteria. + * @param filter The stream searching criteria. * @return The list of streams retrieved according to the searching criteria. * @see List Streams */ @@ -92,8 +94,8 @@ public interface OboStreamService { /** * {@link StreamService#listStreams(StreamFilter, PaginationAttribute)} * - * @param filter The stream searching criteria. - * @param pagination The skip and limit for pagination. + * @param filter The stream searching criteria. + * @param pagination The skip and limit for pagination. * @return The list of streams retrieved according to the searching criteria. * @see List Streams */ @@ -112,8 +114,8 @@ public interface OboStreamService { /** * {@link StreamService#listAllStreams(StreamFilter, StreamPaginationAttribute)} * - * @param filter The stream searching criteria. - * @param pagination The chunkSize and totalSize for pagination. + * @param filter The stream searching criteria. + * @param pagination The chunkSize and totalSize for pagination. * @return a {@link java.util.stream.Stream} of matching streams according to the searching criteria. * @see List Streams */ @@ -175,8 +177,8 @@ java.util.stream.Stream searchAllRooms(V2RoomSearchCriteria query, /** * {@link StreamService#addMemberToRoom(Long, String)} * - * @param userId The id of the user to be added to the given room - * @param roomId The room id + * @param userId The id of the user to be added to the given room + * @param roomId The room id * @see Add Member */ void addMemberToRoom(Long userId, String roomId); @@ -184,8 +186,8 @@ java.util.stream.Stream searchAllRooms(V2RoomSearchCriteria query, /** * {@link StreamService#removeMemberFromRoom(Long, String)} * - * @param userId The id of the user to be removed from the given room - * @param roomId The room id + * @param userId The id of the user to be removed from the given room + * @param roomId The room id * @see Remove Member */ void removeMemberFromRoom(Long userId, String roomId); @@ -193,8 +195,8 @@ java.util.stream.Stream searchAllRooms(V2RoomSearchCriteria query, /** * {@link StreamService#share(String, ShareContent)} * - * @param streamId The stream id. - * @param content The third-party {@link ShareContent} to be shared. + * @param streamId The stream id. + * @param content The third-party {@link ShareContent} to be shared. * @return Message contains share content * @see Share */ @@ -203,8 +205,8 @@ java.util.stream.Stream searchAllRooms(V2RoomSearchCriteria query, /** * {@link StreamService#promoteUserToRoomOwner(Long, String)} * - * @param userId The id of the user to be promoted to room owner. - * @param roomId The room id. + * @param userId The id of the user to be promoted to room owner. + * @param roomId The room id. * @see Promote Owner */ void promoteUserToRoomOwner(Long userId, String roomId); @@ -212,8 +214,8 @@ java.util.stream.Stream searchAllRooms(V2RoomSearchCriteria query, /** * {@link StreamService#demoteUserToRoomParticipant(Long, String)} * - * @param userId The id of the user to be demoted to room participant. - * @param roomId The room id. + * @param userId The id of the user to be demoted to room participant. + * @param roomId The room id. * @see Demote Owner */ void demoteUserToRoomParticipant(Long userId, String roomId); @@ -230,13 +232,35 @@ java.util.stream.Stream searchAllRooms(V2RoomSearchCriteria query, /** * Return a representation of the conversation in Markdown format * Returned data includes : - * - stream title - * - stream properties - * - stream members - * - stream last messages + * - stream title + * - stream properties + * - stream members + * - stream last messages * * @param streamId * @return A String containing markdown representation of the conversation */ String getStreamContext(String streamId); + + /** + * Lists the authenticated user's active chat streams matching specified filtering criteria. + * + * @param limit The maximum number of streams to return. + * @param includeHidden Whether to include hidden streams in the results. + * @param onlyUnread If true, only returns streams that contain unread messages. + * @param channel Filter streams by channel type. Allowed values: Whatsapp, Wechat, Line, SMS, Symphony. + * @param external Filter streams to include external chats. + * @param internal Filter streams to include internal chats. + * @param cursor Opaque pagination cursor returned as nextCursor by a previous call. + * @return A {@link StreamArrayResponse} containing the list of active chat streams and next cursor. + * @see List User Streams + */ + StreamArrayResponse getUserStreams(@Nullable Integer limit, + @Nullable Boolean includeHidden, + @Nullable Boolean onlyUnread, + @Nullable String channel, + @Nullable Boolean external, + @Nullable Boolean internal, + @Nullable String cursor); + } diff --git a/symphony-bdk-core/src/main/java/com/symphony/bdk/core/service/stream/StreamService.java b/symphony-bdk-core/src/main/java/com/symphony/bdk/core/service/stream/StreamService.java index 9f9da24e0..21bfaa327 100644 --- a/symphony-bdk-core/src/main/java/com/symphony/bdk/core/service/stream/StreamService.java +++ b/symphony-bdk-core/src/main/java/com/symphony/bdk/core/service/stream/StreamService.java @@ -16,10 +16,12 @@ import com.symphony.bdk.gen.api.ShareApi; import com.symphony.bdk.gen.api.StreamContextApi; import com.symphony.bdk.gen.api.StreamsApi; +import com.symphony.bdk.gen.api.UserStreamsApi; import com.symphony.bdk.gen.api.model.MemberInfo; import com.symphony.bdk.gen.api.model.RoomDetail; import com.symphony.bdk.gen.api.model.ShareContent; import com.symphony.bdk.gen.api.model.Stream; +import com.symphony.bdk.gen.api.model.StreamArrayResponse; import com.symphony.bdk.gen.api.model.StreamAttributes; import com.symphony.bdk.gen.api.model.StreamFilter; import com.symphony.bdk.gen.api.model.UserId; @@ -67,28 +69,31 @@ public class StreamService implements OboStreamService, OboService retryBuilder; public StreamService(StreamsApi streamsApi, RoomMembershipApi membershipApi, ShareApi shareApi, - StreamContextApi streamContextApi, + StreamContextApi streamContextApi, UserStreamsApi userStreamsApi, AuthSession authSession, RetryWithRecoveryBuilder retryBuilder) { this.streamsApi = streamsApi; this.roomMembershipApi = membershipApi; this.shareApi = shareApi; this.streamContextApi = streamContextApi; + this.userStreamsApi = userStreamsApi; this.authSession = authSession; this.retryBuilder = RetryWithRecoveryBuilder.copyWithoutRecoveryStrategies(retryBuilder) .recoveryStrategy(ApiException::isUnauthorized, authSession::refresh); } public StreamService(StreamsApi streamsApi, RoomMembershipApi membershipApi, ShareApi shareApi, - StreamContextApi streamContextApi, + StreamContextApi streamContextApi, UserStreamsApi userStreamsApi, RetryWithRecoveryBuilder retryBuilder) { this.streamsApi = streamsApi; this.roomMembershipApi = membershipApi; this.shareApi = shareApi; this.streamContextApi = streamContextApi; + this.userStreamsApi = userStreamsApi; this.authSession = null; this.retryBuilder = RetryWithRecoveryBuilder.copyWithoutRecoveryStrategies(retryBuilder); } @@ -98,7 +103,7 @@ public StreamService(StreamsApi streamsApi, RoomMembershipApi membershipApi, Sha */ @Override public OboStreamService obo(AuthSession oboSession) { - return new StreamService(streamsApi, roomMembershipApi, shareApi, streamContextApi, oboSession, retryBuilder); + return new StreamService(streamsApi, roomMembershipApi, shareApi, streamContextApi, userStreamsApi, oboSession, retryBuilder); } /** @@ -509,6 +514,17 @@ public String getStreamContext(String streamId) { () -> streamContextApi.v1StreamContext(toUrlSafeIdIfNeeded(streamId), authSession.getSessionToken(), authSession.getKeyManagerToken())); } + /** + * {@inheritDoc} + */ + @Override + public StreamArrayResponse getUserStreams(@Nullable Integer limit, @Nullable Boolean includeHidden, + @Nullable Boolean onlyUnread, @Nullable String channel, @Nullable Boolean external, @Nullable Boolean internal, + @Nullable String cursor) { + return executeAndRetry("getUserStreams", userStreamsApi.getApiClient().getBasePath(), + () -> userStreamsApi.listUserStreams(authSession.getSessionToken(), limit, includeHidden, onlyUnread, channel, external, internal, cursor)); + } + private T executeAndRetry(String name, String address, SupplierWithApiException supplier) { checkAuthSession(authSession); return RetryWithRecovery.executeAndRetry(retryBuilder, name, address, supplier); diff --git a/symphony-bdk-core/src/test/java/com/symphony/bdk/core/ServiceFactoryTest.java b/symphony-bdk-core/src/test/java/com/symphony/bdk/core/ServiceFactoryTest.java index 4e151e335..ad4458a0a 100644 --- a/symphony-bdk-core/src/test/java/com/symphony/bdk/core/ServiceFactoryTest.java +++ b/symphony-bdk-core/src/test/java/com/symphony/bdk/core/ServiceFactoryTest.java @@ -56,6 +56,7 @@ void setUp() throws BdkConfigException { when(this.apiClientFactory.getPodClient()).thenReturn(mPodClient); when(this.apiClientFactory.getAgentClient()).thenReturn(mock(ApiClient.class)); + when(this.apiClientFactory.getStreamClient()).thenReturn(mock(ApiClient.class)); ApiClient datafeedAgentClient = mock(ApiClient.class); when(datafeedAgentClient.getBasePath()).thenReturn("/agent"); @@ -75,6 +76,7 @@ void getUserServiceTest() { void getStreamServiceTest() { StreamService streamService = this.serviceFactory.getStreamService(); assertNotNull(streamService); + verify(this.apiClientFactory).getStreamClient(); } @Test diff --git a/symphony-bdk-core/src/test/java/com/symphony/bdk/core/client/ApiClientFactoryTest.java b/symphony-bdk-core/src/test/java/com/symphony/bdk/core/client/ApiClientFactoryTest.java index 4a6c2ab6a..698184444 100644 --- a/symphony-bdk-core/src/test/java/com/symphony/bdk/core/client/ApiClientFactoryTest.java +++ b/symphony-bdk-core/src/test/java/com/symphony/bdk/core/client/ApiClientFactoryTest.java @@ -46,6 +46,13 @@ void testGetUsersClient() { assertEquals("https://pod-host:443", usersClient.getBasePath()); } + @Test + void testGetStreamClient() { + final ApiClient streamClient = this.factory.getStreamClient(); + assertEquals(ApiClientJersey2.class, streamClient.getClass()); + assertEquals("https://pod-host:443", streamClient.getBasePath()); + } + @Test void testGetRelayClient() { final ApiClient relayClient = this.factory.getRelayClient(); diff --git a/symphony-bdk-core/src/test/java/com/symphony/bdk/core/service/stream/StreamServiceTest.java b/symphony-bdk-core/src/test/java/com/symphony/bdk/core/service/stream/StreamServiceTest.java index dc18ec929..ba576b940 100644 --- a/symphony-bdk-core/src/test/java/com/symphony/bdk/core/service/stream/StreamServiceTest.java +++ b/symphony-bdk-core/src/test/java/com/symphony/bdk/core/service/stream/StreamServiceTest.java @@ -3,6 +3,7 @@ import static com.symphony.bdk.core.util.IdUtil.fromUrlSafeId; 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.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -23,10 +24,12 @@ import com.symphony.bdk.gen.api.ShareApi; import com.symphony.bdk.gen.api.StreamContextApi; import com.symphony.bdk.gen.api.StreamsApi; +import com.symphony.bdk.gen.api.UserStreamsApi; import com.symphony.bdk.gen.api.model.MemberInfo; import com.symphony.bdk.gen.api.model.RoomDetail; import com.symphony.bdk.gen.api.model.ShareContent; import com.symphony.bdk.gen.api.model.Stream; +import com.symphony.bdk.gen.api.model.StreamArrayResponse; import com.symphony.bdk.gen.api.model.StreamAttributes; import com.symphony.bdk.gen.api.model.StreamFilter; import com.symphony.bdk.gen.api.model.StreamType; @@ -80,6 +83,7 @@ public class StreamServiceTest { private static final String V3_ROOM_UPDATE = "/pod/v3/room/{id}/update"; private static final String V3_SHARE = "/agent/v3/stream/{sid}/share"; private static final String V1_STREAM_CONTEXT = "/agent/v1/stream/{sid}/context"; + private static final String V4_USER_STREAMS = "/v4/streams/user"; private StreamService service; private MockApiClient mockApiClient; @@ -87,6 +91,7 @@ public class StreamServiceTest { private AuthSession authSession; private StreamsApi streamsApi; private StreamContextApi streamContextApi; + private UserStreamsApi userStreamsApi; private ShareApi shareApi; @BeforeEach @@ -95,13 +100,15 @@ void setUp() { this.authSession = mock(AuthSession.class); ApiClient podClient = mockApiClient.getApiClient("/pod"); ApiClient agentClient = mockApiClient.getApiClient("/agent"); + ApiClient streamClient = mockApiClient.getApiClient(""); this.spyRoomMembershipApi = spy(new RoomMembershipApi(podClient)); this.streamsApi = spy(new StreamsApi(podClient)); this.shareApi = new ShareApi(agentClient); this.streamContextApi = new StreamContextApi(agentClient); + this.userStreamsApi = spy(new UserStreamsApi(streamClient)); this.service = new StreamService(this.streamsApi, this.spyRoomMembershipApi, this.shareApi, this.streamContextApi, - this.authSession, new RetryWithRecoveryBuilder<>()); + this.userStreamsApi, this.authSession, new RetryWithRecoveryBuilder<>()); when(authSession.getSessionToken()).thenReturn("1234"); when(authSession.getKeyManagerToken()).thenReturn("1234"); @@ -110,7 +117,7 @@ void setUp() { @Test void nonOboEndpointShouldThrowExceptionInOboMode() { this.service = new StreamService(this.streamsApi, this.spyRoomMembershipApi, this.shareApi, this.streamContextApi, - new RetryWithRecoveryBuilder<>()); + this.userStreamsApi, new RetryWithRecoveryBuilder<>()); assertThrows(IllegalStateException.class, () -> this.service.getStream("")); } @@ -121,7 +128,7 @@ void getStreamInOboMode() throws IOException { JsonHelper.readFromClasspath("/stream/v2_stream_attributes.json")); this.service = new StreamService(this.streamsApi, this.spyRoomMembershipApi, this.shareApi, this.streamContextApi, - new RetryWithRecoveryBuilder<>()); + this.userStreamsApi, new RetryWithRecoveryBuilder<>()); V2StreamAttributes stream = this.service.obo(this.authSession).getStream("p9B316LKDto7iOECc8Xuz3qeWsc0bdA"); assertEquals("p9B316LKDto7iOECc8Xuz3qeWsc0bdA", stream.getId()); @@ -524,7 +531,7 @@ void getStreamContextInOboMode() { this.mockApiClient.onGet(V1_STREAM_CONTEXT.replace("{sid}", "p9B316LKDto7iOECc8Xuz3qeWsc0bdA"), "\"Markdown Context\""); this.service = new StreamService(this.streamsApi, this.spyRoomMembershipApi, this.shareApi, this.streamContextApi, - new RetryWithRecoveryBuilder<>()); + this.userStreamsApi, new RetryWithRecoveryBuilder<>()); String context = this.service.obo(this.authSession).getStreamContext("p9B316LKDto7iOECc8Xuz3qeWsc0bdA"); assertEquals("Markdown Context", context); @@ -537,6 +544,52 @@ void getStreamContextTestFailed() { assertThrows(ApiRuntimeException.class, () -> this.service.getStreamContext("p9B316LKDto7iOECc8Xuz3qeWsc0bdA")); } + @Test + void getUserStreamsTest() throws ApiException { + this.mockApiClient.onGet(V4_USER_STREAMS, + "{\"streams\": [{\"id\": \"p9B316LKDto7iOECc8Xuz3qeWsc0bdA\", \"active\": true}], \"nextCursor\": \"cursor123\"}"); + + StreamArrayResponse response = this.service.getUserStreams(10, false, true, "Symphony", true, false, "cursor000"); + + assertNotNull(response); + assertEquals("cursor123", response.getNextCursor()); + assertEquals(1, response.getStreams().size()); + assertEquals("p9B316LKDto7iOECc8Xuz3qeWsc0bdA", response.getStreams().get(0).getId()); + assertTrue(response.getStreams().get(0).getActive()); + verify(this.userStreamsApi).listUserStreams(eq("1234"), eq(10), eq(false), eq(true), eq("Symphony"), eq(true), eq(false), eq("cursor000")); + } + + @Test + void getUserStreamsInOboMode() throws ApiException { + this.mockApiClient.onGet(V4_USER_STREAMS, + "{\"streams\": [{\"id\": \"p9B316LKDto7iOECc8Xuz3qeWsc0bdA\", \"active\": true}], \"nextCursor\": \"cursor123\"}"); + + this.service = new StreamService(this.streamsApi, this.spyRoomMembershipApi, this.shareApi, this.streamContextApi, + this.userStreamsApi, new RetryWithRecoveryBuilder<>()); + StreamArrayResponse response = this.service.obo(this.authSession).getUserStreams(null, null, null, null, null, null, null); + + assertNotNull(response); + assertEquals("cursor123", response.getNextCursor()); + assertEquals(1, response.getStreams().size()); + assertEquals("p9B316LKDto7iOECc8Xuz3qeWsc0bdA", response.getStreams().get(0).getId()); + verify(this.userStreamsApi).listUserStreams(eq("1234"), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null)); + } + + @Test + void getUserStreamsTestFailed() { + this.mockApiClient.onGet(400, V4_USER_STREAMS, "{}"); + + assertThrows(ApiRuntimeException.class, () -> this.service.getUserStreams(10, false, true, "Symphony", true, false, "cursor000")); + } + + @Test + void getUserStreamsShouldThrowExceptionWhenNoSession() { + this.service = new StreamService(this.streamsApi, this.spyRoomMembershipApi, this.shareApi, this.streamContextApi, + this.userStreamsApi, new RetryWithRecoveryBuilder<>()); + + assertThrows(IllegalStateException.class, () -> this.service.getUserStreams(null, null, null, null, null, null, null)); + } + @Test void createAdminIMorMIMTest() { this.mockApiClient.onPost(V1_IM_CREATE_ADMIN, "{\n\"id\": \"xhGxbTcvTDK6EIMMrwdOrX___quztr2HdA\"\n}"); diff --git a/symphony-bdk-spring/symphony-bdk-core-spring-boot-starter/src/main/java/com/symphony/bdk/spring/config/BdkApiClientsConfig.java b/symphony-bdk-spring/symphony-bdk-core-spring-boot-starter/src/main/java/com/symphony/bdk/spring/config/BdkApiClientsConfig.java index b498c474a..b0cf40dff 100644 --- a/symphony-bdk-spring/symphony-bdk-core-spring-boot-starter/src/main/java/com/symphony/bdk/spring/config/BdkApiClientsConfig.java +++ b/symphony-bdk-spring/symphony-bdk-core-spring-boot-starter/src/main/java/com/symphony/bdk/spring/config/BdkApiClientsConfig.java @@ -1,6 +1,7 @@ package com.symphony.bdk.spring.config; import com.symphony.bdk.gen.api.StreamContextApi; +import com.symphony.bdk.gen.api.UserStreamsApi; import com.symphony.bdk.http.api.ApiClient; import com.symphony.bdk.gen.api.AppEntitlementApi; import com.symphony.bdk.gen.api.ApplicationApi; @@ -201,6 +202,12 @@ public StreamContextApi streamsContextApi(@Qualifier("agentApiClient") ApiClient return new StreamContextApi(agentApiClient); } + @Bean + @ConditionalOnMissingBean + public UserStreamsApi userStreamsApi(@Qualifier("streamApiClient") ApiClient streamApiClient) { + return new UserStreamsApi(streamApiClient); + } + @Bean @ConditionalOnMissingBean public SystemApi systemApi(@Qualifier("agentApiClient") ApiClient agentApiClient) { diff --git a/symphony-bdk-spring/symphony-bdk-core-spring-boot-starter/src/main/java/com/symphony/bdk/spring/config/BdkCoreConfig.java b/symphony-bdk-spring/symphony-bdk-core-spring-boot-starter/src/main/java/com/symphony/bdk/spring/config/BdkCoreConfig.java index 30ed49ad2..6e938319d 100644 --- a/symphony-bdk-spring/symphony-bdk-core-spring-boot-starter/src/main/java/com/symphony/bdk/spring/config/BdkCoreConfig.java +++ b/symphony-bdk-spring/symphony-bdk-core-spring-boot-starter/src/main/java/com/symphony/bdk/spring/config/BdkCoreConfig.java @@ -59,6 +59,14 @@ public ApiClient agentApiClient(ApiClientFactory apiClientFactory, Optional botSession, + BdkConfig config) { + ApiClient client = apiClientFactory.getStreamClient(); + addCommonJwtConfig(client, botSession, config); + return client; + } + @Bean(name = "datafeedAgentApiClient") public ApiClient datafeedAgentApiClient(ApiClientFactory apiClientFactory, Optional botSession, BdkConfig config) { diff --git a/symphony-bdk-spring/symphony-bdk-core-spring-boot-starter/src/main/java/com/symphony/bdk/spring/config/BdkOboServiceConfig.java b/symphony-bdk-spring/symphony-bdk-core-spring-boot-starter/src/main/java/com/symphony/bdk/spring/config/BdkOboServiceConfig.java index 6abcf7c5e..aef449714 100644 --- a/symphony-bdk-spring/symphony-bdk-core-spring-boot-starter/src/main/java/com/symphony/bdk/spring/config/BdkOboServiceConfig.java +++ b/symphony-bdk-spring/symphony-bdk-core-spring-boot-starter/src/main/java/com/symphony/bdk/spring/config/BdkOboServiceConfig.java @@ -29,6 +29,7 @@ import com.symphony.bdk.gen.api.StreamContextApi; import com.symphony.bdk.gen.api.StreamsApi; import com.symphony.bdk.gen.api.UserApi; +import com.symphony.bdk.gen.api.UserStreamsApi; import com.symphony.bdk.gen.api.UsersApi; import com.symphony.bdk.template.api.TemplateEngine; @@ -74,8 +75,8 @@ public SessionService oboSessionService(SessionApi sessionApi, BdkConfig config) @Bean @ConditionalOnMissingBean public StreamService oboStreamService(StreamsApi streamsApi, RoomMembershipApi roomMembershipApi, ShareApi shareApi, - StreamContextApi streamContextApi, BdkConfig config) { - return new StreamService(streamsApi, roomMembershipApi, shareApi, streamContextApi, new RetryWithRecoveryBuilder<>().retryConfig(config.getRetry())); + StreamContextApi streamContextApi, UserStreamsApi userStreamsApi, BdkConfig config) { + return new StreamService(streamsApi, roomMembershipApi, shareApi, streamContextApi, userStreamsApi, new RetryWithRecoveryBuilder<>().retryConfig(config.getRetry())); } @Bean diff --git a/symphony-bdk-spring/symphony-bdk-core-spring-boot-starter/src/main/java/com/symphony/bdk/spring/config/BdkServiceConfig.java b/symphony-bdk-spring/symphony-bdk-core-spring-boot-starter/src/main/java/com/symphony/bdk/spring/config/BdkServiceConfig.java index 5228a722e..f479931fb 100644 --- a/symphony-bdk-spring/symphony-bdk-core-spring-boot-starter/src/main/java/com/symphony/bdk/spring/config/BdkServiceConfig.java +++ b/symphony-bdk-spring/symphony-bdk-core-spring-boot-starter/src/main/java/com/symphony/bdk/spring/config/BdkServiceConfig.java @@ -34,6 +34,7 @@ import com.symphony.bdk.gen.api.StreamsApi; import com.symphony.bdk.gen.api.SystemApi; import com.symphony.bdk.gen.api.UserApi; +import com.symphony.bdk.gen.api.UserStreamsApi; import com.symphony.bdk.gen.api.UsersApi; import com.symphony.bdk.spring.service.BotInfoService; import com.symphony.bdk.template.api.TemplateEngine; @@ -60,8 +61,8 @@ public SessionService sessionService(SessionApi sessionApi, AuthSession botSessi @Bean @ConditionalOnMissingBean public StreamService streamService(StreamsApi streamsApi, RoomMembershipApi roomMembershipApi, ShareApi shareApi, StreamContextApi streamContextApi, - AuthSession botSession, BdkConfig config) { - return new StreamService(streamsApi, roomMembershipApi, shareApi, streamContextApi, botSession, new RetryWithRecoveryBuilder<>().retryConfig(config.getRetry())); + UserStreamsApi userStreamsApi, AuthSession botSession, BdkConfig config) { + return new StreamService(streamsApi, roomMembershipApi, shareApi, streamContextApi, userStreamsApi, botSession, new RetryWithRecoveryBuilder<>().retryConfig(config.getRetry())); } @Bean diff --git a/symphony-bdk-spring/symphony-bdk-core-spring-boot-starter/src/test/java/com/symphony/bdk/spring/SymphonyBdkAutoConfigurationTest.java b/symphony-bdk-spring/symphony-bdk-core-spring-boot-starter/src/test/java/com/symphony/bdk/spring/SymphonyBdkAutoConfigurationTest.java index 7bc1eac1f..9af684b3a 100644 --- a/symphony-bdk-spring/symphony-bdk-core-spring-boot-starter/src/test/java/com/symphony/bdk/spring/SymphonyBdkAutoConfigurationTest.java +++ b/symphony-bdk-spring/symphony-bdk-core-spring-boot-starter/src/test/java/com/symphony/bdk/spring/SymphonyBdkAutoConfigurationTest.java @@ -14,6 +14,7 @@ import com.symphony.bdk.core.service.datafeed.impl.DatahoseLoopImpl; import com.symphony.bdk.core.service.health.HealthService; import com.symphony.bdk.gen.api.SystemApi; +import com.symphony.bdk.gen.api.UserStreamsApi; import com.symphony.bdk.http.api.ApiClient; import com.symphony.bdk.spring.annotation.SlashAnnotationProcessor; import com.symphony.bdk.spring.config.BdkActivityConfig; @@ -64,6 +65,8 @@ void shouldLoadContextWithSuccess() { assertThat(context).hasSingleBean(HealthService.class); assertThat(context).hasSingleBean(DatafeedAsyncLauncherService.class); assertThat(context).hasSingleBean(RealTimeEventsDispatcher.class); + assertThat(context).hasSingleBean(UserStreamsApi.class); + assertThat(context).hasBean("streamApiClient"); // verify datahose not enabled by default assertThat(context).doesNotHaveBean(DatahoseLoopImpl.class); @@ -237,6 +240,11 @@ void shouldAddAuthenticationIfCommonJwtEnabled() { ApiClient podClient = (ApiClient) context.getBean("podApiClient"); assertThat(podClient.getAuthentications()).isNotEmpty(); assertThat(podClient.getAuthentications()).containsKey("bearerAuth"); + + assertThat(context).hasBean("streamApiClient"); + ApiClient streamClient = (ApiClient) context.getBean("streamApiClient"); + assertThat(streamClient.getAuthentications()).isNotEmpty(); + assertThat(streamClient.getAuthentications()).containsKey("bearerAuth"); }); } diff --git a/symphony-bdk-spring/symphony-bdk-core-spring-boot-starter/src/test/java/com/symphony/bdk/spring/config/BdkCoreConfigTest.java b/symphony-bdk-spring/symphony-bdk-core-spring-boot-starter/src/test/java/com/symphony/bdk/spring/config/BdkCoreConfigTest.java index f9c7aec99..b9d107fd0 100644 --- a/symphony-bdk-spring/symphony-bdk-core-spring-boot-starter/src/test/java/com/symphony/bdk/spring/config/BdkCoreConfigTest.java +++ b/symphony-bdk-spring/symphony-bdk-core-spring-boot-starter/src/test/java/com/symphony/bdk/spring/config/BdkCoreConfigTest.java @@ -76,6 +76,19 @@ void shouldCreatePodClient() { when(factory.getPodClient()).thenReturn(mock(ApiClient.class)); assertNotNull(config.podApiClient(factory, Optional.ofNullable(authSession), bdkConfig)); } + + @Test + void shouldCreateStreamApiClient() { + final BdkCoreConfig config = new BdkCoreConfig(); + final BdkConfig bdkConfig = new BdkConfig(); + BdkCommonJwtConfig bdkCommonJwtConfig = new BdkCommonJwtConfig(); + bdkCommonJwtConfig.setEnabled(true); + bdkConfig.setCommonJwt(bdkCommonJwtConfig); + final ApiClientFactory factory = mock(ApiClientFactory.class); + final AuthSession authSession = mock(AuthSession.class); + when(factory.getStreamClient()).thenReturn(mock(ApiClient.class)); + assertNotNull(config.streamApiClient(factory, Optional.ofNullable(authSession), bdkConfig)); + } @Test void shouldCreateKeyAuthApiClient() { final BdkCoreConfig config = new BdkCoreConfig();