Skip to content
Merged
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
7 changes: 4 additions & 3 deletions common/src/main/kotlin/co/nilin/opex/common/OpexError.kt
Original file line number Diff line number Diff line change
Expand Up @@ -157,11 +157,12 @@ enum class OpexError(val code: Int, val message: String?, val status: HttpStatus
MobileAlreadySet(13014, "Mobile already set", HttpStatus.BAD_REQUEST),
EmailAlreadySet(13015, "Email already set", HttpStatus.BAD_REQUEST),
ProfileAlreadyCompleted(13016, "Profile already completed", HttpStatus.BAD_REQUEST),
ComparativeVerificationFailed(13017, "Comparative Verification Failed", HttpStatus.BAD_REQUEST),
ProfileApprovalRequestNotfound(13018, "Profile approval request not found", HttpStatus.NOT_FOUND),
InvalidProfileApprovalRequestStatus(13018, "Invalid profile approval request status", HttpStatus.BAD_REQUEST),
FirstNameIsNotSimilarEnough(13017, "The first name is not similar enough.", HttpStatus.BAD_REQUEST),
LastNameIsNotSimilarEnough(13018, "The last name is not similar enough.", HttpStatus.BAD_REQUEST),
ShahkarInquiryUnavailable(13019,"Shahkar inquiry unavailable" , HttpStatus.SERVICE_UNAVAILABLE),
ComparativeInquiryUnavailable(13020,"Comparative inquiry unavailable" , HttpStatus.SERVICE_UNAVAILABLE),
ProfileApprovalRequestNotfound(13021, "Profile approval request not found", HttpStatus.NOT_FOUND),
InvalidProfileApprovalRequestStatus(13022, "Invalid profile approval request status", HttpStatus.BAD_REQUEST),
;

override fun code() = this.code
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ class ProfileManagement(
)
)
if (verifyResponse.result) {
authProxy.updateEmail(userId, email) //TODO rollback in error ?
authProxy.updateEmail(userId, email)
profilePersister.updateEmail(userId, email)
} else throw OpexError.InvalidOTP.exception()
}
Expand All @@ -173,7 +173,7 @@ class ProfileManagement(
throw OpexError.ProfileAlreadyCompleted.exception()
}

val isIranian = request.nationality == NationalityType.IRANIAN.name
val isIranian = request.nationality == NationalityType.IRANIAN
var useMobileIdentity = false
var usePersonalIdentity = false

Expand Down Expand Up @@ -223,15 +223,16 @@ class ProfileManagement(
identifier: String,
firstName: String,
lastName: String,
birthDate: LocalDateTime
birthDate: Long
) {
if (!inquiryProxy.getComparativeInquiryResult(
identifier,
birthDate,
firstName,
lastName
)
) throw OpexError.ComparativeVerificationFailed.exception()
val comparativeInquiryResult = inquiryProxy.getComparativeInquiryResult(
identifier,
birthDate,
firstName,
lastName
)
if (comparativeInquiryResult.firstNameSimilarityPercentage < 95) throw OpexError.FirstNameIsNotSimilarEnough.exception()
if (comparativeInquiryResult.lastNameSimilarityPercentage < 95) throw OpexError.LastNameIsNotSimilarEnough.exception()
}

private suspend fun saveProfileApprovalRequest(profileId: Long) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package co.nilin.opex.profile.ports.inquiry.data
package co.nilin.opex.profile.core.data.profile

data class ComparativeResponse(
val firstNameSimilarityPercentage: Int,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
package co.nilin.opex.profile.core.data.profile

import java.time.LocalDateTime

data class CompleteProfileRequest(
var firstName: String,
var lastName: String,
var address: String? = null,
var telephone: String? = null,
var postalCode: String? = null,
var nationality: String,
var nationality: NationalityType,
var identifier: String,
var gender: Gender,
var birthDate: LocalDateTime,
var birthDate: Long,
)
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ open class CompleteProfileResponse(
var mobile: String? = null,
var telephone: String? = null,
var postalCode: String? = null,
var nationality: String? = null,
var nationality: NationalityType? = null,
var identifier: String? = null,
var gender: Gender? = null,
var birthDate: LocalDateTime? = null,
var status: UserStatus? = null,
var kycLevel: KycLevel? = null,
var mobileIdentityMatch : Boolean? = false,
var personalIdentityMatch : Boolean? = false
var mobileIdentityMatch: Boolean? = false,
var personalIdentityMatch: Boolean? = false
)
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ data class Profile(
var mobile: String? = null,
var telephone: String? = null,
var postalCode: String? = null,
var nationality: String? = null,
var nationality: NationalityType? = null,
var identifier: String? = null,
var gender: Gender? = null,
var birthDate: LocalDateTime? = null,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
package co.nilin.opex.profile.core.spi

import co.nilin.opex.profile.core.data.profile.ComparativeResponse
import java.time.LocalDateTime

interface InquiryProxy {
suspend fun getShahkarInquiryResult(identifier: String, mobile: String): Boolean

suspend fun getComparativeInquiryResult(
identifier: String,
birthDate: LocalDateTime,
birthDate: Long,
firstName: String,
lastName: String
): Boolean
): ComparativeResponse
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,23 @@ class AuthProxyImpl(private val webClient: WebClient) : AuthProxy {
private lateinit var baseUrl: String

override suspend fun updateEmail(userId: String, email: String) {
webClient.put().uri("$baseUrl/v1/user/email")
webClient.put().uri("$baseUrl/v1/user/update/email")
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(mapOf("userId" to userId, "email" to email))
.retrieve()
.awaitBodilessEntity()
}

override suspend fun updateMobile(userId: String, mobile: String) {
webClient.put().uri("$baseUrl/v1/user/mobile")
webClient.put().uri("$baseUrl/v1/user/update/mobile")
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(mapOf("userId" to userId, "mobile" to mobile))
.retrieve()
.awaitBodilessEntity()
}

override suspend fun updateName(userId: String, firstName: String, lastName: String) {
webClient.put().uri("$baseUrl/v1/user/name")
webClient.put().uri("$baseUrl/v1/user/update/name")
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(mapOf("userId" to userId, "firstName" to firstName, "lastName" to lastName))
.retrieve()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package co.nilin.opex.profile.ports.inquiry.imp

import co.nilin.opex.common.OpexError
import co.nilin.opex.profile.core.data.profile.ComparativeResponse
import co.nilin.opex.profile.core.spi.InquiryProxy
import co.nilin.opex.profile.ports.inquiry.data.ComparativeResponse
import co.nilin.opex.profile.ports.inquiry.data.ShahkarResponse
import co.nilin.opex.profile.ports.inquiry.utils.TokenProvider
import co.nilin.opex.profile.ports.inquiry.utils.toPersianDateFormatted
Expand Down Expand Up @@ -41,13 +41,13 @@ class InquiryProxyImp(

override suspend fun getComparativeInquiryResult(
identifier: String,
birthDate: LocalDateTime,
birthDate: Long,
firstName: String,
lastName: String
): Boolean {
): ComparativeResponse {
val birthDateFormatted = birthDate.toPersianDateFormatted()

val response = webClient.get()
return webClient.get()
.uri("$baseUrl/v1/services/identity/similarity") {
it.queryParam("nationalCode", identifier)
it.queryParam("birthDate", birthDateFormatted)
Expand All @@ -60,7 +60,5 @@ class InquiryProxyImp(
.retrieve()
.onStatus({ t -> t.isError }, { throw OpexError.ComparativeInquiryUnavailable.exception() })
.awaitBody<ComparativeResponse>()

return (response.firstNameSimilarityPercentage >= 95 && response.lastNameSimilarityPercentage >= 95)
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
package co.nilin.opex.profile.ports.inquiry.utils

import com.ibm.icu.util.Calendar
import com.ibm.icu.util.TimeZone
import com.ibm.icu.util.ULocale
import java.time.LocalDateTime
import java.time.ZoneId
import java.util.*

fun LocalDateTime.toPersianDateFormatted(): String {
val instant = this.atZone(ZoneId.systemDefault()).toInstant()
val date = Date.from(instant)
val calendar = Calendar.getInstance(ULocale("fa_IR@calendar=persian"))
calendar.time = date
fun Long.toPersianDateFormatted(): String {
val date = Date(this)
val calendar = Calendar.getInstance(ULocale("fa_IR@calendar=persian")).apply {
timeZone = TimeZone.getTimeZone("Asia/Tehran")
time = date
}

val year = calendar.get(Calendar.YEAR)
val month = calendar.get(Calendar.MONTH) + 1
val day = calendar.get(Calendar.DAY_OF_MONTH)

return String.format("%04d%02d%02d", year, month, day)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import co.nilin.opex.profile.ports.postgres.convertor.convertProfileModelToCompl
import co.nilin.opex.profile.ports.postgres.dao.ProfileHistoryRepository
import co.nilin.opex.profile.ports.postgres.dao.ProfileRepository
import co.nilin.opex.profile.ports.postgres.model.entity.ProfileModel
import co.nilin.opex.profile.ports.postgres.utils.toProfileModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.toList
Expand Down Expand Up @@ -84,23 +85,16 @@ class ProfileManagementImp(
val existingProfile = profileRepository.findByUserId(id)?.awaitFirstOrNull()
?: throw OpexError.ProfileNotfound.exception()

var newProfileModel = data.convert(ProfileModel::class.java)
newProfileModel.email = existingProfile.email
newProfileModel.mobile = existingProfile.mobile
newProfileModel.id = existingProfile.id
newProfileModel.userId = existingProfile.userId
newProfileModel.status = existingProfile.status
newProfileModel.createDate = existingProfile.createDate
newProfileModel.creator = existingProfile.creator
newProfileModel.kycLevel = existingProfile.kycLevel
newProfileModel.lastUpdateDate = LocalDateTime.now()
newProfileModel.mobileIdentityMatch = mobileIdentityMatch
newProfileModel.personalIdentityMatch = personalIdentityMatch
val newProfileModel = data.toProfileModel(
existing = existingProfile,
mobileMatch = mobileIdentityMatch,
personalMatch = personalIdentityMatch
)

return profileRepository.save(newProfileModel)
.map { saved ->
val response = convertProfileModelToCompleteProfileResponse(saved)
if (saved.nationality == NationalityType.IRANIAN.name) {
if (saved.nationality == NationalityType.IRANIAN) {
response.kycLevel = KycLevel.Level2
}
response
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package co.nilin.opex.profile.ports.postgres.model.base

import co.nilin.opex.profile.core.data.profile.Gender
import co.nilin.opex.profile.core.data.kyc.KycLevel
import co.nilin.opex.profile.core.data.profile.NationalityType
import co.nilin.opex.profile.core.data.profile.UserStatus
import java.time.LocalDateTime

Expand All @@ -14,7 +15,7 @@ open class Profile {
var mobile: String? = null
var telephone: String? = null
var postalCode: String? = null
var nationality: String? = null
var nationality: NationalityType? = null
var identifier: String? = null
var gender: Gender? = null
lateinit var birthDate: LocalDateTime
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,41 @@
package co.nilin.opex.profile.ports.postgres.utils

import co.nilin.opex.profile.core.data.profile.CompleteProfileRequest
import co.nilin.opex.profile.ports.postgres.model.entity.ProfileModel
import com.google.gson.Gson
import reactor.core.publisher.Mono
import java.time.LocalDateTime

fun <T> Any.convert(classOfT: Class<T>): T = Gson().fromJson(Gson().toJson(this), classOfT)

fun <T> Mono<Any>.convert(classOfT: Class<T>): Mono<T> = Mono.just(Gson().fromJson(Gson().toJson(this), classOfT))

fun CompleteProfileRequest.toProfileModel(
existing: ProfileModel,
mobileMatch: Boolean,
personalMatch: Boolean
): ProfileModel {
return ProfileModel(id = existing.id).apply {
userId = existing.userId
email = existing.email
mobile = existing.mobile
status = existing.status
createDate = existing.createDate
creator = existing.creator
kycLevel = existing.kycLevel
lastUpdateDate = LocalDateTime.now()

firstName = this@toProfileModel.firstName
lastName = this@toProfileModel.lastName
address = this@toProfileModel.address
telephone = this@toProfileModel.telephone
postalCode = this@toProfileModel.postalCode
nationality = this@toProfileModel.nationality
identifier = this@toProfileModel.identifier
gender = this@toProfileModel.gender
birthDate = this@toProfileModel.birthDate.asLocalDateTime()

mobileIdentityMatch = mobileMatch
personalIdentityMatch = personalMatch
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package co.nilin.opex.profile.ports.postgres.utils


import java.time.LocalDateTime
import java.time.ZoneId
import java.util.*

fun LocalDateTime.asDate(): Date {
return Date.from(atZone(ZoneId.systemDefault()).toInstant())
}

fun Date.asLocalDateTime(): LocalDateTime {
return LocalDateTime.ofInstant(toInstant(), ZoneId.systemDefault())
}

fun Long.asLocalDateTime(): LocalDateTime {
return LocalDateTime.ofInstant(Date(this).toInstant(), ZoneId.systemDefault())
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ CREATE TABLE IF NOT EXISTS profile
CREATE TABLE IF NOT EXISTS profile_history
(
id SERIAL PRIMARY KEY,
email VARCHAR(100) NOT NULL,
email VARCHAR(100),
last_name VARCHAR(256),
user_id VARCHAR(100) NOT NULL,
create_date TIMESTAMP,
Expand Down Expand Up @@ -261,4 +261,15 @@ CREATE TABLE IF NOT EXISTS profile_approval_request
updater VARCHAR(100),
description VARCHAR(255),
UNIQUE (profile_id, status)
);
);

DO
$$
BEGIN
IF EXISTS (SELECT 1
FROM information_schema.columns
WHERE table_name = 'profile_history' AND column_name = 'email') THEN ALTER TABLE profile_history
ALTER COLUMN email DROP NOT NULL;
END IF;
END
$$;
Loading