Skip to content

Commit 5d9b24d

Browse files
Adapt the core for usage in FeedsClient (#3)
2 parents 54fd1de + 5ecc0d5 commit 5d9b24d

File tree

18 files changed

+887
-220
lines changed

18 files changed

+887
-220
lines changed

app/src/main/java/io/getstream/android/core/sample/client/StreamClient.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import io.getstream.android.core.api.authentication.StreamTokenManager
2020
import io.getstream.android.core.api.authentication.StreamTokenProvider
2121
import io.getstream.android.core.api.log.StreamLogger
2222
import io.getstream.android.core.api.log.StreamLoggerProvider
23+
import io.getstream.android.core.api.model.config.StreamClientSerializationConfig
2324
import io.getstream.android.core.api.model.value.StreamApiKey
2425
import io.getstream.android.core.api.model.value.StreamHttpClientInfoHeader
2526
import io.getstream.android.core.api.model.value.StreamUserId
@@ -28,6 +29,7 @@ import io.getstream.android.core.api.processing.StreamBatcher
2829
import io.getstream.android.core.api.processing.StreamRetryProcessor
2930
import io.getstream.android.core.api.processing.StreamSerialProcessingQueue
3031
import io.getstream.android.core.api.processing.StreamSingleFlightProcessor
32+
import io.getstream.android.core.api.serialization.StreamEventSerialization
3133
import io.getstream.android.core.api.socket.StreamConnectionIdHolder
3234
import io.getstream.android.core.api.socket.StreamWebSocketFactory
3335
import io.getstream.android.core.api.socket.listeners.StreamClientListener
@@ -103,6 +105,14 @@ fun createStreamClient(
103105
connectionIdHolder = connectionIdHolder,
104106
socketFactory = socketFactory,
105107
healthMonitor = healthMonitor,
108+
serializationConfig =
109+
StreamClientSerializationConfig.default(
110+
object : StreamEventSerialization<Unit> {
111+
override fun serialize(data: Unit): Result<String> = Result.success("")
112+
113+
override fun deserialize(raw: String): Result<Unit> = Result.success(Unit)
114+
}
115+
),
106116
batcher = batcher,
107117
)
108118
}

stream-android-core/src/main/java/io/getstream/android/core/api/StreamClient.kt

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ package io.getstream.android.core.api
1818
import io.getstream.android.core.annotations.StreamCoreApi
1919
import io.getstream.android.core.api.authentication.StreamTokenManager
2020
import io.getstream.android.core.api.authentication.StreamTokenProvider
21+
import io.getstream.android.core.api.http.StreamOkHttpInterceptors
2122
import io.getstream.android.core.api.log.StreamLoggerProvider
2223
import io.getstream.android.core.api.model.config.StreamClientSerializationConfig
24+
import io.getstream.android.core.api.model.config.StreamHttpConfig
2325
import io.getstream.android.core.api.model.config.StreamSocketConfig
2426
import io.getstream.android.core.api.model.connection.StreamConnectedUser
2527
import io.getstream.android.core.api.model.connection.StreamConnectionState
@@ -31,7 +33,7 @@ import io.getstream.android.core.api.processing.StreamBatcher
3133
import io.getstream.android.core.api.processing.StreamRetryProcessor
3234
import io.getstream.android.core.api.processing.StreamSerialProcessingQueue
3335
import io.getstream.android.core.api.processing.StreamSingleFlightProcessor
34-
import io.getstream.android.core.api.serialization.StreamClientEventSerialization
36+
import io.getstream.android.core.api.serialization.StreamEventSerialization
3537
import io.getstream.android.core.api.socket.StreamConnectionIdHolder
3638
import io.getstream.android.core.api.socket.StreamWebSocket
3739
import io.getstream.android.core.api.socket.StreamWebSocketFactory
@@ -40,6 +42,7 @@ import io.getstream.android.core.api.socket.monitor.StreamHealthMonitor
4042
import io.getstream.android.core.api.subscribe.StreamSubscription
4143
import io.getstream.android.core.api.subscribe.StreamSubscriptionManager
4244
import io.getstream.android.core.internal.client.StreamClientImpl
45+
import io.getstream.android.core.internal.serialization.StreamCompositeEventSerializationImpl
4346
import io.getstream.android.core.internal.serialization.StreamCompositeMoshiJsonSerialization
4447
import io.getstream.android.core.internal.serialization.StreamMoshiJsonSerializationImpl
4548
import io.getstream.android.core.internal.serialization.moshi.StreamCoreMoshiProvider
@@ -189,6 +192,7 @@ interface StreamClient {
189192
* @param tokenManager The token manager.
190193
* @param singleFlight The single-flight processor.
191194
* @param serialQueue The serial processing queue.
195+
* @param httpConfig The HTTP configuration.
192196
* @param retryProcessor The retry processor.
193197
* @param connectionIdHolder The connection ID holder.
194198
* @param socketFactory The WebSocket factory.
@@ -217,9 +221,10 @@ fun StreamClient(
217221
socketFactory: StreamWebSocketFactory,
218222
healthMonitor: StreamHealthMonitor,
219223
batcher: StreamBatcher<String>,
224+
// Http
225+
httpConfig: StreamHttpConfig? = null,
220226
// Serialization
221-
serializationConfig: StreamClientSerializationConfig =
222-
StreamClientSerializationConfig.defaults(),
227+
serializationConfig: StreamClientSerializationConfig,
223228
// Logging
224229
logProvider: StreamLoggerProvider = StreamLoggerProvider.defaultAndroidLogger(),
225230
): StreamClient {
@@ -248,6 +253,21 @@ fun StreamClient(
248253
StreamMoshiJsonSerializationImpl(StreamCoreMoshiProvider().builder {}.build()),
249254
serializationConfig.json,
250255
)
256+
257+
httpConfig?.apply {
258+
if (automaticInterceptors) {
259+
httpBuilder.apply {
260+
addInterceptor(StreamOkHttpInterceptors.clientInfo(clientInfoHeader))
261+
addInterceptor(StreamOkHttpInterceptors.apiKey(apiKey))
262+
addInterceptor(
263+
StreamOkHttpInterceptors.auth("jwt", tokenManager, compositeSerialization)
264+
)
265+
addInterceptor(StreamOkHttpInterceptors.connectionId(connectionIdHolder))
266+
}
267+
}
268+
configuredInterceptors.forEach { httpBuilder.addInterceptor(it) }
269+
}
270+
251271
return StreamClientImpl(
252272
userId = userId,
253273
scope = clientScope,
@@ -256,7 +276,6 @@ fun StreamClient(
256276
serialQueue = serialQueue,
257277
connectionIdHolder = connectionIdHolder,
258278
logger = clientLogger,
259-
retryProcessor = retryProcessor,
260279
mutableConnectionState = MutableStateFlow(StreamConnectionState.Idle),
261280
subscriptionManager = clientSubscriptionManager,
262281
socketSession =
@@ -271,8 +290,12 @@ fun StreamClient(
271290
),
272291
jsonSerialization = compositeSerialization,
273292
eventParser =
274-
serializationConfig.eventParser
275-
?: StreamClientEventSerialization(compositeSerialization),
293+
StreamCompositeEventSerializationImpl(
294+
internal =
295+
serializationConfig.eventParser
296+
?: StreamEventSerialization(compositeSerialization),
297+
external = serializationConfig.productEventSerializers,
298+
),
276299
healthMonitor = healthMonitor,
277300
batcher = batcher,
278301
internalSocket = socket,

stream-android-core/src/main/java/io/getstream/android/core/api/http/StreamOkHttpInterceptors.kt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,14 @@ package io.getstream.android.core.api.http
1818
import io.getstream.android.core.annotations.StreamCoreApi
1919
import io.getstream.android.core.api.authentication.StreamTokenManager
2020
import io.getstream.android.core.api.model.value.StreamApiKey
21+
import io.getstream.android.core.api.model.value.StreamHttpClientInfoHeader
2122
import io.getstream.android.core.api.serialization.StreamJsonSerialization
2223
import io.getstream.android.core.api.socket.StreamConnectionIdHolder
2324
import io.getstream.android.core.internal.http.interceptor.StreamApiKeyInterceptor
2425
import io.getstream.android.core.internal.http.interceptor.StreamAuthInterceptor
26+
import io.getstream.android.core.internal.http.interceptor.StreamClientInfoInterceptor
2527
import io.getstream.android.core.internal.http.interceptor.StreamConnectionIdInterceptor
28+
import io.getstream.android.core.internal.http.interceptor.StreamEndpointErrorInterceptor
2629
import okhttp3.Interceptor
2730

2831
// TODO: Think about better approach
@@ -59,6 +62,15 @@ object StreamOkHttpInterceptors {
5962
fun connectionId(connectionIdHolder: StreamConnectionIdHolder): Interceptor =
6063
StreamConnectionIdInterceptor(connectionIdHolder)
6164

65+
/**
66+
* Creates an OkHttp interceptor that adds the client info header to the request.
67+
*
68+
* @param clientInfoHeader The client info header to add.
69+
* @return An OkHttp interceptor.
70+
*/
71+
fun clientInfo(clientInfoHeader: StreamHttpClientInfoHeader): Interceptor =
72+
StreamClientInfoInterceptor(clientInfoHeader)
73+
6274
/**
6375
* Creates an OkHttp interceptor that adds the Stream API key to the request as a query
6476
* parameter.
@@ -67,4 +79,14 @@ object StreamOkHttpInterceptors {
6779
* @return An OkHttp interceptor.
6880
*/
6981
fun apiKey(apiKey: StreamApiKey): Interceptor = StreamApiKeyInterceptor(apiKey)
82+
83+
/**
84+
* Creates an OkHttp interceptor that parses and throws
85+
* [io.getstream.android.core.api.model.exceptions.StreamEndpointException] for error responses.
86+
*
87+
* @param jsonParser JSON parser used to decode error payloads when requests fail.
88+
* @return An OkHttp interceptor.
89+
*/
90+
fun error(jsonParser: StreamJsonSerialization): Interceptor =
91+
StreamEndpointErrorInterceptor(jsonParser)
7092
}

stream-android-core/src/main/java/io/getstream/android/core/api/model/config/StreamClientSerializationConfig.kt

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@
1616
package io.getstream.android.core.api.model.config
1717

1818
import io.getstream.android.core.annotations.StreamCoreApi
19-
import io.getstream.android.core.api.serialization.StreamClientEventSerialization
19+
import io.getstream.android.core.api.model.event.StreamClientWsEvent
2020
import io.getstream.android.core.api.serialization.StreamJsonSerialization
21+
import io.getstream.android.core.api.serialization.StreamEventSerialization
2122

2223
/**
2324
* Configuration for serialization and deserialization in the Stream client.
@@ -30,32 +31,64 @@ import io.getstream.android.core.api.serialization.StreamJsonSerialization
3031
data class StreamClientSerializationConfig
3132
private constructor(
3233
val json: StreamJsonSerialization? = null,
33-
val eventParser: StreamClientEventSerialization? = null,
34+
val eventParser: StreamEventSerialization<StreamClientWsEvent>? = null,
35+
val productEventSerializers: StreamEventSerialization<*>,
36+
val internalTypes: Set<String> = setOf("connection.ok", "connection.error", "health.check"),
37+
val alsoExternal: Set<String> = emptySet(),
3438
) {
3539
companion object {
3640
/**
3741
* Creates a default [StreamClientSerializationConfig]. Using the internal implementations.
3842
*
43+
* @param productEvents The product event serializers.
44+
* @param alsoExternal The event types to also parse as external.
3945
* @return A default [StreamClientSerializationConfig].
4046
*/
41-
fun defaults() = StreamClientSerializationConfig()
47+
fun <T> default(
48+
productEvents: StreamEventSerialization<T>,
49+
alsoExternal: Set<String> = emptySet(),
50+
) =
51+
StreamClientSerializationConfig(
52+
productEventSerializers = productEvents,
53+
alsoExternal = alsoExternal,
54+
)
4255

4356
/**
4457
* Creates a [StreamClientSerializationConfig] with the given JSON serialization.
4558
*
4659
* @param serialization The JSON serialization implementation.
60+
* @param productEvents The product event serializers.
61+
* @param alsoExternal The event types to also parse as external.
4762
* @return A [StreamClientSerializationConfig] with the given JSON serialization.
4863
*/
49-
fun json(serialization: StreamJsonSerialization) =
50-
StreamClientSerializationConfig(json = serialization)
64+
fun <T> json(
65+
serialization: StreamJsonSerialization,
66+
productEvents: StreamEventSerialization<T>,
67+
alsoExternal: Set<String> = emptySet(),
68+
) =
69+
StreamClientSerializationConfig(
70+
json = serialization,
71+
productEventSerializers = productEvents,
72+
alsoExternal = alsoExternal,
73+
)
5174

5275
/**
5376
* Creates a [StreamClientSerializationConfig] with the given event parsing.
5477
*
5578
* @param serialization The event parsing implementation.
79+
* @param productEvents The product event serializers.
80+
* @param alsoExternal The event types to also parse as external.
5681
* @return A [StreamClientSerializationConfig] with the given event parsing.
5782
*/
58-
fun event(serialization: StreamClientEventSerialization) =
59-
StreamClientSerializationConfig(eventParser = serialization)
83+
fun <T> event(
84+
serialization: StreamEventSerialization<StreamClientWsEvent>,
85+
productEvents: StreamEventSerialization<T>,
86+
alsoExternal: Set<String> = emptySet(),
87+
) =
88+
StreamClientSerializationConfig(
89+
eventParser = serialization,
90+
productEventSerializers = productEvents,
91+
alsoExternal = alsoExternal,
92+
)
6093
}
6194
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright (c) 2014-2025 Stream.io Inc. All rights reserved.
3+
*
4+
* Licensed under the Stream License;
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://github.com/GetStream/stream-core-android/blob/main/LICENSE
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.getstream.android.core.api.model.config
17+
18+
import okhttp3.Interceptor
19+
import okhttp3.OkHttpClient
20+
21+
/**
22+
* Configuration for HTTP clients in the Stream client.
23+
*
24+
* @param httpBuilder The HTTP client builder.
25+
* @param automaticInterceptors Whether to add automatic interceptors.
26+
* @param configuredInterceptors The configured interceptors.
27+
*/
28+
data class StreamHttpConfig(
29+
val httpBuilder: OkHttpClient.Builder,
30+
val automaticInterceptors: Boolean = true,
31+
val configuredInterceptors: Set<Interceptor> = emptySet(),
32+
)

stream-android-core/src/main/java/io/getstream/android/core/api/model/exceptions/StreamEndpointErrorData.kt

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package io.getstream.android.core.api.model.exceptions
1717

18+
import com.squareup.moshi.Json
1819
import com.squareup.moshi.JsonClass
1920
import io.getstream.android.core.annotations.StreamCoreApi
2021

@@ -40,12 +41,12 @@ import io.getstream.android.core.annotations.StreamCoreApi
4041
@StreamCoreApi
4142
@JsonClass(generateAdapter = true)
4243
public data class StreamEndpointErrorData(
43-
val code: Int,
44-
val duration: String,
45-
val message: String,
46-
val moreInfo: String,
47-
val statusCode: Int,
48-
val details: List<Int>,
49-
val unrecoverable: Boolean? = null,
50-
val exceptionFields: Map<String, String>? = null,
44+
@Json(name = "code") val code: Int,
45+
@Json(name = "duration") val duration: String? = null,
46+
@Json(name = "message") val message: String? = null,
47+
@Json(name = "more_info") val moreInfo: String? = null,
48+
@Json(name = "StatusCode") val statusCode: Int? = null,
49+
@Json(name = "details") val details: List<Int>? = null,
50+
@Json(name = "unrecoverable") val unrecoverable: Boolean? = null,
51+
@Json(name = "exception_fields") val exceptionFields: Map<String, String>? = null,
5152
)

stream-android-core/src/main/java/io/getstream/android/core/api/serialization/StreamClientEventSerialization.kt renamed to stream-android-core/src/main/java/io/getstream/android/core/api/serialization/StreamEventSerialization.kt

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,42 +20,45 @@ import io.getstream.android.core.api.model.event.StreamClientWsEvent
2020
import io.getstream.android.core.internal.serialization.StreamClientEventSerializationImpl
2121

2222
/**
23-
* Interface for serializing and deserializing [StreamClientWsEvent] objects.
23+
* Interface for serializing and deserializing product event objects.
2424
*
2525
* All operations return a [Result] so callers can safely propagate failures (e.g. malformed input,
2626
* I/O errors, schema mismatches) without throwing.
2727
*
28-
* @see StreamClientWsEvent for the event type handled by this interface.
28+
* @param T The product event type handled by this interface.
2929
*/
3030
@StreamCoreApi
31-
interface StreamClientEventSerialization {
31+
interface StreamEventSerialization<T> {
32+
3233
/**
33-
* Encodes a [StreamClientWsEvent] into a [String] suitable for transport or storage.
34+
* Encodes a product event into a [String] suitable for transport or storage.
3435
*
3536
* @param data The event to serialize.
3637
* @return `Result.success(String)` when encoding succeeds, or `Result.failure(Throwable)` when
3738
* the process fails.
3839
*/
39-
fun serialize(data: StreamClientWsEvent): Result<String>
40+
fun serialize(data: T): Result<String>
4041

4142
/**
42-
* Decodes a [String] into a [StreamClientWsEvent].
43+
* Decodes a product event from a [String] representation.
4344
*
44-
* @param raw The serialized input to decode (e.g. a JSON string).
45-
* @return `Result.success(StreamClientWsEvent)` on success, or `Result.failure(Throwable)` if
46-
* decoding fails.
45+
* @param raw The string to deserialize.
46+
* @return `Result.success(T)` when decoding succeeds, or `Result.failure(Throwable)` when the
47+
* process fails.
4748
*/
48-
fun deserialize(raw: String): Result<StreamClientWsEvent>
49+
fun deserialize(raw: String): Result<T>
4950
}
5051

52+
53+
5154
/**
52-
* Creates a new [StreamClientEventSerialization] instance.
55+
* Creates a new [StreamEventSerialization] instance that can serialize [io.getstream.android.core.api.model.event.StreamClientWsEvent] objects.
5356
*
5457
* @param jsonParser The [StreamJsonSerialization] to use for JSON serialization and
5558
* deserialization.
56-
* @return A new [StreamClientEventSerialization] instance.
59+
* @return A new [StreamEventSerialization] instance.
5760
*/
5861
@StreamCoreApi
59-
fun StreamClientEventSerialization(
62+
fun StreamEventSerialization(
6063
jsonParser: StreamJsonSerialization
61-
): StreamClientEventSerialization = StreamClientEventSerializationImpl(jsonParser)
64+
): StreamEventSerialization<StreamClientWsEvent> = StreamClientEventSerializationImpl(jsonParser)

stream-android-core/src/main/java/io/getstream/android/core/api/socket/listeners/StreamClientListener.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ package io.getstream.android.core.api.socket.listeners
1717

1818
import io.getstream.android.core.annotations.StreamCoreApi
1919
import io.getstream.android.core.api.model.connection.StreamConnectionState
20-
import io.getstream.android.core.api.model.event.StreamClientWsEvent
2120

2221
/**
2322
* Listener interface for Feeds socket events.
@@ -39,7 +38,7 @@ interface StreamClientListener {
3938
*
4039
* @param event The event received from the WebSocket.
4140
*/
42-
fun onEvent(event: StreamClientWsEvent) {}
41+
fun onEvent(event: Any) {}
4342

4443
/**
4544
* Called when an error occurs on the client.

0 commit comments

Comments
 (0)