Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add cbr source + impl getConvertedAmount method + refactoring #12

Merged
merged 9 commits into from
Jun 5, 2024
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
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ on:

jobs:
build:
uses: valitydev/base-workflow/.github/workflows/maven-service-build.yml@v2
uses: valitydev/java-workflow/.github/workflows/maven-service-build.yml@v2
2 changes: 1 addition & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:

jobs:
build-and-deploy:
uses: valitydev/base-workflow/.github/workflows/maven-service-deploy.yml@v2
uses: valitydev/java-workflow/.github/workflows/maven-service-deploy.yml@v2
secrets:
github-token: ${{ secrets.GITHUB_TOKEN }}
mm-webhook-url: ${{ secrets.MATTERMOST_WEBHOOK_URL }}
22 changes: 21 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
<dependency>
<groupId>dev.vality</groupId>
<artifactId>exrates-proto</artifactId>
<version>1.7-ce9563e</version>
<version>1.8-9794d14</version>
</dependency>
<dependency>
<groupId>dev.vality</groupId>
Expand Down Expand Up @@ -124,6 +124,26 @@
<groupId>software.amazon.msk</groupId>
<artifactId>aws-msk-iam-auth</artifactId>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
<version>2.3.0.1</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.3.6</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-math3</artifactId>
<version>3.6.1</version>
</dependency>

<!--Test/Dev-->
<dependency>
Expand Down
40 changes: 40 additions & 0 deletions src/main/kotlin/dev/vality/rateboss/client/cbr/CbrApiClient.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package dev.vality.rateboss.client.cbr

import dev.vality.rateboss.client.cbr.model.CbrExchangeRateData
import dev.vality.rateboss.config.properties.RatesProperties
import org.springframework.http.HttpEntity
import org.springframework.http.HttpMethod
import org.springframework.stereotype.Component
import org.springframework.web.client.RestTemplate
import org.springframework.web.client.exchange
import org.springframework.web.util.UriComponentsBuilder
import java.time.Instant
import java.time.LocalDate
import java.time.format.DateTimeFormatter

@Component
class CbrApiClient(
private val restTemplate: RestTemplate,
private val ratesProperties: RatesProperties
) {

fun getExchangeRates(time: Instant): CbrExchangeRateData {
val baseUrl = ratesProperties.source.cbr.rootUrl
val timezone = ratesProperties.source.cbr.timeZone
val date = time.atZone(timezone).toLocalDate()
val url = buildUrl(baseUrl, date)
return restTemplate.exchange<CbrExchangeRateData>(url, HttpMethod.GET, HttpEntity(null, null)).body!!
}

private fun buildUrl(endpoint: String, date: LocalDate): String {
return UriComponentsBuilder
.fromUriString(endpoint)
.queryParam("date_req", date.format(DATE_TIME_FORMATTER))
.build()
.toUriString()
}

companion object {
val DATE_TIME_FORMATTER: DateTimeFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package dev.vality.rateboss.client.cbr.adapter

import java.math.BigDecimal
import javax.xml.bind.annotation.adapters.XmlAdapter

class CbrBigDecimalXmlAdapter : XmlAdapter<String, BigDecimal>() {

override fun unmarshal(stringValue: String?): BigDecimal? {
return stringValue?.let {
BigDecimal(it.replace(',', '.'))
}
}

override fun marshal(bigDecimalValue: BigDecimal?): String? {
return bigDecimalValue?.toString()?.replace('.', ',')
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package dev.vality.rateboss.client.cbr.adapter

import java.time.LocalDate
import java.time.format.DateTimeFormatter
import javax.xml.bind.annotation.adapters.XmlAdapter

class CbrLocalDateXmlAdapter : XmlAdapter<String, LocalDate>() {

override fun unmarshal(stringValue: String?): LocalDate? {
return stringValue?.let {
LocalDate.from(DATE_FORMATTER.parse(it))
}
}

override fun marshal(dateValue: LocalDate?): String? {
return dateValue?.let {
DATE_FORMATTER.format(it)
}
}

companion object {
val DATE_FORMATTER: DateTimeFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package dev.vality.rateboss.client.cbr.model

import dev.vality.rateboss.client.cbr.adapter.CbrBigDecimalXmlAdapter
import java.math.BigDecimal
import javax.xml.bind.annotation.*
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter

@XmlRootElement(name = "Valute")
@XmlAccessorType(XmlAccessType.FIELD)
class CbrCurrencyData {

@XmlAttribute(name = "ID")
var id: String? = null

@XmlElement(name = "NumCode")
var numCode: Int? = null

@XmlElement(name = "CharCode")
var charCode: String? = null

@XmlElement(name = "Nominal")
var nominal: Int? = null

@XmlElement(name = "Name")
var name: String? = null

@XmlJavaTypeAdapter(CbrBigDecimalXmlAdapter::class)
@XmlElement(name = "Value")
var value: BigDecimal? = null
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package dev.vality.rateboss.client.cbr.model

import dev.vality.rateboss.client.cbr.adapter.CbrLocalDateXmlAdapter
import java.time.LocalDate
import javax.xml.bind.annotation.*
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter

@XmlRootElement(name = "ValCurs")
@XmlAccessorType(XmlAccessType.FIELD)
class CbrExchangeRateData {

@XmlAttribute
var name: String? = null

@XmlAttribute(name = "Date")
@XmlJavaTypeAdapter(CbrLocalDateXmlAdapter::class)
var date: LocalDate? = null

@XmlElement(name = "Valute")
var currencies: List<CbrCurrencyData>? = null
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package dev.vality.rateboss.client
package dev.vality.rateboss.client.fixer

import dev.vality.rateboss.client.model.FixerLatestResponse
import dev.vality.rateboss.client.fixer.model.FixerLatestResponse
import dev.vality.rateboss.config.properties.RatesProperties
import org.springframework.http.HttpEntity
import org.springframework.http.HttpHeaders
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package dev.vality.rateboss.client.model
package dev.vality.rateboss.client.fixer.model

import java.math.BigDecimal
import java.time.LocalDate
Expand Down
47 changes: 36 additions & 11 deletions src/main/kotlin/dev/vality/rateboss/config/JobConfig.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package dev.vality.rateboss.config

import dev.vality.rateboss.config.properties.RatesProperties
import dev.vality.rateboss.job.ExchangeGrabberMasterJob
import dev.vality.rateboss.job.CbrExchangeGrabberMasterJob
import dev.vality.rateboss.job.FixerExchangeGrabberMasterJob
import org.quartz.*
import org.quartz.impl.JobDetailImpl
import org.springframework.beans.factory.annotation.Autowired
Expand All @@ -19,25 +20,49 @@ class JobConfig {

@PostConstruct
fun init() {
schedulerFactoryBean.addJob(exchangeRateGrabberMasterJob(), true, true)
if (!schedulerFactoryBean.checkExists(TriggerKey(ratesProperties.jobTriggerName))) {
schedulerFactoryBean.scheduleJob(exchangeRateGrabberMasterTrigger())
val fixerJobTriggerName = ratesProperties.fixerJob.jobTriggerName
if (fixerJobTriggerName.isNotEmpty()) {
schedulerFactoryBean.addJob(fixerExchangeRateGrabberMasterJob(), true, true)
if (!schedulerFactoryBean.checkExists(TriggerKey(fixerJobTriggerName))) {
schedulerFactoryBean.scheduleJob(fixerExchangeRateGrabberMasterTrigger())
}
}
val cbrJobTriggerName = ratesProperties.cbrJob.jobTriggerName
if (cbrJobTriggerName.isNotEmpty()) {
schedulerFactoryBean.addJob(cbrExchangeRateGrabberMasterJob(), true, true)
if (!schedulerFactoryBean.checkExists(TriggerKey(ratesProperties.cbrJob.jobTriggerName))) {
schedulerFactoryBean.scheduleJob(cbrExchangeRateGrabberMasterTrigger())
}
}
}

fun exchangeRateGrabberMasterJob(): JobDetailImpl {
fun fixerExchangeRateGrabberMasterJob(): JobDetailImpl {
val jobDetail = JobDetailImpl()
jobDetail.key = JobKey(ratesProperties.jobKey)
jobDetail.jobClass = ExchangeGrabberMasterJob::class.java
jobDetail.key = JobKey(ratesProperties.fixerJob.jobKey)
jobDetail.jobClass = FixerExchangeGrabberMasterJob::class.java
return jobDetail
}

fun fixerExchangeRateGrabberMasterTrigger(): CronTrigger {
return TriggerBuilder.newTrigger()
.forJob(fixerExchangeRateGrabberMasterJob())
.withIdentity(ratesProperties.fixerJob.jobTriggerName)
.withSchedule(CronScheduleBuilder.cronSchedule(ratesProperties.fixerJob.jobCron))
.build()
}

fun cbrExchangeRateGrabberMasterJob(): JobDetailImpl {
val jobDetail = JobDetailImpl()
jobDetail.key = JobKey(ratesProperties.cbrJob.jobKey)
jobDetail.jobClass = CbrExchangeGrabberMasterJob::class.java
return jobDetail
}

fun exchangeRateGrabberMasterTrigger(): CronTrigger {
fun cbrExchangeRateGrabberMasterTrigger(): CronTrigger {
return TriggerBuilder.newTrigger()
.forJob(exchangeRateGrabberMasterJob())
.withIdentity(ratesProperties.jobTriggerName)
.withSchedule(CronScheduleBuilder.cronSchedule(ratesProperties.jobCron))
.forJob(cbrExchangeRateGrabberMasterJob())
.withIdentity(ratesProperties.cbrJob.jobTriggerName)
.withSchedule(CronScheduleBuilder.cronSchedule(ratesProperties.cbrJob.jobCron))
.build()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,22 @@ package dev.vality.rateboss.config.properties
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.context.properties.ConstructorBinding
import org.springframework.validation.annotation.Validated
import java.time.ZoneId

@Validated
@ConstructorBinding
@ConfigurationProperties(prefix = "rates")
data class RatesProperties(
val fixerJob: JobDescription,
val cbrJob: JobDescription,
val source: RatesSourceProperties
)

data class JobDescription(
val jobCron: String,
val jobKey: String,
val jobTriggerName: String,
val currencies: List<CurrencyProperties>,
val source: RatesSourceProperties
val currencies: List<CurrencyProperties>
)

data class CurrencyProperties(
Expand All @@ -21,10 +27,16 @@ data class CurrencyProperties(
)

data class RatesSourceProperties(
val fixer: FixerProperties
val fixer: FixerProperties,
val cbr: CbrProperties
)

data class FixerProperties(
val rootUrl: String,
val apiKey: String
)

data class CbrProperties(
val rootUrl: String,
val timeZone: ZoneId
)
18 changes: 11 additions & 7 deletions src/main/kotlin/dev/vality/rateboss/converter/ExRateConverter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,29 @@ import org.springframework.stereotype.Component
import java.math.BigDecimal
import java.time.LocalDateTime
import java.time.ZoneOffset
import java.util.*

@Component
class ExRateConverter {

fun convert(
baseCurrencySymbolCode: String,
baseCurrencyExponent: Short,
exchangeRateMap: Map.Entry<String, BigDecimal>,
ggmaleva marked this conversation as resolved.
Show resolved Hide resolved
exchangeRateTimestamp: Long
exchangeRateMapEntry: Map.Entry<String, BigDecimal>,
exchangeRateTimestamp: Long,
sourceId: String
): ExRate {
val sourceCurrency = Currency.getInstance(exchangeRateMapEntry.key)
return ExRate().apply {
sourceCurrencySymbolicCode = baseCurrencySymbolCode
sourceCurrencyExponent = baseCurrencyExponent
destinationCurrencySymbolicCode = exchangeRateMap.key
destinationCurrencyExponent = exchangeRateMap.value.scale().toShort()
val rational = exchangeRateMap.value.toRational()
destinationCurrencySymbolicCode = baseCurrencySymbolCode
destinationCurrencyExponent = baseCurrencyExponent
sourceCurrencySymbolicCode = sourceCurrency.currencyCode
sourceCurrencyExponent = sourceCurrency.defaultFractionDigits.toShort()
val rational = exchangeRateMapEntry.value.toRational()
rationalP = rational.numerator
rationalQ = rational.denominator
rateTimestamp = LocalDateTime.ofEpochSecond(exchangeRateTimestamp, 0, ZoneOffset.UTC)
source = sourceId
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import dev.vality.exrates.base.Rational
import dev.vality.exrates.service.CurrencyData
import dev.vality.exrates.service.GetCurrencyExchangeRateResult
import dev.vality.rateboss.converter.Constants.Companion.DATE_TIME_FORMAT
import dev.vality.rateboss.source.model.ExchangeRateData
import dev.vality.rateboss.service.model.ExchangeRateData
import org.springframework.stereotype.Component
import java.time.format.DateTimeFormatter

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package dev.vality.rateboss.converter

import dev.vality.exrates.service.ConversionRequest
import dev.vality.rateboss.converter.Constants.Companion.DATE_TIME_FORMAT
import dev.vality.rateboss.service.model.TimestampExchangeRateRequest
import org.springframework.stereotype.Component
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter

@Component
class TimestampExchangeRateRequestConverter {

fun convert(
conversionRequest: ConversionRequest,
sourceId: String
): TimestampExchangeRateRequest {
return TimestampExchangeRateRequest(
sourceCurrency = conversionRequest.source,
destinationCurrency = conversionRequest.destination,
source = sourceId,
rateTimestamp = LocalDateTime.parse(
conversionRequest.datetime,
DateTimeFormatter.ofPattern(DATE_TIME_FORMAT)
)
)
}
}
Loading
Loading