Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions symphony-bdk-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -81,20 +81,25 @@ dependencies {

// OpenAPI code generation
def apiBaseUrl = "https://raw.githubusercontent.com/finos/symphony-api-spec/a0b152881a0405b7510cfc7e3a9698e8cb8c566c"

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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import com.symphony.bdk.gen.api.SessionApi;
import com.symphony.bdk.gen.api.ShareApi;
import com.symphony.bdk.gen.api.SignalsApi;
import com.symphony.bdk.gen.api.StreamSearchApi;
import com.symphony.bdk.gen.api.StreamsApi;
import com.symphony.bdk.gen.api.SystemApi;
import com.symphony.bdk.gen.api.UserApi;
Expand Down Expand Up @@ -77,6 +78,7 @@ class ServiceFactory {
private final ApiClient podClient;
private final ApiClient usersClient;
private final ApiClient agentClient;
private final ApiClient streamClient;
private final ApiClient datafeedAgentClient;
private final ApiClient datahoseAgentClient;
private final AuthSession authSession;
Expand All @@ -103,6 +105,7 @@ public ServiceFactory(
this.podClient = apiClientFactory.getPodClient();
this.usersClient = apiClientFactory.getUsersClient();
this.agentClient = apiClientFactory.getAgentClient();
this.streamClient = apiClientFactory.getStreamClient();
this.datafeedAgentClient = apiClientFactory.getDatafeedAgentClient();
this.datahoseAgentClient = apiClientFactory.getDatahoseAgentClient();
this.authSession = authSession;
Expand Down Expand Up @@ -165,7 +168,7 @@ public ClientDataService getClientDataService() {
*/
public StreamService getStreamService() {
return new StreamService(new StreamsApi(podClient), new RoomMembershipApi(podClient), new ShareApi(agentClient),
new MessagesApi(agentClient), authSession, retryBuilder);
new MessagesApi(agentClient), new StreamSearchApi(streamClient), authSession, retryBuilder);
}

public DisclaimerService getDisclaimerService() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,18 @@
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.StreamResponse;
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;

/**
Expand All @@ -31,7 +34,7 @@ public interface OboStreamService {
* @return The created IM
* @see <a href="https://developers.symphony.com/restapi/reference/create-im-or-mim">Create IM or MIM</a>
*/
Stream create(Long... uids);
Stream create(Long... uids);

/**
* Create a new single or multi party instant message conversation between the caller and specified users.
Expand Down Expand Up @@ -65,7 +68,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 <a href="https://developers.symphony.com/restapi/reference/stream-info-v2">Stream Info V2</a>
*/
Expand All @@ -83,7 +86,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 <a href="https://developers.symphony.com/restapi/reference/list-user-streams">List Streams</a>
*/
Expand All @@ -92,8 +95,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 <a href="https://developers.symphony.com/restapi/reference/list-user-streams">List Streams</a>
*/
Expand All @@ -112,8 +115,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 <a href="https://developers.symphony.com/restapi/reference/list-user-streams">List Streams</a>
*/
Expand Down Expand Up @@ -175,26 +178,26 @@ java.util.stream.Stream<V3RoomDetail> 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 <a href="https://developers.symphony.com/restapi/reference/add-member">Add Member</a>
*/
void addMemberToRoom(Long userId, String roomId);

/**
* {@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 <a href="https://developers.symphony.com/restapi/reference/remove-member">Remove Member</a>
*/
void removeMemberFromRoom(Long userId, String roomId);

/**
* {@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 <a href="https://developers.symphony.com/restapi/reference/share-v3">Share</a>
*/
Expand All @@ -203,17 +206,17 @@ java.util.stream.Stream<V3RoomDetail> 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 <a href="https://developers.symphony.com/restapi/reference/promote-owner">Promote Owner</a>
*/
void promoteUserToRoomOwner(Long userId, String roomId);

/**
* {@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 <a href="https://developers.symphony.com/restapi/reference/demote-owner">Demote Owner</a>
*/
void demoteUserToRoomParticipant(Long userId, String roomId);
Expand All @@ -230,13 +233,58 @@ java.util.stream.Stream<V3RoomDetail> 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 (maximum 100).
* @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 (e.g. SYMPHONY, WHATSAPP, WCHT...).
* @param external Filter streams to include external chats.
* @param internal Filter streams to include internal chats.
* @param includeAgentStreams Whether to include AI_AGENT subtype streams in the results.
* @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 <a href="https://developers.symphony.com/restapi/reference/list-user-streams">List User Streams</a>
*/
StreamArrayResponse getUserStreams(@Nullable Integer limit,
@Nullable Boolean includeHidden,
@Nullable Boolean onlyUnread,
@Nullable String channel,
@Nullable Boolean external,
@Nullable Boolean internal,
@Nullable Boolean includeAgentStreams,
@Nullable String cursor);

/**
* Retrieve streams for a list of stream IDs via query parameters matching specified filtering criteria.
*
* @param streamIds List of stream IDs (maximum 50 allowed).
* @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 (e.g. SYMPHONY, WHATSAPP, WCHT...).
* @param external Filter streams to include external chats.
* @param internal Filter streams to include internal chats.
* @param includeAgentStreams Whether to include AI_AGENT subtype streams in the results.
* @return A list of {@link StreamResponse} matching the provided IDs and criteria.
* @see <a href="https://developers.symphony.com/restapi/reference/search-streams">Search Streams</a>
*/
List<StreamResponse> searchStreams(List<String> streamIds,
@Nullable Boolean includeHidden,
@Nullable Boolean onlyUnread,
@Nullable String channel,
@Nullable Boolean external,
@Nullable Boolean internal,
@Nullable Boolean includeAgentStreams);

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@
import com.symphony.bdk.gen.api.MessagesApi;
import com.symphony.bdk.gen.api.RoomMembershipApi;
import com.symphony.bdk.gen.api.ShareApi;
import com.symphony.bdk.gen.api.StreamSearchApi;
import com.symphony.bdk.gen.api.StreamsApi;
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.StreamResponse;
import com.symphony.bdk.gen.api.model.StreamAttributes;
import com.symphony.bdk.gen.api.model.StreamFilter;
import com.symphony.bdk.gen.api.model.UserId;
Expand Down Expand Up @@ -67,28 +70,31 @@ public class StreamService implements OboStreamService, OboService<OboStreamServ
private final RoomMembershipApi roomMembershipApi;
private final ShareApi shareApi;
private final MessagesApi messagesApi;
private final StreamSearchApi streamSearchApi;
private final AuthSession authSession;
private final RetryWithRecoveryBuilder<?> retryBuilder;

public StreamService(StreamsApi streamsApi, RoomMembershipApi membershipApi, ShareApi shareApi,
MessagesApi messagesApi,
MessagesApi messagesApi, StreamSearchApi streamSearchApi,
AuthSession authSession, RetryWithRecoveryBuilder<?> retryBuilder) {
this.streamsApi = streamsApi;
this.roomMembershipApi = membershipApi;
this.shareApi = shareApi;
this.messagesApi = messagesApi;
this.streamSearchApi = streamSearchApi;
this.authSession = authSession;
this.retryBuilder = RetryWithRecoveryBuilder.copyWithoutRecoveryStrategies(retryBuilder)
.recoveryStrategy(ApiException::isUnauthorized, authSession::refresh);
}

public StreamService(StreamsApi streamsApi, RoomMembershipApi membershipApi, ShareApi shareApi,
MessagesApi messagesApi,
MessagesApi messagesApi, StreamSearchApi streamSearchApi,
RetryWithRecoveryBuilder<?> retryBuilder) {
this.streamsApi = streamsApi;
this.roomMembershipApi = membershipApi;
this.shareApi = shareApi;
this.messagesApi = messagesApi;
this.streamSearchApi = streamSearchApi;
this.authSession = null;
this.retryBuilder = RetryWithRecoveryBuilder.copyWithoutRecoveryStrategies(retryBuilder);
}
Expand All @@ -98,7 +104,7 @@ public StreamService(StreamsApi streamsApi, RoomMembershipApi membershipApi, Sha
*/
@Override
public OboStreamService obo(AuthSession oboSession) {
return new StreamService(streamsApi, roomMembershipApi, shareApi, messagesApi, oboSession, retryBuilder);
return new StreamService(streamsApi, roomMembershipApi, shareApi, messagesApi, streamSearchApi, oboSession, retryBuilder);
}

/**
Expand Down Expand Up @@ -509,6 +515,28 @@ public String getStreamContext(String streamId) {
() -> messagesApi.v1StreamContext(toUrlSafeIdIfNeeded(streamId), null, null, null, null, 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 Boolean includeAgentStreams, @Nullable String cursor) {
return executeAndRetry("getUserStreams", streamSearchApi.getApiClient().getBasePath(),
() -> streamSearchApi.listUserStreams(authSession.getSessionToken(), limit, includeHidden, onlyUnread, channel, external, internal, includeAgentStreams, cursor));
}

/**
* {@inheritDoc}
*/
@Override
public List<StreamResponse> searchStreams(List<String> streamIds, @Nullable Boolean includeHidden,
@Nullable Boolean onlyUnread, @Nullable String channel, @Nullable Boolean external, @Nullable Boolean internal,
@Nullable Boolean includeAgentStreams) {
return executeAndRetry("searchStreams", streamSearchApi.getApiClient().getBasePath(),
() -> streamSearchApi.getStreamsSearch(authSession.getSessionToken(), streamIds, includeHidden, onlyUnread, channel, external, internal, includeAgentStreams));
}

private <T> T executeAndRetry(String name, String address, SupplierWithApiException<T> supplier) {
checkAuthSession(authSession);
return RetryWithRecovery.executeAndRetry(retryBuilder, name, address, supplier);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ void setUp() throws BdkConfigException {
when(this.apiClientFactory.getPodClient()).thenReturn(mPodClient);
when(this.apiClientFactory.getUsersClient()).thenReturn(mock(ApiClient.class));
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");
Expand All @@ -83,6 +84,7 @@ void getClientDataServiceTest() {
void getStreamServiceTest() {
StreamService streamService = this.serviceFactory.getStreamService();
assertNotNull(streamService);
verify(this.apiClientFactory).getStreamClient();
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Loading
Loading