Skip to content

Commit

Permalink
Implementing MetricsManager
Browse files Browse the repository at this point in the history
  • Loading branch information
mliao95 committed Aug 14, 2024
1 parent af1cce9 commit 9a89028
Show file tree
Hide file tree
Showing 10 changed files with 197 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,3 @@ class ApiService {
.create(ApiInterface::class.java)
}
}












2 changes: 2 additions & 0 deletions chat-sdk/src/main/java/com/amazon/connect/chat/sdk/Config.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ object Config {
val region: Regions = Regions.US_WEST_2
val agentName = "AGENT"
val customerName = "CUSTOMER"
val isDevMode: Boolean = true
val disableCsm: Boolean = false
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.amazon.connect.chat.sdk.ChatSessionImpl
import com.amazon.connect.chat.sdk.network.APIClient
import com.amazon.connect.chat.sdk.network.AWSClient
import com.amazon.connect.chat.sdk.network.WebSocketManager
import com.amazon.connect.chat.sdk.network.MetricsManager
import com.amazon.connect.chat.sdk.repository.ChatService
import com.amazon.connect.chat.sdk.repository.ChatServiceImpl
import com.amazon.connect.chat.sdk.repository.ConnectionDetailsProvider
Expand All @@ -32,11 +33,11 @@ object ChatModule {
@Provides
@Singleton
fun provideChatService(
apiClient: APIClient,
awsClient: AWSClient,
connectionDetailsProvider: ConnectionDetailsProvider
connectionDetailsProvider: ConnectionDetailsProvider,
metricsManager: MetricsManager
): ChatService {
return ChatServiceImpl(apiClient, awsClient, connectionDetailsProvider)
return ChatServiceImpl(awsClient, connectionDetailsProvider, metricsManager)
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import com.amazon.connect.chat.sdk.network.AWSClient
import com.amazon.connect.chat.sdk.network.AWSClientImpl
import com.amazon.connect.chat.sdk.network.ApiUrl
import com.amazon.connect.chat.sdk.network.MetricsInterface
import com.amazon.connect.chat.sdk.network.MetricsManager
import com.amazon.connect.chat.sdk.Config
import com.amazonaws.services.connectparticipant.AmazonConnectParticipantClient
import dagger.Module
import dagger.Provides
Expand Down Expand Up @@ -56,7 +58,23 @@ object NetworkModule {
@Provides
@Singleton
fun provideMetricsInterface(retrofitBuilder: Retrofit.Builder): MetricsInterface {
return createService(MetricsInterface::class.java, retrofitBuilder)
return if (Config.isDevMode) {
createService(MetricsInterface::class.java, retrofitBuilder, url="https://f9cskafqk3.execute-api.us-west-2.amazonaws.com/devo/")
} else {
createService(MetricsInterface::class.java, retrofitBuilder, url="https://ieluqbvv.telemetry.connect.us-west-2.amazonaws.com/prod/")
}
}

/**
* Provides a singleton instance of MetricsInterface.
*
* @param retrofitBuilder The Retrofit.Builder instance for creating the service.
* @return An instance of MetricsInterface.
*/
@Provides
@Singleton
fun provideMetricsManager(apiClient: APIClient): MetricsManager {
return MetricsManager(apiClient = apiClient)
}

/**
Expand Down Expand Up @@ -108,12 +126,12 @@ object NetworkModule {
* @param retrofitBuilder The Retrofit.Builder instance for creating the service.
* @return An instance of the specified service class.
*/
private fun <T> createService(clazz: Class<T>, retrofitBuilder: Retrofit.Builder): T {
private fun <T> createService(clazz: Class<T>, retrofitBuilder: Retrofit.Builder, url: String? = null): T {
// Check if the service has an annotation
val apiUrlAnnotation = clazz.annotations.find { it is ApiUrl } as ApiUrl?
// Take the URL value, otherwise use the default
val url = apiUrlAnnotation?.url ?: defaultApiUrl
val apiUrl = url ?: (apiUrlAnnotation?.url ?: defaultApiUrl)
// Create the service using the extracted URL
return retrofitBuilder.baseUrl(url).build().create(clazz)
return retrofitBuilder.baseUrl(apiUrl).build().create(clazz)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.amazon.connect.chat.sdk.model

import kotlinx.serialization.Serializable

@Serializable
enum class MetricName {
CreateParticipantConnection,
SendMessage
}

data class Metric(
val dimensions: List<Dimension>,
val metricName: String,
val namespace: String,
val optionalDimensions: List<Dimension>,
val timestamp: String,
val unit: String,
val value: Int
)

data class Dimension(
val name: String,
val value: String
)

data class MetricRequestBody(
val metricList: List<Metric>,
val metricNamespace: String
)
Original file line number Diff line number Diff line change
@@ -1,12 +1,29 @@
package com.amazon.connect.chat.sdk.network

import com.amazon.connect.chat.sdk.model.MetricRequestBody
import retrofit2.Call
import retrofit2.Callback

import retrofit2.Response
import javax.inject.Inject

class APIClient @Inject constructor(
private val metricsInterface: MetricsInterface
) {
fun sendMetrics(){
// metricsInterface.sendMetrics
fun sendMetrics(metricRequestBody: MetricRequestBody, callback: (Response<Any>?) -> Unit) {
val call = metricsInterface.sendMetrics(metricRequestBody)

call.enqueue(object: Callback<Any> {
override fun onResponse(
call: Call<Any>,
response: Response<Any>
) {
callback(response)
}

override fun onFailure(call: Call<Any>, t: Throwable) {
callback(null)
}
})
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package com.amazon.connect.chat.sdk.network

import com.amazon.connect.chat.sdk.model.ConnectionDetails
import com.amazon.connect.chat.sdk.model.MetricRequestBody
import retrofit2.Call
import retrofit2.http.GET
import retrofit2.http.Query
import retrofit2.http.Body
import retrofit2.http.POST

@ApiUrl("https://api.aws.example.com/")
interface MetricsInterface {

@GET("someAwsEndpoint")
fun getAwsData(@Query("param") param: String): Call<ConnectionDetails>
@POST("put-metrics/")
fun sendMetrics(@Body metricRequestBody: MetricRequestBody): Call<Any>
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,95 @@
package com.amazon.connect.chat.sdk.network

import com.amazon.connect.chat.sdk.model.MetricRequestBody
import javax.inject.Inject
import java.util.*
import kotlin.concurrent.timer
import com.amazon.connect.chat.sdk.utils.MetricsUtils
import com.amazon.connect.chat.sdk.model.MetricName
import com.amazon.connect.chat.sdk.model.Metric
import com.amazon.connect.chat.sdk.model.Dimension

class MetricsManager @Inject constructor(private val metricsInterface: MetricsInterface) {
class MetricsManager @Inject constructor(
private var apiClient: APIClient
) {
private var metricList: MutableList<Metric> = mutableListOf()
private var isMonitoring: Boolean = false
private var timer: Timer? = null
private var shouldRetry: Boolean = true

}
init {
if (!MetricsUtils.isCsmDisabled()) {
monitorAndSendMetrics()
}
}

@Synchronized
private fun monitorAndSendMetrics() {
if (isMonitoring) return
isMonitoring = true

timer = timer(initialDelay = 10000, period = 10000) {
if (metricList.isNotEmpty()) {
val metricRequestBody = createMetricRequestBody()
apiClient.sendMetrics(metricRequestBody) { response ->
if (response != null && response.isSuccessful) {
metricList = mutableListOf()
isMonitoring = false
timer?.cancel()
} else {
// We should retry once after 10s delay, otherwise we will send the missed
// payload with the next batch of metrics
if (shouldRetry) {
shouldRetry = false
} else {
isMonitoring = false
shouldRetry = true
timer?.cancel()
}
}
}
}
}
}

private fun createMetricRequestBody(): MetricRequestBody {
return MetricRequestBody(
metricNamespace = "chat-widget",
metricList = metricList
)
}

private fun getCountMetricDimensions(): List<Dimension> {
return listOf(
Dimension(name = "WidgetType", value = "MobileChatSDK"),
Dimension(name = "SDKPlatform", value = "Android"),
Dimension(name = "Category", value = "API"),
Dimension(name = "Metric", value = "Count")
)
}

fun addCountMetric(metricName: MetricName) {
val currentTime = MetricsUtils.getCurrentMetricTimestamp()
val countMetricDimensions = getCountMetricDimensions()
val countMetric = Metric(
dimensions = countMetricDimensions,
metricName = metricName.name,
namespace = "chat-widget",
optionalDimensions = emptyList(),
timestamp = currentTime,
unit = "Count",
value = 1
)

addMetric(countMetric)
}

private fun addMetric(metric: Metric) {
if (MetricsUtils.isCsmDisabled()) {
return
}

metricList.add(0, metric)
monitorAndSendMetrics()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ package com.amazon.connect.chat.sdk.repository
import android.util.Log
import com.amazon.connect.chat.sdk.model.ChatDetails
import com.amazon.connect.chat.sdk.model.GlobalConfig
import com.amazon.connect.chat.sdk.network.APIClient
import com.amazon.connect.chat.sdk.model.MetricName
import com.amazon.connect.chat.sdk.network.AWSClient
import com.amazon.connect.chat.sdk.network.MetricsManager
import javax.inject.Inject

interface ChatService {
Expand All @@ -24,9 +25,10 @@ interface ChatService {
}

class ChatServiceImpl @Inject constructor(
private val apiClient: APIClient,
private val awsClient: AWSClient,
private val connectionDetailsProvider: ConnectionDetailsProvider) : ChatService {
private val connectionDetailsProvider: ConnectionDetailsProvider,
private val metricsManager: MetricsManager
) : ChatService {

override fun configure(config: GlobalConfig) {
awsClient.configure(config)
Expand All @@ -36,6 +38,7 @@ class ChatServiceImpl @Inject constructor(
return runCatching {
connectionDetailsProvider.updateChatDetails(chatDetails)
val connectionDetails = awsClient.createParticipantConnection(chatDetails.participantToken).getOrThrow()
metricsManager.addCountMetric(MetricName.CreateParticipantConnection);
connectionDetailsProvider.updateConnectionDetails(connectionDetails)
Log.d("ChatServiceImpl", "Participant Connected")
true
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package com.amazon.connect.chat.sdk.utils

import java.text.SimpleDateFormat
import java.util.*
import com.amazon.connect.chat.sdk.Config

object MetricsUtils {
fun getCurrentMetricTimestamp(): String {
val formatter = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX", Locale.getDefault())
formatter.timeZone = TimeZone.getTimeZone("UTC")
val now = Date()
return formatter.format(now)
}

fun isCsmDisabled(): Boolean {
return Config.disableCsm
}
}

0 comments on commit 9a89028

Please sign in to comment.