Feature ap bank search screen (#7933)

Co-authored-by: Aparna Vadlamani <aparna.vadlamani@navi.com>
This commit is contained in:
Sangaraboina Rishvik Vardhan
2023-09-21 15:16:10 +05:30
committed by GitHub
parent 56348a4019
commit ffa2468a1b
26 changed files with 286 additions and 176 deletions

View File

@@ -0,0 +1,30 @@
package com.navi.ap.common.deserializer
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonElement
import com.navi.ap.common.models.actions.UpdateDataViaHandleAction
import com.navi.common.uitron.deserializer.UiTronActionDeserializer
import com.navi.common.uitron.util.ApActionType
import com.navi.uitron.model.data.UiTronAction
import java.lang.reflect.Type
/** AP specific actions which cannot be placed in common module should be added here **/
class ApUiTronActionDeserializer : UiTronActionDeserializer() {
override fun deserialize(
json: JsonElement?,
typeOfT: Type?,
context: JsonDeserializationContext?
): UiTronAction? {
json?.let {
val jsonObject = it.asJsonObject
val type = jsonObject["type"] ?: return null
return when (type.asString) {
ApActionType.UpdateDataViaHandleAction.name ->
context?.deserialize(jsonObject, UpdateDataViaHandleAction::class.java)
else -> super.deserialize(json, typeOfT, context)
}
}
return null
}
}

View File

@@ -187,7 +187,7 @@ fun getFillApplicationRequestBody(
Field(item.name.orEmpty(), output)
}
SourceType.HANDLE_OBJECT.name -> {
val obj: Any? = viewModel.handle[item.name.orEmpty()]
val obj: Any? = viewModel.handle[item.sourceProperty.orEmpty()]
val output = readJsonPath(obj.toJson(), item.jsonPath.orEmpty())
Field(item.name.orEmpty(), output)
}

View File

@@ -21,15 +21,19 @@ fun lambdaApiActionHandler(
viewModel.resendTelcoOtp(lambdaApiAction = lambdaApiAction)
}
LambdaType.LOAN_OFFER_DETAILS.name -> {
val map = getResolvedFieldValue(fields = lambdaApiAction.fields, viewModel = viewModel)
val map = getResolvedFieldValue(fields = lambdaApiAction.fields, handle = viewModel.handle)
viewModel.fetchOfferDetails(map)
}
LambdaType.APPLY_LOAN.name -> {
val map = getResolvedFieldValue(fields = lambdaApiAction.fields, viewModel = viewModel)
val map = getResolvedFieldValue(fields = lambdaApiAction.fields, handle = viewModel.handle)
viewModel.applyLoan(map)
}
LambdaType.FETCH_FINARKEIN_CONSENT_DATA.name -> {
viewModel.fetchFinarkeinConsentData(lambdaApiAction)
LambdaType.FETCH_CONSENT_DATA.name -> {
viewModel.fetchConsentData(lambdaApiAction)
}
LambdaType.FETCH_BANKS.name -> {
val map = getResolvedFieldValue(fields = lambdaApiAction.fields, handle = viewModel.handle)
viewModel.fetchAllBanks(map)
}
LambdaType.FETCH_RPD_TOKEN.name -> {
viewModel.fetchRpdToken(viewModel, lambdaApiAction)

View File

@@ -58,7 +58,7 @@ private fun handleLaunchIntentAction(
var uri = launchIntentAction.value.intentUriString?.value
launchIntentAction.value.intentUriString?.let {
val resolvedValues =
getResolvedFieldValue(fields = listOf(it), viewModel = viewModel)
getResolvedFieldValue(fields = listOf(it), handle = viewModel.handle)
uri = resolvedValues[it.name]?.toString()
}
val intent =

View File

@@ -14,7 +14,7 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import com.navi.ap.common.models.lambdamodels.FinarkeinConsentResponse
import com.navi.ap.common.models.lambdamodels.ConsentResponse
import com.navi.ap.common.sdk.helper.FinarkeinHelper
import com.navi.ap.common.ui.ApplicationPlatformActivity
import com.navi.ap.common.viewmodel.LambdaVM
@@ -69,9 +69,9 @@ private fun handleThirdPartySdkAction(
}
SDKType.FINARKEIN.name -> {
(action.value as? FinarkeinSDKAction)?.let {
val finarkeinHelper = FinarkeinHelper(anubhavSDKResult, viewModel, action.value)
val finarkeinHelper = FinarkeinHelper(anubhavSDKResult)
(viewModel.handle.get(FINARKEIN_CONSENT_DATA.getInputId())
as? FinarkeinConsentResponse)
as? ConsentResponse)
?.let { finarkeinHelper.openFinarkein(it) }
}
@@ -99,3 +99,12 @@ private fun GetAnubhavResultLauncher(
}
}
}
fun List<String?>.getIncomeVerificationAvailableSDK(): String?{
forEach {
when(it){
SDKType.FINARKEIN.name -> return it
}
}
return null
}

View File

@@ -0,0 +1,30 @@
package com.navi.ap.common.models.actions
import android.os.Parcelable
import com.navi.ap.utils.getResolvedFieldValue
import com.navi.ap.utils.injector.PathInjector
import com.navi.common.uitron.model.action.FillApiData
import com.navi.uitron.model.data.ActionDetails
import com.navi.uitron.model.data.UiTronAction
import com.navi.uitron.model.data.UiTronData
import com.navi.uitron.model.ui.BaseProperty
import kotlinx.parcelize.Parcelize
@Parcelize
data class UpdateDataViaHandleAction(val dataList: List<FillApiData>? = null, val viewDataList: List<ViewData>? = null) : UiTronAction() {
override suspend fun manageAction(actionDetails: ActionDetails) {
val action = actionDetails.uiTronAction as UpdateDataViaHandleAction
val map = getResolvedFieldValue(dataList, actionDetails.handle)
action.viewDataList?.forEach {
val updatedUiTronData = PathInjector<UiTronData, Map<String, Any?>>().injectData(it.uiTronData, map)
actionDetails.handle[it.layoutId + BaseProperty.DATA_SUFFIX] = updatedUiTronData
}
}
@Parcelize
data class ViewData(
val layoutId: String? = null,
val uiTronData: UiTronData? = null
) : Parcelable
}

View File

@@ -19,11 +19,9 @@ data class BankDataResponse(
data class Bank(
@SerializedName("name") val name: String? = null,
@SerializedName("code") val code: String? = null,
@SerializedName("message") val message: String? = null,
@SerializedName("serviceable") val serviceable: Boolean = true,
@SerializedName("suggested") val suggested: Boolean = true,
@SerializedName("title") val title: String? = null,
@SerializedName("iconUrl") val iconUrl: String? = null,
@SerializedName("aaFipId") val fipId: String? = null,
@SerializedName("digitapInstitutionId") val digitapInstitutionId: String? = null
@SerializedName("thirdPartyServiceProviders") val thirdPartyServiceProviders: List<String?>? = null
) : Parcelable
}

View File

@@ -5,19 +5,21 @@ import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
@Parcelize
data class FinarkeinConsentResponse(
@SerializedName("consent_handle") val consentHandle: String? = null,
@SerializedName("redirect_url") val webViewBaseUrl: String? = null,
@SerializedName("customer_reference_id") val customerReferenceId: String? = null,
@SerializedName("fip_id") val fipId: String? = null,
@SerializedName("phone_number") val phoneNumber: String? = null
) : Parcelable
data class ConsentResponse(
@SerializedName("fipToken") val fipToken: String? = null,
@SerializedName("consent") val consent: Consent? = null
) : Parcelable {
@Parcelize
data class Consent(
@SerializedName("consent_handle") val consentHandle: String? = null,
@SerializedName("redirect_url") val webViewBaseUrl: String? = null,
@SerializedName("customer_reference_id") val customerReferenceId: String? = null,
@SerializedName("fip_id") val fipId: String? = null,
@SerializedName("phone_number") val phoneNumber: String? = null
) : Parcelable
}
data class FinarkeinConsentRequest(
@SerializedName("phone_number") val phoneNumber: String? = null,
@SerializedName("fip_id") val fipId: String? = null,
@SerializedName("service_provider") val serviceProvider: String? = "FINARKEIN",
@SerializedName("initiated_by") val initiatedBy: String?
data class ConsentRequest(
@SerializedName("service_provider") val serviceProvider: String? = null,
@SerializedName("bank_name") val bankName: String? = null
)
data class FinarkeinSessionResponse(@SerializedName("session_id") val sessionId: String? = null)

View File

@@ -32,14 +32,15 @@ class DynamicColumnWidget {
LaunchedEffect(listData.value) {
viewModel.getWidgetList(widget, listData.value, widgetListState)
}
LazyColumn(modifier = Modifier.heightIn(columnWidgetData?.minColumnHeight?.dp ?: MIN_SIZE.dp, columnWidgetData?.maxColumnHeight?.dp ?: MAX_SIZE.dp)) {
items (widgetListState.value.size) { index ->
val widgetView = widgetListState.value[index]
widgetView.widgetData?.parentComposeView?.let { UiTronRenderer(widgetView.widgetData.data, viewModel).Render(composeViews = it) }
columnWidgetData?.dividerProperty?.let {
if (index != widgetListState.value.size - 1) {
DividerRenderer().Render(property = it, null, viewModel, null)
if(listData.value?.isNotEmpty() == true){
LazyColumn(modifier = Modifier.heightIn(columnWidgetData?.minColumnHeight?.dp ?: MIN_SIZE.dp, columnWidgetData?.maxColumnHeight?.dp ?: MAX_SIZE.dp)) {
items (widgetListState.value.size) { index ->
val widgetView = widgetListState.value[index]
widgetView.widgetData?.parentComposeView?.let { UiTronRenderer(widgetView.widgetData.data, viewModel).Render(composeViews = it) }
columnWidgetData?.dividerProperty?.let {
if (index != widgetListState.value.size - 1) {
DividerRenderer().Render(property = it, null, viewModel, null)
}
}
}
}

View File

@@ -42,51 +42,48 @@ class DynamicGridWidget {
}
val modifier = Modifier.setPadding(gridWidgetData?.padding).heightIn(gridWidgetData?.minGridSize?.dp ?: MIN_SIZE.dp, gridWidgetData?.maxGridSize?.dp ?: MAX_SIZE.dp)
if (gridWidgetData?.orientation == LazyGridProperty.ORIENTATION_HORIZONTAL) {
LazyHorizontalGrid(
modifier = modifier,
rows = if (gridWidgetData.gridCell?.gridType == LazyGridProperty.GRID_TYPE_ADAPTIVE) {
GridCells.Adaptive(
(if ((gridWidgetData.gridCell.minSize ?: MIN_GRID_CELL_SIZE) > 0) {
gridWidgetData.gridCell.minSize ?: MIN_GRID_CELL_SIZE
} else {
MIN_GRID_CELL_SIZE
}).dp
)
} else {
GridCells.Fixed(gridWidgetData.gridCell?.count ?: MIN_GRID_CELL_COUNT)
},
userScrollEnabled = true,
content = {
getContent(viewModel = viewModel, widgetListState = widgetListState)
},
horizontalArrangement = Arrangement.setHorizontalArrangement(arrangementData = gridWidgetData.horizontalArrangementData),
verticalArrangement = Arrangement.setVerticalArrangement(arrangementData = gridWidgetData.verticalArrangementData)
)
} else {
LazyVerticalGrid(
modifier = modifier,
columns = if (gridWidgetData?.gridCell?.gridType == LazyGridProperty.GRID_TYPE_ADAPTIVE) {
GridCells.Adaptive(
(if ((gridWidgetData.gridCell.minSize ?: MIN_GRID_CELL_SIZE) > 0) {
gridWidgetData.gridCell.minSize ?: MIN_GRID_CELL_SIZE
} else {
MIN_GRID_CELL_SIZE
}).dp
)
} else {
GridCells.Fixed(gridWidgetData?.gridCell?.count ?: MIN_GRID_CELL_COUNT)
},
userScrollEnabled = true,
content = {
getContent(viewModel = viewModel, widgetListState = widgetListState)
},
horizontalArrangement = Arrangement.setHorizontalArrangement(arrangementData = gridWidgetData?.horizontalArrangementData),
verticalArrangement = Arrangement.setVerticalArrangement(arrangementData = gridWidgetData?.verticalArrangementData)
)
if(listData.value?.isNotEmpty() == true){
if (gridWidgetData?.orientation == LazyGridProperty.ORIENTATION_HORIZONTAL) {
LazyHorizontalGrid(
modifier = modifier,
rows = getGridCells(gridWidgetData),
userScrollEnabled = true,
content = {
getContent(viewModel = viewModel, widgetListState = widgetListState)
},
horizontalArrangement = Arrangement.setHorizontalArrangement(arrangementData = gridWidgetData.horizontalArrangementData),
verticalArrangement = Arrangement.setVerticalArrangement(arrangementData = gridWidgetData.verticalArrangementData)
)
} else {
LazyVerticalGrid(
modifier = modifier,
columns = getGridCells(gridWidgetData),
userScrollEnabled = true,
content = {
getContent(viewModel = viewModel, widgetListState = widgetListState)
},
horizontalArrangement = Arrangement.setHorizontalArrangement(arrangementData = gridWidgetData?.horizontalArrangementData),
verticalArrangement = Arrangement.setVerticalArrangement(arrangementData = gridWidgetData?.verticalArrangementData)
)
}
}
}
@Composable
private fun getGridCells(
gridWidgetData: DynamicGridWidgetData?
) = if (gridWidgetData?.gridCell?.gridType == LazyGridProperty.GRID_TYPE_ADAPTIVE) {
GridCells.Adaptive(
(if ((gridWidgetData.gridCell.minSize ?: MIN_GRID_CELL_SIZE) > 0) {
gridWidgetData.gridCell.minSize ?: MIN_GRID_CELL_SIZE
} else {
MIN_GRID_CELL_SIZE
}).dp
)
} else {
GridCells.Fixed(gridWidgetData?.gridCell?.count ?: MIN_GRID_CELL_COUNT)
}
fun LazyGridScope.getContent(viewModel: LambdaVM, widgetListState: State<List<WidgetModelDefinition<UiTronResponse>>>) {
items(widgetListState.value.size) { index ->
val widgetView = widgetListState.value[index]

View File

@@ -27,14 +27,15 @@ class DynamicRowWidget {
LaunchedEffect(listData.value) {
viewModel.getWidgetList(widget, listData.value, widgetListState)
}
LazyRow {
items(widgetListState.value.size) { index ->
val widgetView = widgetListState.value[index]
widgetView.widgetData?.parentComposeView?.let { UiTronRenderer(widgetView.widgetData.data, viewModel).Render(composeViews = it) }
rowWidgetData?.dividerProperty?.let {
if (index != widgetListState.value.size - 1) {
DividerRenderer().Render(property = it, null, viewModel, null)
if(listData.value?.isNotEmpty() == true){
LazyRow {
items(widgetListState.value.size) { index ->
val widgetView = widgetListState.value[index]
widgetView.widgetData?.parentComposeView?.let { UiTronRenderer(widgetView.widgetData.data, viewModel).Render(composeViews = it) }
rowWidgetData?.dividerProperty?.let {
if (index != widgetListState.value.size - 1) {
DividerRenderer().Render(property = it, null, viewModel, null)
}
}
}
}

View File

@@ -9,9 +9,9 @@ package com.navi.ap.common.repository
import com.navi.ap.common.models.TelcoResendOtpRequest
import com.navi.ap.common.models.lambdamodels.ApplyLoanResponse
import com.navi.ap.common.models.lambdamodels.FinarkeinConsentRequest
import com.navi.ap.common.models.lambdamodels.FinarkeinConsentResponse
import com.navi.ap.common.models.lambdamodels.FinarkeinSessionResponse
import com.navi.ap.common.models.lambdamodels.BankDataResponse
import com.navi.ap.common.models.lambdamodels.ConsentRequest
import com.navi.ap.common.models.lambdamodels.ConsentResponse
import com.navi.ap.common.models.lambdamodels.OfferDetails
import com.navi.ap.common.models.lambdamodels.RPDPaymentMethodDetails
import com.navi.ap.common.models.lambdamodels.RPDTokenData
@@ -93,31 +93,27 @@ open class LambdaRepository(val retrofitService: RetrofitService = retrofitServi
return apiResponseCallback(response = response, apiTag = ApiType.LambdaApiAction.name)
}
suspend fun fetchFinarkeinConsentData(
finarkeinConsentRequest: FinarkeinConsentRequest
): ApRepoResult<FinarkeinConsentResponse> {
val response =
retrofitService()
.fetchFinarkeinConsentData(finarkeinConsentRequest = finarkeinConsentRequest)
suspend fun fetchConsentData(serviceProvider: String, consentRequest: ConsentRequest): ApRepoResult<ConsentResponse> {
val response = retrofitService().fetchConsentData(serviceProvider = serviceProvider, consentRequest = consentRequest)
logApEvent(
Pair(
RESULT,
"${response.body()?.errors} | ${response.body()?.genericErrorBottomSheetFields}"
),
Pair(METHOD_NAME, ::fetchFinarkeinConsentData.name),
Pair(METHOD_NAME, ::fetchConsentData.name),
Pair(STATUS_CODE, response.code().toString())
)
return apiResponseCallback(response)
}
suspend fun fetchFinarkeinAuthToken(): ApRepoResult<FinarkeinSessionResponse> {
val response = retrofitService().fetchFinarkeinAuthToken()
suspend fun fetchAllBanks(query: String, flowType: String): ApRepoResult<BankDataResponse> {
val response = retrofitService().fetchAllBanks(query, flowType)
logApEvent(
Pair(
RESULT,
"${response.body()?.errors} | ${response.body()?.genericErrorBottomSheetFields}"
),
Pair(METHOD_NAME, ::fetchFinarkeinAuthToken.name),
Pair(METHOD_NAME, ::fetchAllBanks.name),
Pair(STATUS_CODE, response.code().toString())
)
return apiResponseCallback(response)

View File

@@ -8,9 +8,7 @@
package com.navi.ap.common.sdk.helper
import androidx.activity.compose.ManagedActivityResultLauncher
import com.navi.ap.common.models.lambdamodels.FinarkeinConsentResponse
import com.navi.ap.common.viewmodel.LambdaVM
import com.navi.common.uitron.model.action.ThirdPartySdkAction
import com.navi.ap.common.models.lambdamodels.ConsentResponse
import io.finarkein.anubhav.configuration.AnubhavConfiguration
import io.finarkein.anubhav.configuration.Auth
import io.finarkein.anubhav.constants.AnubhavConstants
@@ -18,33 +16,31 @@ import io.finarkein.anubhav.result.AnubhavResult
class FinarkeinHelper(
private val resultLauncher: ManagedActivityResultLauncher<AnubhavConfiguration, AnubhavResult>?,
val lambdaVM: LambdaVM,
finarkeinSDKAction: ThirdPartySdkAction
) {
fun openFinarkein(finarkeinConsentResponse: FinarkeinConsentResponse) {
fun openFinarkein(consentResponse: ConsentResponse) {
val extraParams: Map<String, String> =
mapOf(
AnubhavConstants.SHOW_EXIT_TRANSITION to AnubhavConstants.TRUE,
AnubhavConstants.REDIRECTION_TIME_IN_MILLISECONDS to
REDIRECTION_WAITING_TIME_IN_MILLISECONDS.toString()
)
val inputs = getWebViewConfiguration(extraParams, finarkeinConsentResponse)
val inputs = getWebViewConfiguration(extraParams, consentResponse)
resultLauncher?.launch(inputs)
}
private fun getWebViewConfiguration(
extraParams: Map<String, String>,
finarkeinConsentResponse: FinarkeinConsentResponse
consentResponse: ConsentResponse
): AnubhavConfiguration {
return AnubhavConfiguration.withWebViewUrl(
finarkeinConsentResponse.webViewBaseUrl.orEmpty()
consentResponse.consent?.webViewBaseUrl.orEmpty()
)
.requestId(finarkeinConsentResponse.consentHandle.orEmpty())
.requestId(consentResponse.consent?.consentHandle.orEmpty())
.auth(
object : Auth {
override suspend fun token(): String {
return lambdaVM.fetchFinarkeinAuthToken().orEmpty()
return consentResponse.fipToken.orEmpty()
}
}
)

View File

@@ -9,13 +9,22 @@ package com.navi.ap.common.viewmodel
import android.os.Bundle
import androidx.lifecycle.viewModelScope
import com.navi.ap.common.models.*
import com.navi.ap.common.models.ApBottomSheetDefinitionState
import com.navi.ap.common.models.ApCtaResponse
import com.navi.ap.common.models.ApGetNextCtaResponse
import com.navi.ap.common.models.ApScreenDefinitionState
import com.navi.ap.common.models.ApScreenDefinitionStructure
import com.navi.ap.common.models.ApplicationIdState
import com.navi.ap.common.models.GetCtaState
import com.navi.ap.common.models.WidgetModelDefinition
import com.navi.ap.common.repository.ApplicationPlatformRepository
import com.navi.ap.network.model.ApplicationHashRequestBody
import com.navi.ap.network.model.ApplicationRequestBody
import com.navi.ap.network.model.FillApplicationRequestBody
import com.navi.ap.network.model.getBottomSheetStructure
import com.navi.ap.network.model.getHideBottomSheetAction
import com.navi.ap.utils.PeriodicTaskScheduler
import com.navi.ap.utils.bundleToMap
import com.navi.ap.utils.constants.APP_ACTION
import com.navi.ap.utils.constants.APP_PLATFORM_APPLICATION_ID
import com.navi.ap.utils.constants.APP_PLATFORM_APPLICATION_TYPE
@@ -30,8 +39,6 @@ import com.navi.ap.utils.constants.ApNavigationActions
import com.navi.ap.utils.constants.CURRENT_SCREEN_ID
import com.navi.ap.utils.constants.DEFAULT_SOURCE_SCREEN
import com.navi.ap.utils.constants.NEXT_SCREEN_CTA
import com.navi.ap.utils.PeriodicTaskScheduler
import com.navi.ap.utils.bundleToMap
import com.navi.ap.utils.constants.REASON
import com.navi.ap.utils.constants.SUBMIT_EVENT_HASH_EVENT
import com.navi.ap.utils.toMap
@@ -45,7 +52,7 @@ import com.navi.common.constants.API_SUCCESS_CODE
import com.navi.common.network.ApiConstants
import com.navi.common.network.models.GenericErrorBottomSheetFields
import com.navi.common.uitron.model.action.AnalyticsActionV2
import com.navi.common.uitron.model.action.AnalyticsActionV2.*
import com.navi.common.uitron.model.action.AnalyticsActionV2.PredefinedEventProperty
import com.navi.common.utils.Constants.LOGOUT
import com.navi.common.viewmodel.BaseVM
import com.navi.uitron.model.UiTronResponse
@@ -128,6 +135,9 @@ constructor(private val repository: ApplicationPlatformRepository) : BaseVM() {
bottomSheetMetaData = bottomSheetDefinitionStructure.screenData?.metaData ?: mapOf()
viewModelScope.launch {
bottomSheetDefinitionStructure.let {
it.screenData?.screenStructure?.content?.widgets?.forEach {
handleActions(it.widgetRenderActions?.preRenderAction)
}
_bottomSheetStructure.emit(ApBottomSheetDefinitionState.Success(it))
}
}

View File

@@ -9,6 +9,7 @@ package com.navi.ap.common.viewmodel
import androidx.compose.runtime.MutableState
import androidx.lifecycle.viewModelScope
import com.navi.ap.common.handler.getIncomeVerificationAvailableSDK
import com.navi.ap.common.models.ApScreenDefinitionState
import com.navi.ap.common.models.ApScreenDefinitionStructure
import com.navi.ap.common.models.ApplyLoanState
@@ -17,11 +18,14 @@ import com.navi.ap.common.models.LambdaState
import com.navi.ap.common.models.TelcoResendOtpRequest
import com.navi.ap.common.models.WidgetModelDefinition
import com.navi.ap.common.models.lambdamodels.BankDataResponse
import com.navi.ap.common.models.lambdamodels.FinarkeinConsentRequest
import com.navi.ap.common.models.lambdamodels.ConsentRequest
import com.navi.ap.common.models.lambdamodels.OfferDetails
import com.navi.ap.common.repository.LambdaRepository
import com.navi.ap.utils.appendIndexToDataKeys
import com.navi.ap.utils.appendIndexToWidgetData
import com.navi.ap.utils.constants.ALL_BANKS_LIST
import com.navi.ap.utils.constants.BANKS_FLOW_TYPE
import com.navi.ap.utils.constants.BANK_SEARCH_QUERY
import com.navi.ap.utils.constants.FETCH_OFFER_DETAILS_SELECTED_LOAN_AMOUNT
import com.navi.ap.utils.constants.FETCH_OFFER_DETAILS_SELECTED_TENURE
import com.navi.ap.utils.constants.FINARKEIN_CONSENT_DATA
@@ -29,6 +33,7 @@ import com.navi.ap.utils.constants.LAST_SELECTED_TENURE_PILL_INDEX
import com.navi.ap.utils.constants.LOAN_APPLICATION_V2
import com.navi.ap.utils.constants.LOAN_SLIDER
import com.navi.ap.utils.constants.OFFER_REFERENCE_ID
import com.navi.ap.utils.constants.PREFERRED_BANKS_LIST
import com.navi.ap.utils.constants.RPD_PAYMENT_METHOD_ID
import com.navi.ap.utils.constants.RPD_PAYMENT_METHOD_TOKEN
import com.navi.ap.utils.constants.RPD_PAYMENT_METHOD_UPI_LINK_KEY
@@ -39,13 +44,14 @@ import com.navi.ap.utils.constants.SELECTED_TENURE_PILL_INDEX
import com.navi.ap.utils.constants.TRUE
import com.navi.ap.utils.getGsonBuilders
import com.navi.ap.utils.injector.PathInjector
import com.navi.ap.utils.jsonToType
import com.navi.ap.utils.toJson
import com.navi.base.model.CtaData
import com.navi.base.utils.BaseUtils
import com.navi.base.utils.isNotNull
import com.navi.common.model.ModuleNameV2
import com.navi.common.uitron.model.action.LambdaApiAction
import com.navi.common.utils.deviceId
import com.navi.common.utils.toJsonObject
import com.navi.naviwidgets.utils.FORWARD_SLASH
import com.navi.uitron.model.UiTronResponse
import getInputId
@@ -92,7 +98,7 @@ open class LambdaVM constructor(val repository: LambdaRepository) :
}
}
fun fetchOfferDetails(resolvedValues: MutableMap<String, Any>) {
fun fetchOfferDetails(resolvedValues: MutableMap<String, Any?>) {
viewModelScope.launch(Dispatchers.IO) {
if (_clonedScreenDefinitionState.value == null) {
_clonedScreenDefinitionState.value = getScreenStructurePreRenderState()
@@ -163,7 +169,7 @@ open class LambdaVM constructor(val repository: LambdaRepository) :
}
// TODO : Need to remove it when bankDetails will be powered through AP
fun applyLoan(resolvedValues: MutableMap<String, Any>) {
fun applyLoan(resolvedValues: MutableMap<String, Any?>) {
viewModelScope.launch(Dispatchers.Default) {
_lambdaState.value = LambdaState.Loading
@@ -212,17 +218,13 @@ open class LambdaVM constructor(val repository: LambdaRepository) :
}
}
fun fetchFinarkeinConsentData(lambdaApiAction: LambdaApiAction) {
fun fetchConsentData(lambdaApiAction: LambdaApiAction) {
viewModelScope.launch(Dispatchers.IO) {
_lambdaState.value = LambdaState.Loading
val bank = handle.get(SELECTED_BANK.getInputId()) as? BankDataResponse.Bank
val finarkeinConsentRequest =
FinarkeinConsentRequest(
phoneNumber = BaseUtils.getPhoneNumber(),
fipId = bank?.fipId,
initiatedBy = ModuleNameV2.PL.name
)
val lambdaResponse = repository.fetchFinarkeinConsentData(finarkeinConsentRequest)
val bank = handle.get<Any>(SELECTED_BANK.getInputId()).toJsonObject()?.jsonToType<BankDataResponse.Bank>()
val availableServiceProvider = bank?.thirdPartyServiceProviders?.getIncomeVerificationAvailableSDK()
val consentRequest = ConsentRequest(bankName = bank?.name, serviceProvider = availableServiceProvider)
val lambdaResponse = repository.fetchConsentData(availableServiceProvider.orEmpty(), consentRequest)
_lambdaState.value =
when {
lambdaResponse.data.isNotNull() -> {
@@ -245,26 +247,32 @@ open class LambdaVM constructor(val repository: LambdaRepository) :
}
}
suspend fun fetchFinarkeinAuthToken(): String? {
_lambdaState.value = LambdaState.Loading
val lambdaResponse = repository.fetchFinarkeinAuthToken()
_lambdaState.value =
when {
lambdaResponse.data.isNotNull() -> {
LambdaState.Success(LambdaResponseType())
fun fetchAllBanks(resolvedValues: MutableMap<String, Any?>) {
viewModelScope.launch(Dispatchers.IO) {
val query = resolvedValues[BANK_SEARCH_QUERY]?.toString().orEmpty()
val flowType = resolvedValues[BANKS_FLOW_TYPE]?.toString().orEmpty()
val preferredBanksKey = resolvedValues[PREFERRED_BANKS_LIST]?.toString().orEmpty()
val allBanksKey = resolvedValues[ALL_BANKS_LIST]?.toString().orEmpty()
val lambdaResponse = repository.fetchAllBanks(query, flowType)
_lambdaState.value =
when {
lambdaResponse.data.isNotNull() -> {
handle[preferredBanksKey] = lambdaResponse.data?.preferredBanks
handle[allBanksKey] = lambdaResponse.data?.allSupportedBanks
LambdaState.Success(LambdaResponseType())
}
lambdaResponse.errorBottomSheetStructure.isNotNull() -> {
LambdaState.Error(
lambdaResponse.statusCode,
lambdaResponse.errorBottomSheetStructure,
lambdaResponse.genericErrorBottomSheetFields
)
}
else -> {
LambdaState.Nothing
}
}
lambdaResponse.errorBottomSheetStructure.isNotNull() -> {
LambdaState.Error(
lambdaResponse.statusCode,
lambdaResponse.errorBottomSheetStructure,
lambdaResponse.genericErrorBottomSheetFields
)
}
else -> {
LambdaState.Nothing
}
}
return lambdaResponse.data?.sessionId
}
}
fun fetchRpdToken(viewModel: LambdaVM, lambdaApiAction: LambdaApiAction) {

View File

@@ -13,9 +13,9 @@ import com.navi.ap.common.models.ApGetNextCtaResponse
import com.navi.ap.common.models.ApScreenDefinitionStructure
import com.navi.ap.common.models.TelcoResendOtpRequest
import com.navi.ap.common.models.lambdamodels.ApplyLoanResponse
import com.navi.ap.common.models.lambdamodels.FinarkeinConsentRequest
import com.navi.ap.common.models.lambdamodels.FinarkeinConsentResponse
import com.navi.ap.common.models.lambdamodels.FinarkeinSessionResponse
import com.navi.ap.common.models.lambdamodels.BankDataResponse
import com.navi.ap.common.models.lambdamodels.ConsentRequest
import com.navi.ap.common.models.lambdamodels.ConsentResponse
import com.navi.ap.common.models.lambdamodels.OfferDetails
import com.navi.ap.common.models.lambdamodels.RPDPaymentMethodDetails
import com.navi.ap.common.models.lambdamodels.RPDTokenData
@@ -111,16 +111,19 @@ interface RetrofitService {
@Body offerDetails: OfferDetails? = null,
): Response<GenericResponse<ApplyLoanResponse>>
@POST("/financial-profile/account-aggregator/finarkein/consent")
suspend fun fetchFinarkeinConsentData(
@POST("financial-profile/sdk/{serviceProvider}/initiate")
suspend fun fetchConsentData(
@Header("X-Target") target: String = ModuleName.LE.name,
@Body finarkeinConsentRequest: FinarkeinConsentRequest
): Response<GenericResponse<FinarkeinConsentResponse>>
@Path("serviceProvider") serviceProvider: String,
@Body consentRequest: ConsentRequest
): Response<GenericResponse<ConsentResponse>>
@GET("/financial-profile/account-aggregator/finarkein/session")
suspend fun fetchFinarkeinAuthToken(
@Header("X-Target") target: String = ModuleName.LE.name,
): Response<GenericResponse<FinarkeinSessionResponse>>
@GET("/financial-profile/banks/v2")
suspend fun fetchAllBanks(
@Query("startsWith") query: String,
@Query("flowType") flowType: String,
@Header("X-Target") header: String = ModuleName.LE.name
): Response<GenericResponse<BankDataResponse>>
@GET("/loan-origination-manager/reverse-penny-drop/token")
suspend fun fetchRpdToken(

View File

@@ -7,6 +7,7 @@
package com.navi.ap.utils
import com.google.gson.reflect.TypeToken
import com.navi.analytics.utils.NaviTrackEvent
import com.navi.ap.network.retrofit.ApRetrofitProvider
import com.navi.ap.network.retrofit.RetrofitService
@@ -21,6 +22,7 @@ import com.navi.common.utils.log
import okhttp3.RequestBody
import okio.Buffer
import java.io.IOException
import org.json.JSONObject
fun CtaData?.toMap() = this?.parameters?.associate { it.key.orEmpty() to it.value.orEmpty() }
@@ -61,4 +63,12 @@ fun RequestBody.bodyToString(): String? {
e.log()
EMPTY
}
}
inline fun <reified U> JSONObject.jsonToType(): U? {
return try {
getGsonBuilders().fromJson(toString(), object : TypeToken<U>() {}.type)
} catch (e: Exception){
null
}
}

View File

@@ -13,15 +13,16 @@ import android.view.inputmethod.InputMethodManager
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.platform.SoftwareKeyboardController
import androidx.core.content.getSystemService
import androidx.lifecycle.SavedStateHandle
import coil.imageLoader
import coil.request.CachePolicy
import coil.request.ImageRequest
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.jayway.jsonpath.JsonPath
import com.navi.ap.common.deserializer.ApUiTronActionDeserializer
import com.navi.ap.common.deserializer.CustomUiTronDataDeserializer
import com.navi.ap.common.models.ApScreenDefinitionStructure
import com.navi.ap.common.viewmodel.LambdaVM
import com.navi.ap.network.di.APNetworkModule.providesDeserializer
import com.navi.ap.network.di.APNetworkModule.providesSerializer
import com.navi.ap.screens.genericscreen.vm.ApGenericScreenVM
@@ -32,6 +33,7 @@ import com.navi.common.uitron.model.action.FillApiData
import com.navi.common.uitron.model.action.SourceType
import com.navi.common.utils.registerUiTronDeSerializers
import com.navi.uitron.model.UiTronResponse
import com.navi.uitron.model.data.UiTronAction
import com.navi.uitron.model.data.UiTronData
import getInputId
import isNull
@@ -52,6 +54,7 @@ fun getGsonBuilders(): Gson = providesDeserializer()
fun GsonBuilder.registerApUiTronDeSerializers(): GsonBuilder {
return registerUiTronDeSerializers().apply {
registerTypeAdapter(UiTronData::class.java, CustomUiTronDataDeserializer())
registerTypeAdapter(UiTronAction::class.java, ApUiTronActionDeserializer())
}
}
@@ -129,27 +132,32 @@ suspend fun cacheImages(uiTronResponse: UiTronResponse?) = withContext(Dispatche
/** Use this to extract value of field and result will be map<sourceProperty , value> */
fun getResolvedFieldValue(
fields: List<FillApiData>?,
viewModel: LambdaVM,
): MutableMap<String, Any> {
val map = mutableMapOf<String, Any>()
handle: SavedStateHandle
): MutableMap<String, Any?> {
val map = mutableMapOf<String, Any?>()
fields
?.filter { it.name.isNotNull() }
?.forEach { item ->
val name = "${item.name.orEmpty()}${item.keySuffix.orEmpty()}"
when (item.source) {
SourceType.WIDGET_OUTPUT.name -> {
fields.forEach { item ->
val layoutId: String? = viewModel.handle[item.name.orEmpty()]
val output: String? = viewModel.handle[layoutId.getInputId()]
val layoutId: String? = handle[name]
val output: String? = handle[layoutId.getInputId()]
map[item.sourceProperty.orEmpty()] = output.orEmpty()
}
}
SourceType.HANDLE.name -> {
val output: String? = viewModel.handle[item.name.orEmpty()]
val output: String? = handle[name]
map[item.sourceProperty.orEmpty()] = output.orEmpty()
}
SourceType.PRE_DEFINED.name -> {
map[item.name.orEmpty()] = item.value.orEmpty()
map[name] = item.value.orEmpty()
}
SourceType.HANDLE_OBJECT.name -> {
val obj: Any? = handle[name]
val output = readJsonPath(obj.toJson(), item.jsonPath.orEmpty())
map[item.sourceProperty.orEmpty()] = output
}
else -> Unit
}

View File

@@ -47,7 +47,8 @@ enum class LambdaType {
TELCO_RESEND_OTP,
LOAN_OFFER_DETAILS,
APPLY_LOAN,
FETCH_FINARKEIN_CONSENT_DATA,
FETCH_RPD_TOKEN,
FETCH_RPD_PAYMENT_METHOD_DETAILS
FETCH_RPD_PAYMENT_METHOD_DETAILS,
FETCH_CONSENT_DATA,
FETCH_BANKS
}

View File

@@ -22,3 +22,8 @@ const val FINARKEIN_CONSENT_DATA = "finarkein_consent_data"
const val RPD_PAYMENT_METHOD_TOKEN = "rpdPaymentMethodToken"
const val RPD_PAYMENT_METHOD_ID = "rpdPaymentMethodId"
const val RPD_PAYMENT_METHOD_UPI_LINK_KEY = "upiLink"
const val ALL_BANKS_LIST = "all_banks_list"
const val PREFERRED_BANKS_LIST = "preferred_banks_list"
const val BANK_SEARCH_QUERY = "bank_search_query"
const val BANKS_FLOW_TYPE = "banks_flow_type"

View File

@@ -45,7 +45,7 @@ class PathInjector<U, V>() : Injector<U, V>() {
if (placeHolderKeyName.startsWith('$')) {
val jsonPath = placeHolderKeyName.trim()
val placeHolderValue = readJsonPath(jsonResponse.toString(), jsonPath)
currentObject.put(key, placeHolderValue)
currentObject.put(key, JSONArray(placeHolderValue.toString()))
}
}
JSON_TYPE.JSONObject -> {

View File

@@ -25,7 +25,7 @@ import com.navi.uitron.model.data.UiTronAction
import com.navi.uitron.model.data.UiTronActionType
import java.lang.reflect.Type
class UiTronActionDeserializer : BaseUiTronActionDeserializer() {
open class UiTronActionDeserializer : BaseUiTronActionDeserializer() {
override fun deserialize(
json: JsonElement?,

View File

@@ -28,11 +28,9 @@ open class ThirdPartySdkAction(
}
}
data class FinarkeinSDKAction(
@SerializedName("consentDataInputKey") val consentDataInputKey: String? = null
) : ThirdPartySdkAction()
class FinarkeinSDKAction() : ThirdPartySdkAction()
class FinoramicSDKAction(): ThirdPartySdkAction()
class FinoramicSDKAction(): ThirdPartySdkAction()
enum class SDKType {
FINORAMIC,

View File

@@ -30,7 +30,8 @@ data class FillApiData(
@SerializedName("source") val source: String? = null,
@SerializedName("sourceProperty") val sourceProperty: String? = null,
@SerializedName("value") val value: String? = null,
@SerializedName("jsonPath") val jsonPath: String? = null
@SerializedName("jsonPath") val jsonPath: String? = null,
@SerializedName("keySuffix") val keySuffix: String? = null
) : Parcelable
open class LambdaApiAction(

View File

@@ -12,6 +12,7 @@ import com.google.gson.annotations.SerializedName
import com.navi.uitron.model.data.ActionDetails
import com.navi.uitron.model.data.UiTronAction
import kotlinx.parcelize.Parcelize
import kotlinx.parcelize.RawValue
@Parcelize
data class UpdateStateHandleActionV2(
@@ -27,7 +28,7 @@ data class UpdateStateHandleActionV2(
@Parcelize
data class HandleData(
val key: String? = null,
var value: String? = null,
var value: @RawValue Any? = null,
val keySuffix: String? = null
) : Parcelable
}

View File

@@ -15,5 +15,6 @@ enum class ApActionType {
ValidateMultipleWidgetsAction,
ExecuteActionsCorrespondingToKey,
AnalyticsActionV2,
LaunchIntentAction
LaunchIntentAction,
UpdateDataViaHandleAction
}