Skip to content

Commit

Permalink
feat(unified_activity): AND-6698 Unified Activity Websocket (#4092)
Browse files Browse the repository at this point in the history
  • Loading branch information
dtverdota-bc committed Nov 15, 2022
1 parent 086216b commit 34bdc95
Show file tree
Hide file tree
Showing 23 changed files with 352 additions and 3,683 deletions.
3 changes: 3 additions & 0 deletions app/build.gradle
Expand Up @@ -116,6 +116,7 @@ android {
buildConfigField "String", "EVERYPAY_HOST_URL", "\"${buildProperties.secrets['prodEverypayHost']}\""
buildConfigField "String", "STATUS_API_URL", "\"${buildProperties.secrets['prodApiStatus']}\""
buildConfigField "String", "NABU_WEBSOCKET_URL", "\"${buildProperties.secrets['prodNabuWebsocket']}\""
buildConfigField "String", "UNIFIED_ACTIVITY_WS_URL", "\"${buildProperties.secrets['prodUAWebsocket']}\""
buildConfigField "String", "HORIZON_URL", "\"${buildProperties.secrets['prodHorizonUrl']}\""
buildConfigField "String", "EXCHANGE_LAUNCH_URL", "\"${buildProperties.secrets['prodExchangeLaunch']}\""
buildConfigField "String", "BROKERAGE_SUCCESS", "\"${buildProperties.secrets['brokerageSuccessProduction']}\""
Expand Down Expand Up @@ -172,6 +173,7 @@ android {
buildConfigField "String", "EVERYPAY_HOST_URL", "\"${buildProperties.secrets['prodEverypayHost']}\""
buildConfigField "String", "STATUS_API_URL", "\"${buildProperties.secrets['prodApiStatus']}\""
buildConfigField "String", "NABU_WEBSOCKET_URL", "\"${buildProperties.secrets['prodNabuWebsocket']}\""
buildConfigField "String", "UNIFIED_ACTIVITY_WS_URL", "\"${buildProperties.secrets['prodUAWebsocket']}\""
buildConfigField "String", "HORIZON_URL", "\"${buildProperties.secrets['prodHorizonUrl']}\""
buildConfigField "String", "EXCHANGE_LAUNCH_URL", "\"${buildProperties.secrets['prodExchangeLaunch']}\""
buildConfigField "String", "COINS_WEBSOCKET_URL", "\"${buildProperties.secrets['prodCoinsWebsocket']}\""
Expand Down Expand Up @@ -229,6 +231,7 @@ android {
buildConfigField "String", "EVERYPAY_HOST_URL", "\"${buildProperties.secrets['stagingEverypayHost']}\""
buildConfigField "String", "STATUS_API_URL", "\"${buildProperties.secrets['stagingApiStatus']}\""
buildConfigField "String", "NABU_WEBSOCKET_URL", "\"${buildProperties.secrets['stagingNabuWebsocket']}\""
buildConfigField "String", "UNIFIED_ACTIVITY_WS_URL", "\"${buildProperties.secrets['stagingUAWebsocket']}\""
buildConfigField "String", "HORIZON_URL", "\"${buildProperties.secrets['stagingHorizonUrl']}\""
buildConfigField "String", "EXCHANGE_LAUNCH_URL", "\"${buildProperties.secrets['stagingExchangeLaunch']}\""
buildConfigField "String", "COINS_WEBSOCKET_URL", "\"${buildProperties.secrets['stagingCoinsWebsocket']}\""
Expand Down
3 changes: 3 additions & 0 deletions app/secretsDefaults.properties
Expand Up @@ -14,6 +14,9 @@ stagingApiStatus=https://www.blockchain-status.com
# Nabu Websocket URL
prodNabuWebsocket=wss://ws.blockchain.info/nabu-gateway/markets/quotes
stagingNabuWebsocket=wss://ws.blockchain.info/nabu-gateway/markets/quotes
# Unified Activity Websocket URL
prodUAWebsocket=
stagingUAWebsocket=
# Horizon URL
prodHorizonUrl=https://api.blockchain.info/stellar
stagingHorizonUrl=https://api.blockchain.info/stellar
Expand Down
Expand Up @@ -18,6 +18,7 @@ val urls = mapOf(
"explorer-api" to BuildConfig.EXPLORER_URL,
"blockchain-api" to BuildConfig.API_URL,
"wallet-pubkey-api" to "${BuildConfig.API_URL}wallet-pubkey/",
"unified-activity-ws" to "${BuildConfig.UNIFIED_ACTIVITY_WS_URL}",
"nabu-api" to "${BuildConfig.API_URL}nabu-gateway/",
"wallet-helper-url" to BuildConfig.WALLET_HELPER_URL,
CHECKMARKET_URL to BuildConfig.CHECKMARKET_URL,
Expand Down
30 changes: 30 additions & 0 deletions blockchainApi/src/main/java/com/blockchain/api/koin.kt
Expand Up @@ -40,10 +40,13 @@ import com.blockchain.api.paymentmethods.PaymentMethodsApi
import com.blockchain.api.payments.PaymentsApi
import com.blockchain.api.referral.ReferralApi
import com.blockchain.api.selfcustody.SelfCustodyApi
import com.blockchain.api.selfcustody.activity.ActivityRequest
import com.blockchain.api.selfcustody.activity.ActivityResponse
import com.blockchain.api.selfcustody.activity.activityDetailSerializer
import com.blockchain.api.selfcustody.activity.activityIconSerializer
import com.blockchain.api.selfcustody.activity.activityViewItemSerializer
import com.blockchain.api.selfcustody.activity.stackComponentSerializer
import com.blockchain.api.services.ActivityWebSocketService
import com.blockchain.api.services.AddressMappingService
import com.blockchain.api.services.AddressVerificationApiService
import com.blockchain.api.services.AnalyticsService
Expand Down Expand Up @@ -78,12 +81,18 @@ import com.blockchain.api.trade.TradeApi
import com.blockchain.api.txlimits.TxLimitsApi
import com.blockchain.api.wallet.WalletApi
import com.blockchain.api.watchlist.WatchlistApi
import com.blockchain.koin.applicationScope
import com.blockchain.koin.authOkHttpClient
import com.blockchain.koin.kotlinJsonConverterFactory
import com.blockchain.koin.kotlinXApiRetrofit
import com.blockchain.koin.kotlinXCoinApiRetrofit
import com.blockchain.koin.payloadScopeQualifier
import com.blockchain.network.modules.OkHttpLoggingInterceptors
import com.blockchain.network.websocket.Options
import com.blockchain.network.websocket.autoRetry
import com.blockchain.network.websocket.debugLog
import com.blockchain.network.websocket.newBlockchainWebSocket
import com.blockchain.network.websocket.toJsonSocket
import com.blockchain.serializers.BigDecimalSerializer
import com.blockchain.serializers.BigIntSerializer
import com.blockchain.serializers.IsoDateSerializer
Expand Down Expand Up @@ -463,13 +472,34 @@ val blockchainApiModule = module {
}

scope(payloadScopeQualifier) {

scoped {
val api = get<Retrofit>(walletPubkeyApi).create(SelfCustodyApi::class.java)
DynamicSelfCustodyService(
selfCustodyApi = api,
credentials = get()
)
}

scoped {
val webSocket = get<OkHttpClient>()
.newBlockchainWebSocket(
options = Options(url = getBaseUrl("unified-activity-ws"))
)
.autoRetry()
.debugLog("ACTIVITY_LOG")
.toJsonSocket(
json = get(),
outgoingAdapter = ActivityRequest.serializer(),
incomingAdapter = ActivityResponse.serializer()
)
ActivityWebSocketService(
webSocket = webSocket,
activityCacheService = get(),
credentials = get(),
wsScope = get(applicationScope)
)
}
}
}

Expand Down

This file was deleted.

@@ -1,6 +1,5 @@
package com.blockchain.api.selfcustody

import com.blockchain.api.selfcustody.activity.ActivityResponse
import com.blockchain.outcome.Outcome
import retrofit2.http.Body
import retrofit2.http.POST
Expand Down Expand Up @@ -32,9 +31,6 @@ interface SelfCustodyApi {
@Body request: TransactionHistoryRequest
): Outcome<Exception, TransactionHistoryResponse>

@POST("activity")
suspend fun getActivity(@Body request: ActivityRequest): Outcome<Exception, ActivityResponse>

@POST("buildTx")
suspend fun buildTransaction(@Body request: BuildTxRequest): Outcome<Exception, BuildTxResponse>

Expand Down
@@ -0,0 +1,27 @@
package com.blockchain.api.selfcustody.activity

import com.blockchain.api.selfcustody.AuthInfo
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class ActivityRequest(
@SerialName("auth")
val auth: AuthInfo,
@SerialName("params")
val params: ActivityRequestParams,
@SerialName("action")
val action: String,
@SerialName("channel")
val channel: String
)

@Serializable
data class ActivityRequestParams(
@SerialName("timezoneIana")
val timezone: String,
@SerialName("fiatCurrency")
val fiatCurrency: String,
@SerialName("acceptLanguage")
val acceptLanguage: String
)
Expand Up @@ -5,10 +5,24 @@ import kotlinx.serialization.Serializable

@Serializable
data class ActivityResponse(
@SerialName("seqnum")
val seqnum: Int,
@SerialName("event")
val event: String,
@SerialName("channel")
val channel: String,
@SerialName("data")
val activityData: NetworkActivityResponse
)

@Serializable
data class NetworkActivityResponse(
@SerialName("network")
val network: String,
@SerialName("pubKey")
val pubKey: String,
@SerialName("activity")
val activity: List<ActivityItemDto>,
@SerialName("nextPage")
val nextPage: String?
val activity: List<ActivityItemDto>
)

@Serializable
Expand All @@ -19,8 +33,6 @@ data class ActivityItemDto(
val externalUrl: String,
@SerialName("item")
val summary: ActivityViewItemDto,
@SerialName("detail")
val detail: ActivityDetailGroupsDto,
@SerialName("state")
val status: String,
@SerialName("timestamp")
Expand Down
@@ -0,0 +1,7 @@
package com.blockchain.api.services

import com.blockchain.api.selfcustody.activity.ActivityResponse

interface ActivityCacheService {
fun addOrUpdateActivityItems(items: ActivityResponse)
}
@@ -0,0 +1,59 @@
package com.blockchain.api.services

import com.blockchain.api.selfcustody.AuthInfo
import com.blockchain.api.selfcustody.activity.ActivityRequest
import com.blockchain.api.selfcustody.activity.ActivityRequestParams
import com.blockchain.api.selfcustody.activity.ActivityResponse
import com.blockchain.network.websocket.WebSocket
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import kotlinx.coroutines.rx3.asFlow

class ActivityWebSocketService(
private val webSocket: WebSocket<ActivityRequest, ActivityResponse>,
private val activityCacheService: ActivityCacheService,
private val credentials: SelfCustodyServiceAuthCredentials,
private val wsScope: CoroutineScope
) {
private val authInfo: AuthInfo
get() = AuthInfo(
guidHash = credentials.hashedGuid,
sharedKeyHash = credentials.hashedSharedKey,
)

fun open() {
webSocket.open()
}

fun send(fiatCurrency: String, acceptLanguage: String, timeZone: String) {
webSocket.send(
ActivityRequest(
auth = authInfo,
params = ActivityRequestParams(
timezone = timeZone,
fiatCurrency = fiatCurrency,
acceptLanguage = acceptLanguage // "en-GB;q=1.0, en"
),
action = UNIFIED_ACTIVITY_WS_ACTION,
channel = UNIFIED_ACTIVITY_WS_CHANNEL
)
)
}

suspend fun subscribeToActivity() = wsScope.launch {
webSocket.responses.asFlow()
.collect {
activityCacheService.addOrUpdateActivityItems(it)
}
}

fun stop() {
webSocket.close()
}

companion object {
private const val UNIFIED_ACTIVITY_WS_ACTION = "subscribe"
private const val UNIFIED_ACTIVITY_WS_CHANNEL = "activity"
}
}
@@ -1,9 +1,6 @@
package com.blockchain.api.services

import com.blockchain.api.selfcustody.AccountInfo
import com.blockchain.api.selfcustody.ActivityPubKeyInfo
import com.blockchain.api.selfcustody.ActivityRequest
import com.blockchain.api.selfcustody.ActivityRequestParams
import com.blockchain.api.selfcustody.AddSubscriptionRequest
import com.blockchain.api.selfcustody.AddressesRequest
import com.blockchain.api.selfcustody.AuthInfo
Expand Down Expand Up @@ -135,31 +132,6 @@ class DynamicSelfCustodyService(
)
)

suspend fun getActivity(
fiatCurrency: String,
currency: String,
pubKey: String,
acceptLanguage: String,
timeZone: String,
nextPage: String?
) = selfCustodyApi.getActivity(
request = ActivityRequest(
auth = authInfo,
params = ActivityRequestParams(
timezone = timeZone,
fiatCurrency = fiatCurrency,
acceptLanguage = acceptLanguage, // "en-GB;q=1.0, en"
networkTicker = currency,
pubKeyInfo = ActivityPubKeyInfo(
pubKey = pubKey,
style = PubKeyStyle.SINGLE,
descriptor = "legacy" // TODO(dtverdota): what values are acceptable here?
)
),
nextPage = nextPage
)
)

suspend fun buildTransaction(
currency: String,
accountIndex: Int = 0,
Expand Down
Expand Up @@ -9,7 +9,7 @@ sealed interface ActivityIntent : Intent<ActivityModelState> {

data class FilterSearch(val term: String) : ActivityIntent {
override fun isValidFor(modelState: ActivityModelState): Boolean {
return modelState.activityPage is DataResource.Data
return modelState.activityItems is DataResource.Data
}
}
}
Expand Up @@ -3,10 +3,10 @@ package com.blockchain.home.presentation.activity.list
import com.blockchain.commonarch.presentation.mvi_v2.ModelState
import com.blockchain.data.DataResource
import com.blockchain.home.presentation.SectionSize
import com.blockchain.unifiedcryptowallet.domain.activity.model.UnifiedActivityPage
import com.blockchain.unifiedcryptowallet.domain.activity.model.UnifiedActivityItem

data class ActivityModelState(
val activityPage: DataResource<UnifiedActivityPage> = DataResource.Loading,
val activityItems: DataResource<List<UnifiedActivityItem>> = DataResource.Loading,
val sectionSize: SectionSize = SectionSize.All,
val filterTerm: String = ""
) : ModelState

0 comments on commit 34bdc95

Please sign in to comment.