diff --git a/.editorconfig b/.editorconfig index ad78e9e..0b4240b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,2 +1,4 @@ [*.{kt,kts}] -ktlint_disabled_rules=no-wildcard-imports +ktlint_standard_no-wildcard-imports = disabled +ktlint_standard_trailing-comma-on-declaration-site = disabled +ktlint_standard_trailing-comma-on-call-site = disabled diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cfbd1fe..6ebd1d8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,4 +7,4 @@ on: jobs: build: - uses: valitydev/java-workflow/.github/workflows/maven-service-build.yml@v2 \ No newline at end of file + uses: valitydev/java-workflow/.github/workflows/maven-service-build.yml@v3 \ No newline at end of file diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 1166548..85af119 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -7,7 +7,9 @@ on: jobs: build-and-deploy: - uses: valitydev/java-workflow/.github/workflows/maven-service-deploy.yml@v2 + uses: valitydev/java-workflow/.github/workflows/maven-service-deploy.yml@v3 + with: + ignore-coverage: true secrets: github-token: ${{ secrets.GITHUB_TOKEN }} mm-webhook-url: ${{ secrets.MATTERMOST_WEBHOOK_URL }} \ No newline at end of file diff --git a/pom.xml b/pom.xml index cbcf2bf..aa36045 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ dev.vality service-parent-pom - 2.0.3 + 3.1.0 rate-boss 0.0.1-SNAPSHOT @@ -14,8 +14,8 @@ 8022 8023 - 17 - 1.7.10 + 21 + 2.1.21 1.17.3 ${server.port} ${management.port} 5432 @@ -94,7 +94,7 @@ dev.vality.geck common - 0.0.1 + 1.0.2 @@ -102,6 +102,10 @@ org.flywaydb flyway-core + + org.flywaydb + flyway-database-postgresql + org.postgresql postgresql @@ -150,6 +154,10 @@ 1.4 pom + + jakarta.servlet + jakarta.servlet-api + @@ -238,7 +246,7 @@ 3.7.1 - dev.vality.maven.plugins + dev.crashteam.maven.plugins pg-embedded-plugin 1.0.1 @@ -258,12 +266,19 @@ PG_server_stop - compile + generate-resources stop + + + commons-io + commons-io + 2.18.0 + + org.flywaydb @@ -332,7 +347,6 @@ org.jetbrains.kotlin kotlin-maven-plugin - ${kotlin.version} -Xjsr305=strict @@ -341,7 +355,6 @@ spring jpa - 1.8 @@ -386,7 +399,7 @@ com.github.gantsign.maven ktlint-maven-plugin - 1.15.1 + 3.5.0 validate diff --git a/src/main/kotlin/dev/vality/rateboss/client/cbr/CbrApiClient.kt b/src/main/kotlin/dev/vality/rateboss/client/cbr/CbrApiClient.kt index bea67ec..05e3235 100644 --- a/src/main/kotlin/dev/vality/rateboss/client/cbr/CbrApiClient.kt +++ b/src/main/kotlin/dev/vality/rateboss/client/cbr/CbrApiClient.kt @@ -15,9 +15,8 @@ import java.time.format.DateTimeFormatter @Component class CbrApiClient( private val restTemplate: RestTemplate, - private val ratesProperties: RatesProperties + private val ratesProperties: RatesProperties, ) { - fun getExchangeRates(time: Instant): CbrExchangeRateData { val baseUrl = ratesProperties.source.cbr.rootUrl val timezone = ratesProperties.source.cbr.timeZone @@ -26,13 +25,15 @@ class CbrApiClient( return restTemplate.exchange(url, HttpMethod.GET, HttpEntity(null, null)).body!! } - private fun buildUrl(endpoint: String, date: LocalDate): String { - return UriComponentsBuilder + private fun buildUrl( + endpoint: String, + date: LocalDate, + ): String = + 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") diff --git a/src/main/kotlin/dev/vality/rateboss/client/cbr/adapter/CbrBigDecimalXmlAdapter.kt b/src/main/kotlin/dev/vality/rateboss/client/cbr/adapter/CbrBigDecimalXmlAdapter.kt index 11437e8..fbb8e79 100644 --- a/src/main/kotlin/dev/vality/rateboss/client/cbr/adapter/CbrBigDecimalXmlAdapter.kt +++ b/src/main/kotlin/dev/vality/rateboss/client/cbr/adapter/CbrBigDecimalXmlAdapter.kt @@ -4,14 +4,10 @@ import java.math.BigDecimal import javax.xml.bind.annotation.adapters.XmlAdapter class CbrBigDecimalXmlAdapter : XmlAdapter() { - - override fun unmarshal(stringValue: String?): BigDecimal? { - return stringValue?.let { + override fun unmarshal(stringValue: String?): BigDecimal? = + stringValue?.let { BigDecimal(it.replace(',', '.')) } - } - override fun marshal(bigDecimalValue: BigDecimal?): String? { - return bigDecimalValue?.toString()?.replace('.', ',') - } + override fun marshal(bigDecimalValue: BigDecimal?): String? = bigDecimalValue?.toString()?.replace('.', ',') } diff --git a/src/main/kotlin/dev/vality/rateboss/client/cbr/adapter/CbrLocalDateXmlAdapter.kt b/src/main/kotlin/dev/vality/rateboss/client/cbr/adapter/CbrLocalDateXmlAdapter.kt index d13a71a..a270f9a 100644 --- a/src/main/kotlin/dev/vality/rateboss/client/cbr/adapter/CbrLocalDateXmlAdapter.kt +++ b/src/main/kotlin/dev/vality/rateboss/client/cbr/adapter/CbrLocalDateXmlAdapter.kt @@ -5,18 +5,15 @@ import java.time.format.DateTimeFormatter import javax.xml.bind.annotation.adapters.XmlAdapter class CbrLocalDateXmlAdapter : XmlAdapter() { - - override fun unmarshal(stringValue: String?): LocalDate? { - return stringValue?.let { + override fun unmarshal(stringValue: String?): LocalDate? = + stringValue?.let { LocalDate.from(DATE_FORMATTER.parse(it)) } - } - override fun marshal(dateValue: LocalDate?): String? { - return dateValue?.let { + override fun marshal(dateValue: LocalDate?): String? = + dateValue?.let { DATE_FORMATTER.format(it) } - } companion object { val DATE_FORMATTER: DateTimeFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy") diff --git a/src/main/kotlin/dev/vality/rateboss/client/cbr/model/CbrCurrencyData.kt b/src/main/kotlin/dev/vality/rateboss/client/cbr/model/CbrCurrencyData.kt index 4b2e725..bd9c8a0 100644 --- a/src/main/kotlin/dev/vality/rateboss/client/cbr/model/CbrCurrencyData.kt +++ b/src/main/kotlin/dev/vality/rateboss/client/cbr/model/CbrCurrencyData.kt @@ -8,7 +8,6 @@ import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter @XmlRootElement(name = "Valute") @XmlAccessorType(XmlAccessType.FIELD) class CbrCurrencyData { - @XmlAttribute(name = "ID") var id: String? = null diff --git a/src/main/kotlin/dev/vality/rateboss/client/cbr/model/CbrExchangeRateData.kt b/src/main/kotlin/dev/vality/rateboss/client/cbr/model/CbrExchangeRateData.kt index 863cb80..d6fb8bf 100644 --- a/src/main/kotlin/dev/vality/rateboss/client/cbr/model/CbrExchangeRateData.kt +++ b/src/main/kotlin/dev/vality/rateboss/client/cbr/model/CbrExchangeRateData.kt @@ -8,7 +8,6 @@ import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter @XmlRootElement(name = "ValCurs") @XmlAccessorType(XmlAccessType.FIELD) class CbrExchangeRateData { - @XmlAttribute var name: String? = null diff --git a/src/main/kotlin/dev/vality/rateboss/client/fixer/FixerApiClient.kt b/src/main/kotlin/dev/vality/rateboss/client/fixer/FixerApiClient.kt index c6c56e6..64bfa02 100644 --- a/src/main/kotlin/dev/vality/rateboss/client/fixer/FixerApiClient.kt +++ b/src/main/kotlin/dev/vality/rateboss/client/fixer/FixerApiClient.kt @@ -13,26 +13,31 @@ import org.springframework.web.util.UriComponentsBuilder @Component class FixerApiClient( private val ratesProperties: RatesProperties, - private val restTemplate: RestTemplate + private val restTemplate: RestTemplate, ) { - - fun getLatest(base: String, symbols: String? = null): FixerLatestResponse { - val urlTemplate = UriComponentsBuilder.fromHttpUrl(ratesProperties.source.fixer.rootUrl).apply { - path("/latest") - queryParam("base", base) - if (symbols != null) { - queryParam("symbols", symbols) + fun getLatest( + base: String, + symbols: String? = null, + ): FixerLatestResponse { + val urlTemplate = + UriComponentsBuilder.fromHttpUrl(ratesProperties.source.fixer.rootUrl).apply { + path("/latest") + queryParam("base", base) + if (symbols != null) { + queryParam("symbols", symbols) + } + encode() + toUriString() + } + val httpHeaders = + HttpHeaders().apply { + set("apiKey", ratesProperties.source.fixer.apiKey) } - encode() - toUriString() - } - val httpHeaders = HttpHeaders().apply { - set("apiKey", ratesProperties.source.fixer.apiKey) - } - return restTemplate.exchange( - urlTemplate.toUriString(), - HttpMethod.GET, - HttpEntity(null, httpHeaders) - ).body!! + return restTemplate + .exchange( + urlTemplate.toUriString(), + HttpMethod.GET, + HttpEntity(null, httpHeaders), + ).body!! } } diff --git a/src/main/kotlin/dev/vality/rateboss/client/fixer/model/FixerLatestResponse.kt b/src/main/kotlin/dev/vality/rateboss/client/fixer/model/FixerLatestResponse.kt index 7b43dfe..5fd3dd6 100644 --- a/src/main/kotlin/dev/vality/rateboss/client/fixer/model/FixerLatestResponse.kt +++ b/src/main/kotlin/dev/vality/rateboss/client/fixer/model/FixerLatestResponse.kt @@ -8,5 +8,5 @@ data class FixerLatestResponse( val date: LocalDate?, val rates: Map?, val success: Boolean, - val timestamp: Long? + val timestamp: Long?, ) diff --git a/src/main/kotlin/dev/vality/rateboss/config/AppConfig.kt b/src/main/kotlin/dev/vality/rateboss/config/AppConfig.kt index 01d559a..bed1d29 100644 --- a/src/main/kotlin/dev/vality/rateboss/config/AppConfig.kt +++ b/src/main/kotlin/dev/vality/rateboss/config/AppConfig.kt @@ -10,14 +10,13 @@ import org.springframework.web.client.RestTemplate @Configuration class AppConfig { - @Bean fun restTemplate() = RestTemplate() @Bean fun retryTemplate( @Value("\${retryTemplate.backOffPeriod}") backOffPeriod: Long, - @Value("\${retryTemplate.maxAttempts}") maxAttempts: Int + @Value("\${retryTemplate.maxAttempts}") maxAttempts: Int, ): RetryTemplate { val retryTemplate = RetryTemplate() val fixedBackOffPolicy = FixedBackOffPolicy() diff --git a/src/main/kotlin/dev/vality/rateboss/config/JobConfig.kt b/src/main/kotlin/dev/vality/rateboss/config/JobConfig.kt index 79b1846..2924c67 100644 --- a/src/main/kotlin/dev/vality/rateboss/config/JobConfig.kt +++ b/src/main/kotlin/dev/vality/rateboss/config/JobConfig.kt @@ -11,7 +11,6 @@ import javax.annotation.PostConstruct @Configuration class JobConfig { - @Autowired private lateinit var schedulerFactoryBean: Scheduler @@ -43,13 +42,13 @@ class JobConfig { return jobDetail } - fun fixerExchangeRateGrabberMasterTrigger(): CronTrigger { - return TriggerBuilder.newTrigger() + fun fixerExchangeRateGrabberMasterTrigger(): CronTrigger = + TriggerBuilder + .newTrigger() .forJob(fixerExchangeRateGrabberMasterJob()) .withIdentity(ratesProperties.fixerJob.jobTriggerName) .withSchedule(CronScheduleBuilder.cronSchedule(ratesProperties.fixerJob.jobCron)) .build() - } fun cbrExchangeRateGrabberMasterJob(): JobDetailImpl { val jobDetail = JobDetailImpl() @@ -58,11 +57,11 @@ class JobConfig { return jobDetail } - fun cbrExchangeRateGrabberMasterTrigger(): CronTrigger { - return TriggerBuilder.newTrigger() + fun cbrExchangeRateGrabberMasterTrigger(): CronTrigger = + TriggerBuilder + .newTrigger() .forJob(cbrExchangeRateGrabberMasterJob()) .withIdentity(ratesProperties.cbrJob.jobTriggerName) .withSchedule(CronScheduleBuilder.cronSchedule(ratesProperties.cbrJob.jobCron)) .build() - } } diff --git a/src/main/kotlin/dev/vality/rateboss/config/KafkaConfig.kt b/src/main/kotlin/dev/vality/rateboss/config/KafkaConfig.kt index d431093..5c6d5fa 100644 --- a/src/main/kotlin/dev/vality/rateboss/config/KafkaConfig.kt +++ b/src/main/kotlin/dev/vality/rateboss/config/KafkaConfig.kt @@ -15,7 +15,6 @@ import org.springframework.kafka.core.ProducerFactory @Configuration class KafkaConfig { - @Autowired lateinit var kafkaProperties: KafkaProperties @@ -27,7 +26,7 @@ class KafkaConfig { retryBackoffMs: Long, batchSize: Int, acks: String, - deliveryTimeout: Int + deliveryTimeout: Int, ): Map { val props: MutableMap = HashMap(kafkaProperties.buildProducerProperties()) props[ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG] = StringSerializer::class.java @@ -41,22 +40,20 @@ class KafkaConfig { } @Bean - fun producerFactory(): ProducerFactory { - return DefaultKafkaProducerFactory( + fun producerFactory(): ProducerFactory = + DefaultKafkaProducerFactory( producerConfigs( kafkaCustomProperties.producer.maxRetries, kafkaCustomProperties.producer.retryBackoffMs, kafkaCustomProperties.producer.batchSize, ACKS_CONFIG, - kafkaCustomProperties.producer.deliveryTimeoutMs - ) + kafkaCustomProperties.producer.deliveryTimeoutMs, + ), ) - } @Bean - fun kafkaTemplate(producerFactory: ProducerFactory): KafkaTemplate { - return KafkaTemplate(producerFactory) - } + fun kafkaTemplate(producerFactory: ProducerFactory): KafkaTemplate = + KafkaTemplate(producerFactory) private companion object { const val ACKS_CONFIG = "1" diff --git a/src/main/kotlin/dev/vality/rateboss/config/QuartzConfig.kt b/src/main/kotlin/dev/vality/rateboss/config/QuartzConfig.kt index 59313c6..19596c5 100644 --- a/src/main/kotlin/dev/vality/rateboss/config/QuartzConfig.kt +++ b/src/main/kotlin/dev/vality/rateboss/config/QuartzConfig.kt @@ -10,7 +10,6 @@ import javax.sql.DataSource @Configuration class QuartzConfig { - @Autowired private lateinit var quartzProperties: QuartzProperties diff --git a/src/main/kotlin/dev/vality/rateboss/config/properties/KafkaCustomProperties.kt b/src/main/kotlin/dev/vality/rateboss/config/properties/KafkaCustomProperties.kt index 2707a42..28b6e99 100644 --- a/src/main/kotlin/dev/vality/rateboss/config/properties/KafkaCustomProperties.kt +++ b/src/main/kotlin/dev/vality/rateboss/config/properties/KafkaCustomProperties.kt @@ -1,26 +1,24 @@ package dev.vality.rateboss.config.properties import org.springframework.boot.context.properties.ConfigurationProperties -import org.springframework.boot.context.properties.ConstructorBinding -@ConstructorBinding @ConfigurationProperties(prefix = "kafka") data class KafkaCustomProperties( val producer: KafkaProducerProperties, - val topic: KafkaTopicProperties + val topic: KafkaTopicProperties, ) data class KafkaProducerProperties( val maxRetries: Int, val retryBackoffMs: Long, val batchSize: Int, - val deliveryTimeoutMs: Int + val deliveryTimeoutMs: Int, ) data class KafkaTopicProperties( - val producer: KafkaTopicProducerProperties + val producer: KafkaTopicProducerProperties, ) data class KafkaTopicProducerProperties( - val exchangeTopic: String + val exchangeTopic: String, ) diff --git a/src/main/kotlin/dev/vality/rateboss/config/properties/RatesProperties.kt b/src/main/kotlin/dev/vality/rateboss/config/properties/RatesProperties.kt index dbfa0b6..9264c98 100644 --- a/src/main/kotlin/dev/vality/rateboss/config/properties/RatesProperties.kt +++ b/src/main/kotlin/dev/vality/rateboss/config/properties/RatesProperties.kt @@ -1,42 +1,40 @@ 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 + val source: RatesSourceProperties, ) data class JobDescription( val jobCron: String, val jobKey: String, val jobTriggerName: String, - val currencies: List + val currencies: List, ) data class CurrencyProperties( val symbolCode: String, - val exponent: Int + val exponent: Int, ) data class RatesSourceProperties( val fixer: FixerProperties, - val cbr: CbrProperties + val cbr: CbrProperties, ) data class FixerProperties( val rootUrl: String, - val apiKey: String + val apiKey: String, ) data class CbrProperties( val rootUrl: String, - val timeZone: ZoneId + val timeZone: ZoneId, ) diff --git a/src/main/kotlin/dev/vality/rateboss/converter/ExRateConverter.kt b/src/main/kotlin/dev/vality/rateboss/converter/ExRateConverter.kt index 7d6a56b..b7bb481 100644 --- a/src/main/kotlin/dev/vality/rateboss/converter/ExRateConverter.kt +++ b/src/main/kotlin/dev/vality/rateboss/converter/ExRateConverter.kt @@ -13,19 +13,19 @@ private const val DEFAULT_EXPONENT = 2 @Component class ExRateConverter { - fun convert( baseCurrencySymbolCode: String, baseCurrencyExponent: Short, exchangeRateMapEntry: Map.Entry, exchangeRateTimestamp: Long, - sourceId: String + sourceId: String, ): ExRate { - val exponent = try { - Monetary.getCurrency(exchangeRateMapEntry.key).defaultFractionDigits - } catch (e: Exception) { - DEFAULT_EXPONENT - } + val exponent = + try { + Monetary.getCurrency(exchangeRateMapEntry.key).defaultFractionDigits + } catch (e: Exception) { + DEFAULT_EXPONENT + } return ExRate().apply { destinationCurrencySymbolicCode = baseCurrencySymbolCode destinationCurrencyExponent = baseCurrencyExponent diff --git a/src/main/kotlin/dev/vality/rateboss/converter/GetCurrencyExchangeRateResultConverter.kt b/src/main/kotlin/dev/vality/rateboss/converter/GetCurrencyExchangeRateResultConverter.kt index 37be136..bceb8eb 100644 --- a/src/main/kotlin/dev/vality/rateboss/converter/GetCurrencyExchangeRateResultConverter.kt +++ b/src/main/kotlin/dev/vality/rateboss/converter/GetCurrencyExchangeRateResultConverter.kt @@ -10,11 +10,8 @@ import java.time.format.DateTimeFormatter @Component class GetCurrencyExchangeRateResultConverter { - - fun convert( - exchangeRateData: ExchangeRateData - ): GetCurrencyExchangeRateResult { - return GetCurrencyExchangeRateResult().apply { + fun convert(exchangeRateData: ExchangeRateData): GetCurrencyExchangeRateResult = + GetCurrencyExchangeRateResult().apply { currencyData = CurrencyData() currencyData.sourceCurrency = exchangeRateData.sourceCurrencySymbolicCode currencyData.destinationCurrency = exchangeRateData.destinationCurrencySymbolicCode @@ -23,7 +20,6 @@ class GetCurrencyExchangeRateResultConverter { exchange_rate.q = exchangeRateData.rationalQ timestamp = exchangeRateData.rateTimestamp.format(DateTimeFormatter.ofPattern(DATE_TIME_FORMAT)) } - } } class Constants { diff --git a/src/main/kotlin/dev/vality/rateboss/converter/TimestampExchangeRateRequestConverter.kt b/src/main/kotlin/dev/vality/rateboss/converter/TimestampExchangeRateRequestConverter.kt index 919f66d..30f4013 100644 --- a/src/main/kotlin/dev/vality/rateboss/converter/TimestampExchangeRateRequestConverter.kt +++ b/src/main/kotlin/dev/vality/rateboss/converter/TimestampExchangeRateRequestConverter.kt @@ -9,19 +9,18 @@ import java.time.format.DateTimeFormatter @Component class TimestampExchangeRateRequestConverter { - fun convert( conversionRequest: ConversionRequest, - sourceId: String - ): TimestampExchangeRateRequest { - return TimestampExchangeRateRequest( + sourceId: String, + ): TimestampExchangeRateRequest = + TimestampExchangeRateRequest( sourceCurrency = conversionRequest.source, destinationCurrency = conversionRequest.destination, source = sourceId, - rateTimestamp = LocalDateTime.parse( - conversionRequest.datetime, - DateTimeFormatter.ofPattern(DATE_TIME_FORMAT) - ) + rateTimestamp = + LocalDateTime.parse( + conversionRequest.datetime, + DateTimeFormatter.ofPattern(DATE_TIME_FORMAT), + ), ) - } } diff --git a/src/main/kotlin/dev/vality/rateboss/dao/ExRateDao.kt b/src/main/kotlin/dev/vality/rateboss/dao/ExRateDao.kt index b3dfbcd..583ba5d 100644 --- a/src/main/kotlin/dev/vality/rateboss/dao/ExRateDao.kt +++ b/src/main/kotlin/dev/vality/rateboss/dao/ExRateDao.kt @@ -4,10 +4,12 @@ import dev.vality.rateboss.dao.domain.tables.pojos.ExRate import dev.vality.rateboss.service.model.TimestampExchangeRateRequest interface ExRateDao { - fun saveBatch(entities: List) - fun getRecentBySymbolicCodes(sourceCode: String, destinationCode: String): ExRate? + fun getRecentBySymbolicCodes( + sourceCode: String, + destinationCode: String, + ): ExRate? fun getByCodesAndTimestamp(request: TimestampExchangeRateRequest): ExRate? } diff --git a/src/main/kotlin/dev/vality/rateboss/dao/ExRateDaoImpl.kt b/src/main/kotlin/dev/vality/rateboss/dao/ExRateDaoImpl.kt index da3ae07..7fe90eb 100644 --- a/src/main/kotlin/dev/vality/rateboss/dao/ExRateDaoImpl.kt +++ b/src/main/kotlin/dev/vality/rateboss/dao/ExRateDaoImpl.kt @@ -10,44 +10,51 @@ import org.springframework.stereotype.Repository @Repository class ExRateDaoImpl( - private val dsl: DSLContext + private val dsl: DSLContext, ) : ExRateDao { override fun saveBatch(entities: List) { val t = EX_RATE - dsl.batch( - entities.map { entity -> - dsl.insertInto( - t, - t.SOURCE_CURRENCY_SYMBOLIC_CODE, - t.SOURCE_CURRENCY_EXPONENT, - t.DESTINATION_CURRENCY_SYMBOLIC_CODE, - t.DESTINATION_CURRENCY_EXPONENT, - t.RATIONAL_P, - t.RATIONAL_Q, - t.RATE_TIMESTAMP, - t.SOURCE, - t.REQUEST_DATE - ).values( - entity.sourceCurrencySymbolicCode, - entity.sourceCurrencyExponent, - entity.destinationCurrencySymbolicCode, - entity.destinationCurrencyExponent, - entity.rationalP, - entity.rationalQ, - entity.rateTimestamp, - entity.source, - entity.requestDate - ) - } - ).execute() + dsl + .batch( + entities.map { entity -> + dsl + .insertInto( + t, + t.SOURCE_CURRENCY_SYMBOLIC_CODE, + t.SOURCE_CURRENCY_EXPONENT, + t.DESTINATION_CURRENCY_SYMBOLIC_CODE, + t.DESTINATION_CURRENCY_EXPONENT, + t.RATIONAL_P, + t.RATIONAL_Q, + t.RATE_TIMESTAMP, + t.SOURCE, + t.REQUEST_DATE, + ).values( + entity.sourceCurrencySymbolicCode, + entity.sourceCurrencyExponent, + entity.destinationCurrencySymbolicCode, + entity.destinationCurrencyExponent, + entity.rationalP, + entity.rationalQ, + entity.rateTimestamp, + entity.source, + entity.requestDate, + ) + }, + ).execute() } - override fun getRecentBySymbolicCodes(sourceCode: String, destinationCode: String): ExRate? { + override fun getRecentBySymbolicCodes( + sourceCode: String, + destinationCode: String, + ): ExRate? { val t = EX_RATE - return dsl.selectFrom(t) + return dsl + .selectFrom(t) .where( - t.DESTINATION_CURRENCY_SYMBOLIC_CODE.eq(destinationCode) - .and(t.SOURCE_CURRENCY_SYMBOLIC_CODE.eq(sourceCode)) + t.DESTINATION_CURRENCY_SYMBOLIC_CODE + .eq(destinationCode) + .and(t.SOURCE_CURRENCY_SYMBOLIC_CODE.eq(sourceCode)), ).orderBy(t.RATE_TIMESTAMP.desc()) .limit(1) .fetchOneInto(ExRate::class.java) @@ -56,12 +63,14 @@ class ExRateDaoImpl( override fun getByCodesAndTimestamp(request: TimestampExchangeRateRequest): ExRate? { val t = EX_RATE val targetRateDate = request.rateTimestamp.toLocalDate() - return dsl.selectFrom(t) + return dsl + .selectFrom(t) .where( - t.DESTINATION_CURRENCY_SYMBOLIC_CODE.eq(request.destinationCurrency) + t.DESTINATION_CURRENCY_SYMBOLIC_CODE + .eq(request.destinationCurrency) .and(t.SOURCE_CURRENCY_SYMBOLIC_CODE.eq(request.sourceCurrency)) .and(t.SOURCE.eq(request.source)) - .and(DSL.cast(t.RATE_TIMESTAMP, SQLDataType.LOCALDATE).eq(targetRateDate)) + .and(DSL.cast(t.RATE_TIMESTAMP, SQLDataType.LOCALDATE).eq(targetRateDate)), ).orderBy(t.RATE_TIMESTAMP.desc()) .limit(1) .fetchOneInto(ExRate::class.java) diff --git a/src/main/kotlin/dev/vality/rateboss/extensions/BigDecimalExtension.kt b/src/main/kotlin/dev/vality/rateboss/extensions/BigDecimalExtension.kt index a4f95ec..5846e8d 100644 --- a/src/main/kotlin/dev/vality/rateboss/extensions/BigDecimalExtension.kt +++ b/src/main/kotlin/dev/vality/rateboss/extensions/BigDecimalExtension.kt @@ -5,9 +5,12 @@ import java.math.BigInteger fun BigDecimal.toRational(): Rational { val denominator: BigInteger = if (this.scale() > 0) BigInteger.TEN.pow(this.scale()) else BigInteger.ONE - val numerator: BigInteger = this.remainder(BigDecimal.ONE) - .movePointRight(this.scale()).toBigInteger() - .add(this.toBigInteger().multiply(denominator)) + val numerator: BigInteger = + this + .remainder(BigDecimal.ONE) + .movePointRight(this.scale()) + .toBigInteger() + .add(this.toBigInteger().multiply(denominator)) if (numerator > BigInteger.valueOf(Long.MAX_VALUE)) { throw ArithmeticException("Numerator out of long range: $numerator") @@ -21,5 +24,5 @@ fun BigDecimal.toRational(): Rational { data class Rational( val numerator: Long, - val denominator: Long + val denominator: Long, ) diff --git a/src/main/kotlin/dev/vality/rateboss/extensions/JobExecutionContextExtension.kt b/src/main/kotlin/dev/vality/rateboss/extensions/JobExecutionContextExtension.kt index 5406f8d..d5eb2fc 100644 --- a/src/main/kotlin/dev/vality/rateboss/extensions/JobExecutionContextExtension.kt +++ b/src/main/kotlin/dev/vality/rateboss/extensions/JobExecutionContextExtension.kt @@ -4,7 +4,6 @@ import org.quartz.JobExecutionContext import org.quartz.JobExecutionException import org.springframework.context.ApplicationContext -fun JobExecutionContext.getApplicationContext(): ApplicationContext { - return this.scheduler.context["applicationContext"] as? ApplicationContext +fun JobExecutionContext.getApplicationContext(): ApplicationContext = + this.scheduler.context["applicationContext"] as? ApplicationContext ?: throw JobExecutionException("No application context available in scheduler context") -} diff --git a/src/main/kotlin/dev/vality/rateboss/job/AbstractExchangeGrabberJob.kt b/src/main/kotlin/dev/vality/rateboss/job/AbstractExchangeGrabberJob.kt index 0957014..cf9070d 100644 --- a/src/main/kotlin/dev/vality/rateboss/job/AbstractExchangeGrabberJob.kt +++ b/src/main/kotlin/dev/vality/rateboss/job/AbstractExchangeGrabberJob.kt @@ -11,23 +11,23 @@ import org.springframework.scheduling.quartz.QuartzJobBean private val log = KotlinLogging.logger {} abstract class AbstractExchangeGrabberJob : QuartzJobBean() { - fun saveExchangeRates( applicationContext: ApplicationContext, currencySymbolCode: String, currencyExponent: Int, exchangeRates: ExchangeRates, - sourceId: String + sourceId: String, ) { try { val exchangeDaoService = applicationContext.getBean(ExchangeDaoService::class.java) log.info { "Save exchange rates for currency=$currencySymbolCode, sourceId=$sourceId" } - val exchangeRatesData = ExchangeRatesData( - destinationCurrencySymbolicCode = currencySymbolCode, - destinationCurrencyExponent = currencyExponent.toShort(), - exchangeRates = exchangeRates, - sourceId = sourceId - ) + val exchangeRatesData = + ExchangeRatesData( + destinationCurrencySymbolicCode = currencySymbolCode, + destinationCurrencyExponent = currencyExponent.toShort(), + exchangeRates = exchangeRates, + sourceId = sourceId, + ) exchangeDaoService.saveExchangeRates(exchangeRatesData) } catch (e: Exception) { log.error("Couldn't save exchange rates for currency={} ", currencySymbolCode, e) @@ -38,7 +38,7 @@ abstract class AbstractExchangeGrabberJob : QuartzJobBean() { applicationContext: ApplicationContext, currencySymbolCode: String, currencyExponent: Int, - exchangeRates: ExchangeRates + exchangeRates: ExchangeRates, ) { val exchangeEventService = applicationContext.getBean(ExchangeEventService::class.java) log.info { "Send exchange rates for currency=$currencySymbolCode" } diff --git a/src/main/kotlin/dev/vality/rateboss/job/AbstractExchangeGrabberMasterJob.kt b/src/main/kotlin/dev/vality/rateboss/job/AbstractExchangeGrabberMasterJob.kt index 3a8086f..e598955 100644 --- a/src/main/kotlin/dev/vality/rateboss/job/AbstractExchangeGrabberMasterJob.kt +++ b/src/main/kotlin/dev/vality/rateboss/job/AbstractExchangeGrabberMasterJob.kt @@ -10,27 +10,30 @@ import java.util.* private val log = KotlinLogging.logger {} abstract class AbstractExchangeGrabberMasterJob : QuartzJobBean() { - fun launchJob( currencies: List, schedulerFactoryBean: Scheduler, jobType: Class, - jobName: String + jobName: String, ) { for (currency in currencies) { val jobIdentity = "$jobName-${currency.symbolCode}-currency-job" - val jobDetail = JobBuilder.newJob(jobType) - .withIdentity(jobIdentity) - .build() - val triggerFactoryBean = SimpleTriggerFactoryBean().apply { - setPriority(Int.MAX_VALUE) - setName(jobIdentity) - setStartTime(Date()) - setRepeatInterval(0L) - setRepeatCount(0) - setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW) - afterPropertiesSet() - }.getObject() + val jobDetail = + JobBuilder + .newJob(jobType) + .withIdentity(jobIdentity) + .build() + val triggerFactoryBean = + SimpleTriggerFactoryBean() + .apply { + setPriority(Int.MAX_VALUE) + setName(jobIdentity) + setStartTime(Date()) + setRepeatInterval(0L) + setRepeatCount(0) + setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW) + afterPropertiesSet() + }.getObject() jobDetail.jobDataMap["currencySymbolCode"] = currency.symbolCode jobDetail.jobDataMap["currencyExponent"] = currency.exponent try { diff --git a/src/main/kotlin/dev/vality/rateboss/job/CbrExchangeGrabberJob.kt b/src/main/kotlin/dev/vality/rateboss/job/CbrExchangeGrabberJob.kt index f66dbb9..3ddda09 100644 --- a/src/main/kotlin/dev/vality/rateboss/job/CbrExchangeGrabberJob.kt +++ b/src/main/kotlin/dev/vality/rateboss/job/CbrExchangeGrabberJob.kt @@ -13,7 +13,6 @@ import org.springframework.retry.support.RetryTemplate private val log = KotlinLogging.logger {} class CbrExchangeGrabberJob : AbstractExchangeGrabberJob() { - override fun executeInternal(context: JobExecutionContext) { val applicationContext = context.getApplicationContext() val currencySymbolCode = context.jobDetail.jobDataMap["currencySymbolCode"] as String @@ -28,12 +27,13 @@ class CbrExchangeGrabberJob : AbstractExchangeGrabberJob() { private fun getExchangeRates( applicationContext: ApplicationContext, exchangeRateSource: ExchangeRateSource, - currencySymbolCode: String + currencySymbolCode: String, ): ExchangeRates { val retryTemplate = applicationContext.getBean(RetryTemplate::class.java) - val exchangeRates = retryTemplate.execute { - exchangeRateSource.getExchangeRate(currencySymbolCode) - } + val exchangeRates = + retryTemplate.execute { + exchangeRateSource.getExchangeRate(currencySymbolCode) + } return exchangeRates } } diff --git a/src/main/kotlin/dev/vality/rateboss/job/CbrExchangeGrabberMasterJob.kt b/src/main/kotlin/dev/vality/rateboss/job/CbrExchangeGrabberMasterJob.kt index c99d39a..528b907 100644 --- a/src/main/kotlin/dev/vality/rateboss/job/CbrExchangeGrabberMasterJob.kt +++ b/src/main/kotlin/dev/vality/rateboss/job/CbrExchangeGrabberMasterJob.kt @@ -6,7 +6,6 @@ import org.quartz.JobExecutionContext import org.quartz.Scheduler class CbrExchangeGrabberMasterJob : AbstractExchangeGrabberMasterJob() { - override fun executeInternal(context: JobExecutionContext) { val applicationContext = context.getApplicationContext() val ratesProperties = applicationContext.getBean(RatesProperties::class.java) @@ -15,7 +14,5 @@ class CbrExchangeGrabberMasterJob : AbstractExchangeGrabberMasterJob() { launchJob(currencies, schedulerFactoryBean, CbrExchangeGrabberJob::class.java, getJobName()) } - override fun getJobName(): String { - return "cbrJob" - } + override fun getJobName(): String = "cbrJob" } diff --git a/src/main/kotlin/dev/vality/rateboss/job/FixerExchangeGrabberJob.kt b/src/main/kotlin/dev/vality/rateboss/job/FixerExchangeGrabberJob.kt index 12a7b9c..6a8e218 100644 --- a/src/main/kotlin/dev/vality/rateboss/job/FixerExchangeGrabberJob.kt +++ b/src/main/kotlin/dev/vality/rateboss/job/FixerExchangeGrabberJob.kt @@ -13,7 +13,6 @@ import org.springframework.retry.support.RetryTemplate private val log = KotlinLogging.logger {} class FixerExchangeGrabberJob : AbstractExchangeGrabberJob() { - override fun executeInternal(context: JobExecutionContext) { val applicationContext = context.getApplicationContext() val currencySymbolCode = context.jobDetail.jobDataMap["currencySymbolCode"] as String @@ -29,12 +28,13 @@ class FixerExchangeGrabberJob : AbstractExchangeGrabberJob() { private fun getExchangeRates( applicationContext: ApplicationContext, exchangeRateSource: ExchangeRateSource, - currencySymbolCode: String + currencySymbolCode: String, ): ExchangeRates { val retryTemplate = applicationContext.getBean(RetryTemplate::class.java) - val exchangeRates = retryTemplate.execute { - exchangeRateSource.getExchangeRate(currencySymbolCode) - } + val exchangeRates = + retryTemplate.execute { + exchangeRateSource.getExchangeRate(currencySymbolCode) + } return exchangeRates } } diff --git a/src/main/kotlin/dev/vality/rateboss/job/FixerExchangeGrabberMasterJob.kt b/src/main/kotlin/dev/vality/rateboss/job/FixerExchangeGrabberMasterJob.kt index bb7773a..13e1cba 100644 --- a/src/main/kotlin/dev/vality/rateboss/job/FixerExchangeGrabberMasterJob.kt +++ b/src/main/kotlin/dev/vality/rateboss/job/FixerExchangeGrabberMasterJob.kt @@ -9,7 +9,6 @@ import org.quartz.Scheduler private val log = KotlinLogging.logger {} class FixerExchangeGrabberMasterJob : AbstractExchangeGrabberMasterJob() { - override fun executeInternal(context: JobExecutionContext) { val applicationContext = context.getApplicationContext() val ratesProperties = applicationContext.getBean(RatesProperties::class.java) @@ -18,7 +17,5 @@ class FixerExchangeGrabberMasterJob : AbstractExchangeGrabberMasterJob() { launchJob(currencies, schedulerFactoryBean, FixerExchangeGrabberJob::class.java, getJobName()) } - override fun getJobName(): String { - return "fixerJob" - } + override fun getJobName(): String = "fixerJob" } diff --git a/src/main/kotlin/dev/vality/rateboss/service/ConversionService.kt b/src/main/kotlin/dev/vality/rateboss/service/ConversionService.kt index 992d4a9..5c29157 100644 --- a/src/main/kotlin/dev/vality/rateboss/service/ConversionService.kt +++ b/src/main/kotlin/dev/vality/rateboss/service/ConversionService.kt @@ -5,9 +5,10 @@ import org.springframework.stereotype.Service @Service class ConversionService { - - fun convertAmount(amount: Long, rate: BigFraction): BigFraction { - return BigFraction(amount) + fun convertAmount( + amount: Long, + rate: BigFraction, + ): BigFraction = + BigFraction(amount) .multiply(BigFraction(rate.numerator, rate.denominator)) - } } diff --git a/src/main/kotlin/dev/vality/rateboss/service/ExRateServiceHandler.kt b/src/main/kotlin/dev/vality/rateboss/service/ExRateServiceHandler.kt index c4e46ac..c26f304 100644 --- a/src/main/kotlin/dev/vality/rateboss/service/ExRateServiceHandler.kt +++ b/src/main/kotlin/dev/vality/rateboss/service/ExRateServiceHandler.kt @@ -14,30 +14,35 @@ class ExRateServiceHandler( private val exRateDaoService: ExchangeDaoService, private val getCurrencyExchangeRateResultConverter: GetCurrencyExchangeRateResultConverter, private val conversionService: ConversionService, - private val timestampExchangeRateRequestConverter: TimestampExchangeRateRequestConverter + private val timestampExchangeRateRequestConverter: TimestampExchangeRateRequestConverter, ) : ExchangeRateServiceSrv.Iface { - override fun getExchangeRateData(request: GetCurrencyExchangeRateRequest): GetCurrencyExchangeRateResult { log.info("Get getExchangeRateData request with body: {} ", request) - val exchangeRateData = exRateDaoService.getRecentExchangeRateBySymbolicCodes( - request.currencyData.sourceCurrency, - request.currencyData.destinationCurrency - ) - val result = exchangeRateData?.let { - getCurrencyExchangeRateResultConverter.convert(exchangeRateData) - } ?: throw ExRateNotFound() + val exchangeRateData = + exRateDaoService.getRecentExchangeRateBySymbolicCodes( + request.currencyData.sourceCurrency, + request.currencyData.destinationCurrency, + ) + val result = + exchangeRateData?.let { + getCurrencyExchangeRateResultConverter.convert(exchangeRateData) + } ?: throw ExRateNotFound() log.info("Result getExchangeRateData with body: {}", result) return result } - override fun getConvertedAmount(sourceId: String, conversionRequest: ConversionRequest): Rational { + override fun getConvertedAmount( + sourceId: String, + conversionRequest: ConversionRequest, + ): Rational { log.info("Get getConvertedAmount request for source: {} with body: {} ", sourceId, conversionRequest) val exchangeTimestampRequest = timestampExchangeRateRequestConverter.convert(conversionRequest, sourceId) val exRateByTimestamp = exRateDaoService.getExchangeRateByTimestamp(exchangeTimestampRequest) - val result = exRateByTimestamp?.let { - val convertedAmount = conversionService.convertAmount(conversionRequest.amount, exRateByTimestamp) - Rational(convertedAmount.numeratorAsLong, convertedAmount.denominatorAsLong) - } ?: throw ExRateNotFound() + val result = + exRateByTimestamp?.let { + val convertedAmount = conversionService.convertAmount(conversionRequest.amount, exRateByTimestamp) + Rational(convertedAmount.numeratorAsLong, convertedAmount.denominatorAsLong) + } ?: throw ExRateNotFound() log.info("Result getConvertedAmount with body: {} ", result) return result } diff --git a/src/main/kotlin/dev/vality/rateboss/service/ExchangeDaoService.kt b/src/main/kotlin/dev/vality/rateboss/service/ExchangeDaoService.kt index f485751..452bbce 100644 --- a/src/main/kotlin/dev/vality/rateboss/service/ExchangeDaoService.kt +++ b/src/main/kotlin/dev/vality/rateboss/service/ExchangeDaoService.kt @@ -14,28 +14,30 @@ private val log = KotlinLogging.logger {} @Service class ExchangeDaoService( private val exRateDao: ExRateDao, - private val exRateConverter: ExRateConverter + private val exRateConverter: ExRateConverter, ) { - - fun saveExchangeRates( - exchangeRatesData: ExchangeRatesData - ) { - val exRates = exchangeRatesData.exchangeRates.rates.map { exchangeRatesMap -> - val exRate = exRateConverter.convert( - exchangeRatesData.destinationCurrencySymbolicCode, - exchangeRatesData.destinationCurrencyExponent, - exchangeRatesMap, - exchangeRatesData.exchangeRates.timestamp, - exchangeRatesData.sourceId - ) - exRate - } + fun saveExchangeRates(exchangeRatesData: ExchangeRatesData) { + val exRates = + exchangeRatesData.exchangeRates.rates.map { exchangeRatesMap -> + val exRate = + exRateConverter.convert( + exchangeRatesData.destinationCurrencySymbolicCode, + exchangeRatesData.destinationCurrencyExponent, + exchangeRatesMap, + exchangeRatesData.exchangeRates.timestamp, + exchangeRatesData.sourceId, + ) + exRate + } log.debug("Try to save exRate batch {}", exRates) exRateDao.saveBatch(exRates) log.info("Successfully save exRate batch with size: {}", exRates.size) } - fun getRecentExchangeRateBySymbolicCodes(sourceCode: String, destinationCode: String): ExchangeRateData? { + fun getRecentExchangeRateBySymbolicCodes( + sourceCode: String, + destinationCode: String, + ): ExchangeRateData? { val exRate = exRateDao.getRecentBySymbolicCodes(sourceCode, destinationCode) return exRate?.let { ExchangeRateData( @@ -44,7 +46,7 @@ class ExchangeDaoService( rationalP = it.rationalP, rationalQ = it.rationalQ, rateTimestamp = it.rateTimestamp, - source = it.source + source = it.source, ) } } diff --git a/src/main/kotlin/dev/vality/rateboss/service/ExchangeEventService.kt b/src/main/kotlin/dev/vality/rateboss/service/ExchangeEventService.kt index be3e8c2..4951ea2 100644 --- a/src/main/kotlin/dev/vality/rateboss/service/ExchangeEventService.kt +++ b/src/main/kotlin/dev/vality/rateboss/service/ExchangeEventService.kt @@ -23,40 +23,49 @@ private val log = KotlinLogging.logger {} @Service class ExchangeEventService( - private val kafkaTemplate: KafkaTemplate + private val kafkaTemplate: KafkaTemplate, ) { - @Value("\${kafka.topic.producer.exchangeTopic}") lateinit var topicName: String fun sendExchangeRates( baseCurrencySymbolCode: String, baseCurrencyExponent: Short, - exchangeRates: ExchangeRates + exchangeRates: ExchangeRates, ) { - val currencyEvents = exchangeRates.rates.map { exchangeRatesMap -> - val eventId = UUID.randomUUID().toString() - val eventTime = TypeUtil.temporalToString(LocalDateTime.now(), ZoneOffset.UTC) - val payload = buildCurrencyExchangeRatePayload( - baseCurrencySymbolCode, - baseCurrencyExponent, - exchangeRatesMap, - exchangeRates.timestamp - ) - CurrencyEvent(eventId, eventTime, payload) - } + val currencyEvents = + exchangeRates.rates.map { exchangeRatesMap -> + val eventId = UUID.randomUUID().toString() + val eventTime = TypeUtil.temporalToString(LocalDateTime.now(), ZoneOffset.UTC) + val payload = + buildCurrencyExchangeRatePayload( + baseCurrencySymbolCode, + baseCurrencyExponent, + exchangeRatesMap, + exchangeRates.timestamp, + ) + CurrencyEvent(eventId, eventTime, payload) + } for (currencyEvent in currencyEvents) { val producerRecord = ProducerRecord(topicName, currencyEvent.eventId, currencyEvent) - kafkaTemplate.send(producerRecord).addCallback( - { result -> - log.info { - "Successfully send currency event. Topic=" + result?.recordMetadata?.topic() + ";" + - " Offset=" + result?.recordMetadata?.offset() + ";" + - " Partition=" + result?.recordMetadata?.partition() + kafkaTemplate + .send(producerRecord) + .thenAccept( + { result -> + log.info { + "Successfully send currency event. Topic=" + result?.recordMetadata?.topic() + ";" + + " Offset=" + result?.recordMetadata?.offset() + ";" + + " Partition=" + result?.recordMetadata?.partition() + } } - }, - { log.error(it.cause) { "Failed to send event. Topic=${producerRecord.topic()}; Partition=${producerRecord.partition()}" } } - ) + ).exceptionally( + { err -> + log.error( + err.cause + ) { "Failed to send event. Topic=${producerRecord.topic()}; Partition=${producerRecord.partition()}" } + null + } + ) } } @@ -64,30 +73,26 @@ class ExchangeEventService( baseCurrencySymbolCode: String, baseCurrencyExponent: Short, exchangeRateMap: Map.Entry, - exchangeRateTimestamp: Long - ): CurrencyEventPayload? { - return CurrencyEventPayload.exchange_rate( + exchangeRateTimestamp: Long, + ): CurrencyEventPayload? = + CurrencyEventPayload.exchange_rate( CurrencyExchangeRate() .setSourceCurrency( Currency( baseCurrencySymbolCode, - baseCurrencyExponent - ) - ) - .setDestinationCurrency( + baseCurrencyExponent, + ), + ).setDestinationCurrency( Currency( exchangeRateMap.key, - exchangeRateMap.value.scale().toShort() - ) - ) - .setExchangeRate( + exchangeRateMap.value.scale().toShort(), + ), + ).setExchangeRate( Rational().apply { val rational = exchangeRateMap.value.toRational() p = rational.numerator q = rational.denominator - } - ) - .setTimestamp(TypeUtil.temporalToString(Instant.ofEpochSecond(exchangeRateTimestamp))) + }, + ).setTimestamp(TypeUtil.temporalToString(Instant.ofEpochSecond(exchangeRateTimestamp))), ) - } } diff --git a/src/main/kotlin/dev/vality/rateboss/service/model/ExchangeRateData.kt b/src/main/kotlin/dev/vality/rateboss/service/model/ExchangeRateData.kt index 531a709..9ec1bbd 100644 --- a/src/main/kotlin/dev/vality/rateboss/service/model/ExchangeRateData.kt +++ b/src/main/kotlin/dev/vality/rateboss/service/model/ExchangeRateData.kt @@ -8,5 +8,5 @@ data class ExchangeRateData( val rationalP: Long, val rationalQ: Long, val rateTimestamp: LocalDateTime, - val source: String + val source: String, ) diff --git a/src/main/kotlin/dev/vality/rateboss/service/model/TimestampExchangeRateRequest.kt b/src/main/kotlin/dev/vality/rateboss/service/model/TimestampExchangeRateRequest.kt index 9dcf1d8..aec139b 100644 --- a/src/main/kotlin/dev/vality/rateboss/service/model/TimestampExchangeRateRequest.kt +++ b/src/main/kotlin/dev/vality/rateboss/service/model/TimestampExchangeRateRequest.kt @@ -6,5 +6,5 @@ data class TimestampExchangeRateRequest( val sourceCurrency: String, val destinationCurrency: String, val rateTimestamp: LocalDateTime, - val source: String + val source: String, ) diff --git a/src/main/kotlin/dev/vality/rateboss/servlet/ExRateServlet.kt b/src/main/kotlin/dev/vality/rateboss/servlet/ExRateServlet.kt index a441bcd..84831ab 100644 --- a/src/main/kotlin/dev/vality/rateboss/servlet/ExRateServlet.kt +++ b/src/main/kotlin/dev/vality/rateboss/servlet/ExRateServlet.kt @@ -2,14 +2,13 @@ package dev.vality.rateboss.servlet import dev.vality.exrates.service.ExchangeRateServiceSrv import dev.vality.woody.thrift.impl.http.THServiceBuilder -import javax.servlet.* -import javax.servlet.annotation.WebServlet +import jakarta.servlet.* +import jakarta.servlet.annotation.WebServlet @WebServlet("/ex-rate") class ExRateServlet( - private val serverHandler: ExchangeRateServiceSrv.Iface + private val serverHandler: ExchangeRateServiceSrv.Iface, ) : GenericServlet() { - private lateinit var servlet: Servlet override fun init(config: ServletConfig) { @@ -17,7 +16,10 @@ class ExRateServlet( servlet = THServiceBuilder().build(ExchangeRateServiceSrv.Iface::class.java, serverHandler) } - override fun service(request: ServletRequest, response: ServletResponse) { + override fun service( + request: ServletRequest, + response: ServletResponse, + ) { servlet.service(request, response) } } diff --git a/src/main/kotlin/dev/vality/rateboss/source/ExchangeRateSourceException.kt b/src/main/kotlin/dev/vality/rateboss/source/ExchangeRateSourceException.kt index eeb1780..467a317 100644 --- a/src/main/kotlin/dev/vality/rateboss/source/ExchangeRateSourceException.kt +++ b/src/main/kotlin/dev/vality/rateboss/source/ExchangeRateSourceException.kt @@ -2,5 +2,5 @@ package dev.vality.rateboss.source class ExchangeRateSourceException( msg: String, - cause: Throwable? = null + cause: Throwable? = null, ) : RuntimeException(msg, cause) diff --git a/src/main/kotlin/dev/vality/rateboss/source/impl/CbrExchangeRateSource.kt b/src/main/kotlin/dev/vality/rateboss/source/impl/CbrExchangeRateSource.kt index d7cb575..d80b600 100644 --- a/src/main/kotlin/dev/vality/rateboss/source/impl/CbrExchangeRateSource.kt +++ b/src/main/kotlin/dev/vality/rateboss/source/impl/CbrExchangeRateSource.kt @@ -15,34 +15,33 @@ private val log = KotlinLogging.logger {} @Component class CbrExchangeRateSource( - private val cbrApiClient: CbrApiClient + private val cbrApiClient: CbrApiClient, ) : ExchangeRateSource { - override fun getExchangeRate(currencySymbolCode: String): ExchangeRates { val time = Instant.now() log.info("Trying to get exchange rates from cbr for currency={}, time={}", currencySymbolCode, time) - val response = try { - cbrApiClient.getExchangeRates(time) - } catch (e: Exception) { - throw ExchangeRateSourceException("Remote client exception", e) - } + val response = + try { + cbrApiClient.getExchangeRates(time) + } catch (e: Exception) { + throw ExchangeRateSourceException("Remote client exception", e) + } if (response.currencies.isNullOrEmpty()) { throw ExchangeRateSourceException("Unsuccessful response from CbrApi") } - val rates: Map = response.currencies!!.associate { - it.charCode!! to it.value!!.divide(it.nominal!!.toBigDecimal()) - } + val rates: Map = + response.currencies!!.associate { + it.charCode!! to it.value!!.divide(it.nominal!!.toBigDecimal()) + } val responseDate = response.date!! val nextDayTimestamp = responseDate.plusDays(1).atStartOfDay().toEpochSecond(ZoneOffset.UTC) log.info("Exchange rates from cbr have been retrieved, date=$responseDate, exchangeRates=$rates, targetTimestamp=$nextDayTimestamp") return ExchangeRates( rates = rates, - timestamp = nextDayTimestamp + timestamp = nextDayTimestamp, ) } - override fun getSourceId(): String { - return ExRateSources.CBR - } + override fun getSourceId(): String = ExRateSources.CBR } diff --git a/src/main/kotlin/dev/vality/rateboss/source/impl/FixerExchangeRateSource.kt b/src/main/kotlin/dev/vality/rateboss/source/impl/FixerExchangeRateSource.kt index 17dd25f..0056f74 100644 --- a/src/main/kotlin/dev/vality/rateboss/source/impl/FixerExchangeRateSource.kt +++ b/src/main/kotlin/dev/vality/rateboss/source/impl/FixerExchangeRateSource.kt @@ -9,26 +9,24 @@ import org.springframework.stereotype.Component @Component class FixerExchangeRateSource( - private val fixerApiClient: FixerApiClient + private val fixerApiClient: FixerApiClient, ) : ExchangeRateSource { - override fun getExchangeRate(currencySymbolCode: String): ExchangeRates { - val fixerLatestResponse = try { - fixerApiClient.getLatest(currencySymbolCode) - } catch (e: Exception) { - throw ExchangeRateSourceException("Remote client exception", e) - } + val fixerLatestResponse = + try { + fixerApiClient.getLatest(currencySymbolCode) + } catch (e: Exception) { + throw ExchangeRateSourceException("Remote client exception", e) + } if (!fixerLatestResponse.success) { throw ExchangeRateSourceException("Unsuccessful response from FixerApi") } return ExchangeRates( rates = fixerLatestResponse.rates!!, - timestamp = fixerLatestResponse.timestamp!! + timestamp = fixerLatestResponse.timestamp!!, ) } - override fun getSourceId(): String { - return ExRateSources.FIXER - } + override fun getSourceId(): String = ExRateSources.FIXER } diff --git a/src/main/kotlin/dev/vality/rateboss/source/model/ExchangeRates.kt b/src/main/kotlin/dev/vality/rateboss/source/model/ExchangeRates.kt index 57c5b6c..f7f58f4 100644 --- a/src/main/kotlin/dev/vality/rateboss/source/model/ExchangeRates.kt +++ b/src/main/kotlin/dev/vality/rateboss/source/model/ExchangeRates.kt @@ -4,5 +4,5 @@ import java.math.BigDecimal data class ExchangeRates( val rates: Map, - val timestamp: Long + val timestamp: Long, ) diff --git a/src/main/kotlin/dev/vality/rateboss/source/model/ExchangeRatesData.kt b/src/main/kotlin/dev/vality/rateboss/source/model/ExchangeRatesData.kt index 29dfa8d..44b0fb6 100644 --- a/src/main/kotlin/dev/vality/rateboss/source/model/ExchangeRatesData.kt +++ b/src/main/kotlin/dev/vality/rateboss/source/model/ExchangeRatesData.kt @@ -4,5 +4,5 @@ data class ExchangeRatesData( val destinationCurrencySymbolicCode: String, val destinationCurrencyExponent: Short, val exchangeRates: ExchangeRates, - val sourceId: String + val sourceId: String, ) diff --git a/src/test/kotlin/dev/vality/rateboss/ContainerConfiguration.kt b/src/test/kotlin/dev/vality/rateboss/ContainerConfiguration.kt index ab68dff..ae526b3 100644 --- a/src/test/kotlin/dev/vality/rateboss/ContainerConfiguration.kt +++ b/src/test/kotlin/dev/vality/rateboss/ContainerConfiguration.kt @@ -19,16 +19,18 @@ class ContainerConfiguration { } @JvmStatic - val postgresql: PostgreSQLContainer = PostgreSQLContainer("postgres:14-alpine").apply { - withDatabaseName("postgresql") - withUsername("user") - withPassword("password") - } + val postgresql: PostgreSQLContainer = + PostgreSQLContainer("postgres:14-alpine").apply { + withDatabaseName("postgresql") + withUsername("user") + withPassword("password") + } @JvmStatic - val kafka: KafkaContainer = KafkaContainer( - DockerImageName.parse("confluentinc/cp-kafka").withTag("7.0.1") - ).withEmbeddedZookeeper() + val kafka: KafkaContainer = + KafkaContainer( + DockerImageName.parse("confluentinc/cp-kafka").withTag("7.0.1"), + ).withEmbeddedZookeeper() @JvmStatic @DynamicPropertySource diff --git a/src/test/kotlin/dev/vality/rateboss/client/cbr/CbrApiClientTest.kt b/src/test/kotlin/dev/vality/rateboss/client/cbr/CbrApiClientTest.kt index 7396566..e724b80 100644 --- a/src/test/kotlin/dev/vality/rateboss/client/cbr/CbrApiClientTest.kt +++ b/src/test/kotlin/dev/vality/rateboss/client/cbr/CbrApiClientTest.kt @@ -1,7 +1,8 @@ package dev.vality.rateboss.client.cbr import dev.vality.rateboss.config.TestConfig -import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Assertions.assertNotNull +import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @@ -16,7 +17,6 @@ import java.time.Instant @ContextConfiguration(classes = [CbrApiClient::class]) @Import(TestConfig::class) class CbrApiClientTest { - @Autowired lateinit var cbrApiClient: CbrApiClient diff --git a/src/test/kotlin/dev/vality/rateboss/config/TestConfig.kt b/src/test/kotlin/dev/vality/rateboss/config/TestConfig.kt index 8315efd..2d45379 100644 --- a/src/test/kotlin/dev/vality/rateboss/config/TestConfig.kt +++ b/src/test/kotlin/dev/vality/rateboss/config/TestConfig.kt @@ -8,29 +8,27 @@ import java.time.ZoneId @TestConfiguration class TestConfig { - @Bean fun testRestTemplate() = RestTemplate() @Bean - fun testRatesProperties(): RatesProperties { - return RatesProperties( + fun testRatesProperties(): RatesProperties = + RatesProperties( JobDescription( "fixer-cron", "fixer-key", "fixer-name", - listOf(CurrencyProperties("USD", 2)) + listOf(CurrencyProperties("USD", 2)), ), JobDescription( "cbr-cron", "cbr-key", "cbr-name", - listOf(CurrencyProperties("RUB", 2)) + listOf(CurrencyProperties("RUB", 2)), ), RatesSourceProperties( FixerProperties("url", "key"), - CbrProperties("https://www.cbr.ru/scripts/XML_daily.asp", ZoneId.of("Europe/Moscow")) - ) + CbrProperties("https://www.cbr.ru/scripts/XML_daily.asp", ZoneId.of("Europe/Moscow")), + ), ) - } } diff --git a/src/test/kotlin/dev/vality/rateboss/dao/ExRateDaoImplTest.kt b/src/test/kotlin/dev/vality/rateboss/dao/ExRateDaoImplTest.kt index ccd6fb9..6215a5a 100644 --- a/src/test/kotlin/dev/vality/rateboss/dao/ExRateDaoImplTest.kt +++ b/src/test/kotlin/dev/vality/rateboss/dao/ExRateDaoImplTest.kt @@ -10,14 +10,13 @@ import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.quartz.Scheduler import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.mock.mockito.MockBean +import org.springframework.test.context.bean.override.mockito.MockitoBean import org.testcontainers.junit.jupiter.Testcontainers import java.time.LocalDateTime @Testcontainers class ExRateDaoImplTest : ContainerConfiguration() { - - @MockBean + @MockitoBean lateinit var scheduler: Scheduler @Autowired @@ -33,26 +32,28 @@ class ExRateDaoImplTest : ContainerConfiguration() { @Test fun saveBatchWithSameCodes() { - val firstExRate = ExRate().apply { - sourceCurrencySymbolicCode = "USD" - sourceCurrencyExponent = 2 - destinationCurrencySymbolicCode = "UZS" - destinationCurrencyExponent = 6 - rationalP = 11190000264 - rationalQ = 1000000 - rateTimestamp = LocalDateTime.now() - source = "source" - } - val secondExRate = ExRate().apply { - sourceCurrencySymbolicCode = "USD" - sourceCurrencyExponent = 2 - destinationCurrencySymbolicCode = "UZS" - destinationCurrencyExponent = 6 - rationalP = 1119000000 - rationalQ = 1000000 - rateTimestamp = LocalDateTime.now() - source = "source" - } + val firstExRate = + ExRate().apply { + sourceCurrencySymbolicCode = "USD" + sourceCurrencyExponent = 2 + destinationCurrencySymbolicCode = "UZS" + destinationCurrencyExponent = 6 + rationalP = 11190000264 + rationalQ = 1000000 + rateTimestamp = LocalDateTime.now() + source = "source" + } + val secondExRate = + ExRate().apply { + sourceCurrencySymbolicCode = "USD" + sourceCurrencyExponent = 2 + destinationCurrencySymbolicCode = "UZS" + destinationCurrencyExponent = 6 + rationalP = 1119000000 + rationalQ = 1000000 + rateTimestamp = LocalDateTime.now() + source = "source" + } val entities = listOf(firstExRate, secondExRate) exRateDao.saveBatch(entities) @@ -65,26 +66,28 @@ class ExRateDaoImplTest : ContainerConfiguration() { @Test fun saveBatch() { - val firstExRate = ExRate().apply { - sourceCurrencySymbolicCode = "USD" - sourceCurrencyExponent = 2 - destinationCurrencySymbolicCode = "UZS" - destinationCurrencyExponent = 6 - rationalP = 11190000264 - rationalQ = 1000000 - rateTimestamp = LocalDateTime.now() - source = "source" - } - val secondExRate = ExRate().apply { - sourceCurrencySymbolicCode = "USD" - sourceCurrencyExponent = 2 - destinationCurrencySymbolicCode = "RUB" - destinationCurrencyExponent = 6 - rationalP = 1119000000 - rationalQ = 1000000 - rateTimestamp = LocalDateTime.now() - source = "source" - } + val firstExRate = + ExRate().apply { + sourceCurrencySymbolicCode = "USD" + sourceCurrencyExponent = 2 + destinationCurrencySymbolicCode = "UZS" + destinationCurrencyExponent = 6 + rationalP = 11190000264 + rationalQ = 1000000 + rateTimestamp = LocalDateTime.now() + source = "source" + } + val secondExRate = + ExRate().apply { + sourceCurrencySymbolicCode = "USD" + sourceCurrencyExponent = 2 + destinationCurrencySymbolicCode = "RUB" + destinationCurrencyExponent = 6 + rationalP = 1119000000 + rationalQ = 1000000 + rateTimestamp = LocalDateTime.now() + source = "source" + } val entities = listOf(firstExRate, secondExRate) exRateDao.saveBatch(entities) @@ -97,27 +100,30 @@ class ExRateDaoImplTest : ContainerConfiguration() { fun getRecentBySymbolicCodes() { val sourceCurrency = "USD" val destinationCurrency = "UZS" - val oldExRate = ExRate().apply { - sourceCurrencySymbolicCode = sourceCurrency - sourceCurrencyExponent = 2 - destinationCurrencySymbolicCode = destinationCurrency - destinationCurrencyExponent = 6 - rationalP = 11190000264 - rationalQ = 1000000 - rateTimestamp = LocalDateTime.now().minusHours(1) - source = "source" - } - val recentExRate = ExRate().apply { - sourceCurrencySymbolicCode = sourceCurrency - sourceCurrencyExponent = 2 - destinationCurrencySymbolicCode = destinationCurrency - destinationCurrencyExponent = 6 - rationalP = 1119000000 - rationalQ = 1000000 - rateTimestamp = LocalDateTime.now() - source = "source" - } - dslContext.insertInto(EX_RATE) + val oldExRate = + ExRate().apply { + sourceCurrencySymbolicCode = sourceCurrency + sourceCurrencyExponent = 2 + destinationCurrencySymbolicCode = destinationCurrency + destinationCurrencyExponent = 6 + rationalP = 11190000264 + rationalQ = 1000000 + rateTimestamp = LocalDateTime.now().minusHours(1) + source = "source" + } + val recentExRate = + ExRate().apply { + sourceCurrencySymbolicCode = sourceCurrency + sourceCurrencyExponent = 2 + destinationCurrencySymbolicCode = destinationCurrency + destinationCurrencyExponent = 6 + rationalP = 1119000000 + rationalQ = 1000000 + rateTimestamp = LocalDateTime.now() + source = "source" + } + dslContext + .insertInto(EX_RATE) .set(dslContext.newRecord(EX_RATE, oldExRate)) .newRecord() .set(dslContext.newRecord(EX_RATE, recentExRate)) @@ -135,37 +141,41 @@ class ExRateDaoImplTest : ContainerConfiguration() { val destinationCurrency = "USD" val sourceCurrency = "UZS" val sourceId = "source" - val firstExRate = ExRate().apply { - sourceCurrencySymbolicCode = sourceCurrency - sourceCurrencyExponent = 2 - destinationCurrencySymbolicCode = destinationCurrency - destinationCurrencyExponent = 2 - rationalP = 11190000264 - rationalQ = 1000055 - rateTimestamp = LocalDateTime.now().minusDays(3) - source = sourceId - } - val secondExRate = ExRate().apply { - sourceCurrencySymbolicCode = sourceCurrency - sourceCurrencyExponent = 2 - destinationCurrencySymbolicCode = destinationCurrency - destinationCurrencyExponent = 2 - rationalP = 1119000546 - rationalQ = 1000066 - rateTimestamp = LocalDateTime.now().minusDays(2) - source = sourceId - } - val thirdExRate = ExRate().apply { - sourceCurrencySymbolicCode = sourceCurrency - sourceCurrencyExponent = 2 - destinationCurrencySymbolicCode = destinationCurrency - destinationCurrencyExponent = 2 - rationalP = 1119000229 - rationalQ = 1000077 - rateTimestamp = LocalDateTime.now().minusDays(1) - source = sourceId - } - dslContext.insertInto(EX_RATE) + val firstExRate = + ExRate().apply { + sourceCurrencySymbolicCode = sourceCurrency + sourceCurrencyExponent = 2 + destinationCurrencySymbolicCode = destinationCurrency + destinationCurrencyExponent = 2 + rationalP = 11190000264 + rationalQ = 1000055 + rateTimestamp = LocalDateTime.now().minusDays(3) + source = sourceId + } + val secondExRate = + ExRate().apply { + sourceCurrencySymbolicCode = sourceCurrency + sourceCurrencyExponent = 2 + destinationCurrencySymbolicCode = destinationCurrency + destinationCurrencyExponent = 2 + rationalP = 1119000546 + rationalQ = 1000066 + rateTimestamp = LocalDateTime.now().minusDays(2) + source = sourceId + } + val thirdExRate = + ExRate().apply { + sourceCurrencySymbolicCode = sourceCurrency + sourceCurrencyExponent = 2 + destinationCurrencySymbolicCode = destinationCurrency + destinationCurrencyExponent = 2 + rationalP = 1119000229 + rationalQ = 1000077 + rateTimestamp = LocalDateTime.now().minusDays(1) + source = sourceId + } + dslContext + .insertInto(EX_RATE) .set(dslContext.newRecord(EX_RATE, firstExRate)) .newRecord() .set(dslContext.newRecord(EX_RATE, secondExRate)) @@ -173,33 +183,36 @@ class ExRateDaoImplTest : ContainerConfiguration() { .set(dslContext.newRecord(EX_RATE, thirdExRate)) .execute() - val requestTimestampBeforeFirstExRate = TimestampExchangeRateRequest( - sourceCurrency = sourceCurrency, - destinationCurrency = destinationCurrency, - source = sourceId, - rateTimestamp = firstExRate.rateTimestamp.minusDays(1) - ) + val requestTimestampBeforeFirstExRate = + TimestampExchangeRateRequest( + sourceCurrency = sourceCurrency, + destinationCurrency = destinationCurrency, + source = sourceId, + rateTimestamp = firstExRate.rateTimestamp.minusDays(1), + ) val emptyResult = exRateDao.getByCodesAndTimestamp(requestTimestampBeforeFirstExRate) assertNull(emptyResult) - val requestTimestampBetweenSecondAndThirdExRate = TimestampExchangeRateRequest( - sourceCurrency = sourceCurrency, - destinationCurrency = destinationCurrency, - source = sourceId, - rateTimestamp = secondExRate.rateTimestamp.plusMinutes(10) - ) + val requestTimestampBetweenSecondAndThirdExRate = + TimestampExchangeRateRequest( + sourceCurrency = sourceCurrency, + destinationCurrency = destinationCurrency, + source = sourceId, + rateTimestamp = secondExRate.rateTimestamp.plusMinutes(10), + ) val secondResult = exRateDao.getByCodesAndTimestamp(requestTimestampBetweenSecondAndThirdExRate)!! assertEquals(secondExRate.rationalP, secondResult.rationalP) assertEquals(secondExRate.rationalQ, secondResult.rationalQ) - val requestTimestampBetweenAfterThirdExRate = TimestampExchangeRateRequest( - sourceCurrency = sourceCurrency, - destinationCurrency = destinationCurrency, - source = sourceId, - rateTimestamp = thirdExRate.rateTimestamp.plusMinutes(10) - ) + val requestTimestampBetweenAfterThirdExRate = + TimestampExchangeRateRequest( + sourceCurrency = sourceCurrency, + destinationCurrency = destinationCurrency, + source = sourceId, + rateTimestamp = thirdExRate.rateTimestamp.plusMinutes(10), + ) val thirdResult = exRateDao.getByCodesAndTimestamp(requestTimestampBetweenAfterThirdExRate)!! assertEquals(thirdExRate.rationalP, thirdResult.rationalP) diff --git a/src/test/kotlin/dev/vality/rateboss/job/CbrExchangeGrabberJobTest.kt b/src/test/kotlin/dev/vality/rateboss/job/CbrExchangeGrabberJobTest.kt index 42e96cb..df34df5 100644 --- a/src/test/kotlin/dev/vality/rateboss/job/CbrExchangeGrabberJobTest.kt +++ b/src/test/kotlin/dev/vality/rateboss/job/CbrExchangeGrabberJobTest.kt @@ -17,8 +17,8 @@ import org.quartz.TriggerKey import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Qualifier import org.springframework.boot.test.context.SpringBootTest -import org.springframework.boot.test.mock.mockito.MockBean -import org.springframework.boot.test.mock.mockito.SpyBean +import org.springframework.test.context.bean.override.mockito.MockitoBean +import org.springframework.test.context.bean.override.mockito.MockitoSpyBean import java.math.BigDecimal import java.time.Instant import java.util.concurrent.TimeUnit @@ -27,15 +27,14 @@ import java.util.concurrent.TimeUnit properties = [ "rates.cbr-job.jobCron=0/5 * * * * ?", "rates.cbr-job.currencies.[0].symbolCode=RUB", - "rates.cbr-job.currencies.[0].exponent=2" - ] + "rates.cbr-job.currencies.[0].exponent=2", + ], ) class CbrExchangeGrabberJobTest : ContainerConfiguration() { - - @SpyBean + @MockitoSpyBean lateinit var exchangeDaoService: ExchangeDaoService - @MockBean + @MockitoBean lateinit var cbrExchangeRateSource: CbrExchangeRateSource @Autowired @@ -55,11 +54,12 @@ class CbrExchangeGrabberJobTest : ContainerConfiguration() { whenever(cbrExchangeRateSource.getSourceId()).thenReturn("sourceId") whenever(cbrExchangeRateSource.getExchangeRate(any())).then { ExchangeRates( - rates = mapOf( - "USD" to BigDecimal.valueOf(90.593066), - "EUR" to BigDecimal.valueOf(98.376632) - ), - timestamp = Instant.now().epochSecond + rates = + mapOf( + "USD" to BigDecimal.valueOf(90.593066), + "EUR" to BigDecimal.valueOf(98.376632), + ), + timestamp = Instant.now().epochSecond, ) } await().atMost(5, TimeUnit.SECONDS).untilAsserted { diff --git a/src/test/kotlin/dev/vality/rateboss/job/FixerExchangeGrabberJobTest.kt b/src/test/kotlin/dev/vality/rateboss/job/FixerExchangeGrabberJobTest.kt index fc6269e..2bba1e1 100644 --- a/src/test/kotlin/dev/vality/rateboss/job/FixerExchangeGrabberJobTest.kt +++ b/src/test/kotlin/dev/vality/rateboss/job/FixerExchangeGrabberJobTest.kt @@ -20,34 +20,33 @@ import org.quartz.TriggerKey import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Qualifier import org.springframework.boot.test.context.SpringBootTest -import org.springframework.boot.test.mock.mockito.MockBean -import org.springframework.boot.test.mock.mockito.SpyBean import org.springframework.kafka.core.KafkaTemplate import org.springframework.kafka.support.SendResult -import org.springframework.util.concurrent.SettableListenableFuture +import org.springframework.test.context.bean.override.mockito.MockitoBean +import org.springframework.test.context.bean.override.mockito.MockitoSpyBean import java.math.BigDecimal import java.time.Instant +import java.util.concurrent.CompletableFuture import java.util.concurrent.TimeUnit @SpringBootTest( properties = [ "rates.fixer-job.jobCron=0/5 * * * * ?", "rates.fixer-job.currencies.[0].symbolCode=USD", - "rates.fixer-job.currencies.[0].exponent=2" - ] + "rates.fixer-job.currencies.[0].exponent=2", + ], ) class FixerExchangeGrabberJobTest : ContainerConfiguration() { - - @SpyBean + @MockitoSpyBean lateinit var exchangeDaoService: ExchangeDaoService - @SpyBean + @MockitoSpyBean lateinit var exchangeEventService: ExchangeEventService - @MockBean + @MockitoBean lateinit var kafkaTemplate: KafkaTemplate - @MockBean + @MockitoBean lateinit var fixerExchangeRateSource: FixerExchangeRateSource @Autowired @@ -67,14 +66,15 @@ class FixerExchangeGrabberJobTest : ContainerConfiguration() { whenever(fixerExchangeRateSource.getSourceId()).thenReturn("sourceId") whenever(fixerExchangeRateSource.getExchangeRate(any())).then { ExchangeRates( - rates = mapOf( - "AED" to BigDecimal.valueOf(3.593066), - "AMD" to BigDecimal.valueOf(397.376632) - ), - timestamp = Instant.now().epochSecond + rates = + mapOf( + "AED" to BigDecimal.valueOf(3.593066), + "AMD" to BigDecimal.valueOf(397.376632), + ), + timestamp = Instant.now().epochSecond, ) } - val future = SettableListenableFuture>() + val future = CompletableFuture>() whenever(kafkaTemplate.send(any>())).thenReturn(future) await().atMost(30, TimeUnit.SECONDS).untilAsserted { diff --git a/src/test/kotlin/dev/vality/rateboss/service/ExRateServiceHandlerTest.kt b/src/test/kotlin/dev/vality/rateboss/service/ExRateServiceHandlerTest.kt index d8639db..12e46b6 100644 --- a/src/test/kotlin/dev/vality/rateboss/service/ExRateServiceHandlerTest.kt +++ b/src/test/kotlin/dev/vality/rateboss/service/ExRateServiceHandlerTest.kt @@ -15,15 +15,14 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import org.quartz.Scheduler import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.mock.mockito.MockBean +import org.springframework.test.context.bean.override.mockito.MockitoBean import org.testcontainers.junit.jupiter.Testcontainers import java.time.LocalDateTime import java.time.format.DateTimeFormatter @Testcontainers class ExRateServiceHandlerTest : ContainerConfiguration() { - - @MockBean + @MockitoBean lateinit var scheduler: Scheduler @Autowired @@ -41,12 +40,13 @@ class ExRateServiceHandlerTest : ContainerConfiguration() { fun getExchangeRateWithoutData() { val sourceCurrency = "USD" val destinationCurrency = "UZS" - val request = GetCurrencyExchangeRateRequest() - .setCurrencyData( - CurrencyData() - .setSourceCurrency(sourceCurrency) - .setDestinationCurrency(destinationCurrency) - ) + val request = + GetCurrencyExchangeRateRequest() + .setCurrencyData( + CurrencyData() + .setSourceCurrency(sourceCurrency) + .setDestinationCurrency(destinationCurrency), + ) assertThrows { exRateServiceHandler.getExchangeRateData(request) } } @@ -55,25 +55,28 @@ class ExRateServiceHandlerTest : ContainerConfiguration() { val sourceCurrency = "USD" val destinationCurrency = "UZS" val sourceId = "sourceId" - val exRate = ExRate().apply { - sourceCurrencySymbolicCode = sourceCurrency - sourceCurrencyExponent = 2 - destinationCurrencySymbolicCode = destinationCurrency - destinationCurrencyExponent = 6 - rationalP = 11190000264 - rationalQ = 1000000 - rateTimestamp = LocalDateTime.now() - source = sourceId - } - dslContext.insertInto(Tables.EX_RATE) + val exRate = + ExRate().apply { + sourceCurrencySymbolicCode = sourceCurrency + sourceCurrencyExponent = 2 + destinationCurrencySymbolicCode = destinationCurrency + destinationCurrencyExponent = 6 + rationalP = 11190000264 + rationalQ = 1000000 + rateTimestamp = LocalDateTime.now() + source = sourceId + } + dslContext + .insertInto(Tables.EX_RATE) .set(dslContext.newRecord(Tables.EX_RATE, exRate)) .execute() - val request = GetCurrencyExchangeRateRequest() - .setCurrencyData( - CurrencyData() - .setSourceCurrency(sourceCurrency) - .setDestinationCurrency(destinationCurrency) - ) + val request = + GetCurrencyExchangeRateRequest() + .setCurrencyData( + CurrencyData() + .setSourceCurrency(sourceCurrency) + .setDestinationCurrency(destinationCurrency), + ) val result = exRateServiceHandler.getExchangeRateData(request) @@ -83,13 +86,13 @@ class ExRateServiceHandlerTest : ContainerConfiguration() { @Test fun getEmptyExRateForConvertedAmount() { - val conversionRequest = ConversionRequest() - .setAmount(100L) - .setDatetime( - LocalDateTime.now().format(DateTimeFormatter.ofPattern(Constants.DATE_TIME_FORMAT)) - ) - .setDestination("USD") - .setSource("UZS") + val conversionRequest = + ConversionRequest() + .setAmount(100L) + .setDatetime( + LocalDateTime.now().format(DateTimeFormatter.ofPattern(Constants.DATE_TIME_FORMAT)), + ).setDestination("USD") + .setSource("UZS") assertThrows(ExRateNotFound::class.java) { exRateServiceHandler.getConvertedAmount("sourceId", conversionRequest) @@ -101,26 +104,28 @@ class ExRateServiceHandlerTest : ContainerConfiguration() { val sourceCurrency = "UZS" val destinationCurrency = "USD" val sourceId = "sourceId" - val exRate = ExRate().apply { - sourceCurrencySymbolicCode = sourceCurrency - sourceCurrencyExponent = 2 - destinationCurrencySymbolicCode = destinationCurrency - destinationCurrencyExponent = 2 - rationalP = 1398750033 - rationalQ = 125000 - rateTimestamp = LocalDateTime.now().minusDays(1) - source = sourceId - } - dslContext.insertInto(Tables.EX_RATE) + val exRate = + ExRate().apply { + sourceCurrencySymbolicCode = sourceCurrency + sourceCurrencyExponent = 2 + destinationCurrencySymbolicCode = destinationCurrency + destinationCurrencyExponent = 2 + rationalP = 1398750033 + rationalQ = 125000 + rateTimestamp = LocalDateTime.now().minusDays(1) + source = sourceId + } + dslContext + .insertInto(Tables.EX_RATE) .set(dslContext.newRecord(Tables.EX_RATE, exRate)) .execute() - val conversionRequest = ConversionRequest() - .setAmount(100L) - .setDatetime( - exRate.rateTimestamp.plusMinutes(10).format(DateTimeFormatter.ofPattern(Constants.DATE_TIME_FORMAT)) - ) - .setDestination(destinationCurrency) - .setSource(sourceCurrency) + val conversionRequest = + ConversionRequest() + .setAmount(100L) + .setDatetime( + exRate.rateTimestamp.plusMinutes(10).format(DateTimeFormatter.ofPattern(Constants.DATE_TIME_FORMAT)), + ).setDestination(destinationCurrency) + .setSource(sourceCurrency) val result = exRateServiceHandler.getConvertedAmount(sourceId, conversionRequest) diff --git a/src/test/kotlin/dev/vality/rateboss/service/ExchangeDaoServiceTest.kt b/src/test/kotlin/dev/vality/rateboss/service/ExchangeDaoServiceTest.kt index edcdaba..104ce2c 100644 --- a/src/test/kotlin/dev/vality/rateboss/service/ExchangeDaoServiceTest.kt +++ b/src/test/kotlin/dev/vality/rateboss/service/ExchangeDaoServiceTest.kt @@ -14,13 +14,12 @@ import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.quartz.Scheduler import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.mock.mockito.MockBean +import org.springframework.test.context.bean.override.mockito.MockitoBean import java.math.BigDecimal import java.time.Instant class ExchangeDaoServiceTest : ContainerConfiguration() { - - @MockBean + @MockitoBean lateinit var scheduler: Scheduler @Autowired @@ -39,21 +38,24 @@ class ExchangeDaoServiceTest : ContainerConfiguration() { // Given val baseCurrencySymbolCode = "USD" val baseCurrencyExponent = 2 - val exchangeRates = ExchangeRates( - rates = mapOf( - "RUB" to BigDecimal.valueOf(56.761762), - "AED" to BigDecimal.valueOf(3.593066), - "AMD" to BigDecimal.valueOf(397.376632) - ), - timestamp = Instant.now().epochSecond - ) + val exchangeRates = + ExchangeRates( + rates = + mapOf( + "RUB" to BigDecimal.valueOf(56.761762), + "AED" to BigDecimal.valueOf(3.593066), + "AMD" to BigDecimal.valueOf(397.376632), + ), + timestamp = Instant.now().epochSecond, + ) - val exchangeRatesData = ExchangeRatesData( - destinationCurrencySymbolicCode = baseCurrencySymbolCode, - destinationCurrencyExponent = baseCurrencyExponent.toShort(), - exchangeRates = exchangeRates, - sourceId = "source" - ) + val exchangeRatesData = + ExchangeRatesData( + destinationCurrencySymbolicCode = baseCurrencySymbolCode, + destinationCurrencyExponent = baseCurrencyExponent.toShort(), + exchangeRates = exchangeRates, + sourceId = "source", + ) // When exchangeDaoService.saveExchangeRates(exchangeRatesData) @@ -62,9 +64,11 @@ class ExchangeDaoServiceTest : ContainerConfiguration() { val records = dslContext.fetch(EX_RATE) assertEquals(exchangeRates.rates.size, records.size) - val codes = records.stream() - .map(ExRateRecord::getSourceCurrencySymbolicCode) - .toList() + val codes = + records + .stream() + .map(ExRateRecord::getSourceCurrencySymbolicCode) + .toList() assertThat(codes, contains("RUB", "AED", "AMD")) } } diff --git a/src/test/kotlin/dev/vality/rateboss/service/ExchangeEventServiceTest.kt b/src/test/kotlin/dev/vality/rateboss/service/ExchangeEventServiceTest.kt index 6e2554f..9749433 100644 --- a/src/test/kotlin/dev/vality/rateboss/service/ExchangeEventServiceTest.kt +++ b/src/test/kotlin/dev/vality/rateboss/service/ExchangeEventServiceTest.kt @@ -12,21 +12,20 @@ import org.mockito.kotlin.times import org.mockito.kotlin.verify import org.quartz.Scheduler import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.mock.mockito.MockBean -import org.springframework.boot.test.mock.mockito.SpyBean import org.springframework.kafka.core.KafkaTemplate +import org.springframework.test.context.bean.override.mockito.MockitoBean +import org.springframework.test.context.bean.override.mockito.MockitoSpyBean import java.math.BigDecimal import java.time.Instant class ExchangeEventServiceTest : ContainerConfiguration() { - - @MockBean + @MockitoBean lateinit var scheduler: Scheduler @Autowired lateinit var exchangeEventService: ExchangeEventService - @SpyBean + @MockitoSpyBean lateinit var kafkaTemplate: KafkaTemplate @Test @@ -34,14 +33,16 @@ class ExchangeEventServiceTest : ContainerConfiguration() { // Given val baseCurrencySymbolCode = "USD" val baseCurrencyExponent = 2 - val exchangeRates = ExchangeRates( - rates = mapOf( - "RUB" to BigDecimal.valueOf(56.761762), - "AED" to BigDecimal.valueOf(3.593066), - "AMD" to BigDecimal.valueOf(397.376632) - ), - timestamp = Instant.now().epochSecond - ) + val exchangeRates = + ExchangeRates( + rates = + mapOf( + "RUB" to BigDecimal.valueOf(56.761762), + "AED" to BigDecimal.valueOf(3.593066), + "AMD" to BigDecimal.valueOf(397.376632), + ), + timestamp = Instant.now().epochSecond, + ) // When exchangeEventService.sendExchangeRates(baseCurrencySymbolCode, baseCurrencyExponent.toShort(), exchangeRates) @@ -57,13 +58,15 @@ class ExchangeEventServiceTest : ContainerConfiguration() { val baseCurrencyExponent = 2 val rubExchangeRate = BigDecimal.valueOf(56.761762) val btcExchangeRate = BigDecimal.valueOf(5.0203877e-05) - val exchangeRates = ExchangeRates( - rates = mapOf( - "RUB" to rubExchangeRate, - "BTC" to btcExchangeRate - ), - timestamp = Instant.now().epochSecond - ) + val exchangeRates = + ExchangeRates( + rates = + mapOf( + "RUB" to rubExchangeRate, + "BTC" to btcExchangeRate, + ), + timestamp = Instant.now().epochSecond, + ) // When exchangeEventService.sendExchangeRates(baseCurrencySymbolCode, baseCurrencyExponent.toShort(), exchangeRates) @@ -74,10 +77,31 @@ class ExchangeEventServiceTest : ContainerConfiguration() { val firstRecordCurrencyEvent = argumentCaptor.allValues[0].value() val secondRecordCurrencyEvent = argumentCaptor.allValues[1].value() val firstRecordExchangeRate = - BigDecimal.valueOf(firstRecordCurrencyEvent.getPayload().exchangeRate.exchange_rate.p) - .divideAndRemainder(BigDecimal.valueOf(firstRecordCurrencyEvent.getPayload().exchangeRate.exchange_rate.q)) - val secondRecordExchangeRate = BigDecimal.valueOf(secondRecordCurrencyEvent.getPayload().exchangeRate.exchange_rate.p) - .divide(BigDecimal.valueOf(secondRecordCurrencyEvent.getPayload().exchangeRate.exchange_rate.q)) + BigDecimal + .valueOf( + firstRecordCurrencyEvent + .getPayload() + .exchangeRate.exchange_rate.p, + ).divideAndRemainder( + BigDecimal.valueOf( + firstRecordCurrencyEvent + .getPayload() + .exchangeRate.exchange_rate.q, + ), + ) + val secondRecordExchangeRate = + BigDecimal + .valueOf( + secondRecordCurrencyEvent + .getPayload() + .exchangeRate.exchange_rate.p, + ).divide( + BigDecimal.valueOf( + secondRecordCurrencyEvent + .getPayload() + .exchangeRate.exchange_rate.q, + ), + ) assertTrue(BigDecimal("${firstRecordExchangeRate[0]}.${firstRecordExchangeRate[1]}") == rubExchangeRate) assertTrue(secondRecordExchangeRate == btcExchangeRate) } diff --git a/src/test/kotlin/dev/vality/rateboss/source/CbrExchangeRateSourceTest.kt b/src/test/kotlin/dev/vality/rateboss/source/CbrExchangeRateSourceTest.kt index 4087f28..3ff5fd8 100644 --- a/src/test/kotlin/dev/vality/rateboss/source/CbrExchangeRateSourceTest.kt +++ b/src/test/kotlin/dev/vality/rateboss/source/CbrExchangeRateSourceTest.kt @@ -4,7 +4,6 @@ import dev.vality.rateboss.client.cbr.CbrApiClient import dev.vality.rateboss.client.cbr.model.CbrCurrencyData import dev.vality.rateboss.client.cbr.model.CbrExchangeRateData import dev.vality.rateboss.config.TestConfig -import dev.vality.rateboss.config.properties.* import dev.vality.rateboss.source.impl.CbrExchangeRateSource import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test @@ -12,9 +11,9 @@ import org.junit.jupiter.api.extension.ExtendWith import org.mockito.kotlin.any import org.mockito.kotlin.whenever import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.mock.mockito.MockBean import org.springframework.context.annotation.Import import org.springframework.test.context.ContextConfiguration +import org.springframework.test.context.bean.override.mockito.MockitoBean import org.springframework.test.context.junit.jupiter.SpringExtension import org.springframework.web.client.ResourceAccessException import java.math.BigDecimal @@ -24,11 +23,10 @@ import java.time.LocalDate @ContextConfiguration(classes = [CbrApiClient::class, CbrExchangeRateSource::class]) @Import(TestConfig::class) class CbrExchangeRateSourceTest { - @Autowired lateinit var exchangeRateSource: ExchangeRateSource - @MockBean + @MockitoBean lateinit var cbrApiClient: CbrApiClient @Test @@ -36,9 +34,10 @@ class CbrExchangeRateSourceTest { val currencySymbolCode = "RUB" whenever(cbrApiClient.getExchangeRates(any())).thenThrow(ResourceAccessException("Error")) - val exception = assertThrows(ExchangeRateSourceException::class.java) { - exchangeRateSource.getExchangeRate(currencySymbolCode) - } + val exception = + assertThrows(ExchangeRateSourceException::class.java) { + exchangeRateSource.getExchangeRate(currencySymbolCode) + } assertEquals("Remote client exception", exception.message) } @@ -50,9 +49,10 @@ class CbrExchangeRateSourceTest { cbrExchangeRateData.currencies = emptyList() whenever(cbrApiClient.getExchangeRates(any())).thenReturn(cbrExchangeRateData) - val exception = assertThrows(ExchangeRateSourceException::class.java) { - exchangeRateSource.getExchangeRate(currencySymbolCode) - } + val exception = + assertThrows(ExchangeRateSourceException::class.java) { + exchangeRateSource.getExchangeRate(currencySymbolCode) + } assertEquals("Unsuccessful response from CbrApi", exception.message) }