From 4d18a37ac7be90ba6c176e761078749ca1d90bc5 Mon Sep 17 00:00:00 2001 From: echerniak Date: Fri, 3 Sep 2021 14:30:01 +0300 Subject: [PATCH 01/37] Template generated --- .gitignore | 78 +++++++++ .gitmodules | 4 + Jenkinsfile | 15 ++ build_utils | 1 + pom.xml | 150 ++++++++++++++++++ .../rbkmoney/anapiv/AnapiV2Application.java | 15 ++ src/main/resources/application.yml | 36 +++++ .../anapiv/AnapiV2ApplicationTest.java | 16 ++ src/test/resources/logback-test.xml | 10 ++ 9 files changed, 325 insertions(+) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 Jenkinsfile create mode 160000 build_utils create mode 100644 pom.xml create mode 100644 src/main/java/com/rbkmoney/anapiv/AnapiV2Application.java create mode 100644 src/main/resources/application.yml create mode 100644 src/test/java/com/rbkmoney/anapiv/AnapiV2ApplicationTest.java create mode 100644 src/test/resources/logback-test.xml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fb08ff7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,78 @@ +# Created by .ignore support plugin (hsz.mobi) +.eunit +deps +*.o +*.beam +*.plt +erl_crash.dump +ebin/*.beam +rel/example_project +.concrete/DEV_MODE +.rebar +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea/ +.idea/workspace.xml +.idea/tasks.xml +.idea/dictionaries +.idea/vcs.xml +.idea/jsLibraryMappings.xml + +# Sensitive or high-churn files: +.idea/dataSources.ids +.idea/dataSources.xml +.idea/dataSources.local.xml +.idea/sqlDataSources.xml +.idea/dynamic.xml +.idea/uiDesigner.xml + +# Gradle: +.idea/gradle.xml +.idea/libraries + +# Mongo Explorer plugin: +.idea/mongoSettings.xml + +*.iws +*.ipr +*.iml + + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties +*.class + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.ear + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +env.list diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..ca5a761 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "build_utils"] + path = build_utils + url = git@github.com:rbkmoney/build_utils.git + branch = master diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..4b775e2 --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,15 @@ +#!groovy +build('anapi-v2', 'java-maven') { + checkoutRepo() + loadBuildUtils() + + def javaServicePipeline + runStage('load JavaService pipeline') { + javaServicePipeline = load("build_utils/jenkins_lib/pipeJavaServiceInsideDocker.groovy") + } + + def serviceName = env.REPO_NAME + def mvnArgs = '-DjvmArgs="-Xmx256m"' + + javaServicePipeline(serviceName, mvnArgs) +} \ No newline at end of file diff --git a/build_utils b/build_utils new file mode 160000 index 0000000..c12c9dd --- /dev/null +++ b/build_utils @@ -0,0 +1 @@ +Subproject commit c12c9dd296912ec3c34ad443b448df98fef2556a diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..2eddb12 --- /dev/null +++ b/pom.xml @@ -0,0 +1,150 @@ + + + 4.0.0 + + + com.rbkmoney + service-parent-pom + 2.0.7 + + + anapi-v2 + 0.0.1-SNAPSHOT + jar + + anapi-v2 + + + + UTF-8 + UTF-8 + 15 + 8022 + 8023 + ${server.port} ${management.port} + ${env.REGISTRY} + + + + + + + com.rbkmoney + custom-metrics-spring-boot-starter + + + com.rbkmoney + custom-actuator-endpoints + + + com.rbkmoney.woody + woody-thrift + + + com.rbkmoney + shared-resources + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-web + + + org.hibernate + hibernate-validator + + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + + javax.servlet + javax.servlet-api + + + org.projectlombok + lombok + provided + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + ${project.basedir}/src/main/java + ${project.basedir}/src/test/java + + + ${project.build.directory}/maven-shared-archive-resources + ${project.build.directory} + + Dockerfile + + true + + + ${project.build.directory}/maven-shared-archive-resources + true + + Dockerfile + + + + src/main/resources + true + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-remote-resources-plugin + 1.6.0 + + + org.apache.maven.shared + maven-filtering + 1.3 + + + + + com.rbkmoney:shared-resources:${shared-resources.version} + + false + false + + + + + process + + + + + + + diff --git a/src/main/java/com/rbkmoney/anapiv/AnapiV2Application.java b/src/main/java/com/rbkmoney/anapiv/AnapiV2Application.java new file mode 100644 index 0000000..7f1be4f --- /dev/null +++ b/src/main/java/com/rbkmoney/anapiv/AnapiV2Application.java @@ -0,0 +1,15 @@ +package com.rbkmoney.anapiv; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.web.servlet.ServletComponentScan; + +@ServletComponentScan +@SpringBootApplication +public class AnapiV2Application extends SpringApplication { + + public static void main(String[] args) { + SpringApplication.run(AnapiV2Application.class, args); + } + +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..824ab89 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,36 @@ +server: + port: '@server.port@' + +management: + security: + flag: false + server: + port: '@management.port@' + metrics: + export: + statsd: + flavor: etsy + enabled: false + prometheus: + enabled: false + endpoint: + health: + show-details: always + metrics: + enabled: true + prometheus: + enabled: true + endpoints: + web: + exposure: + include: health,info,prometheus + +spring: + application: + name: '@project.name@' + output: + ansi: + enabled: always +info: + version: '@project.version@' + stage: dev diff --git a/src/test/java/com/rbkmoney/anapiv/AnapiV2ApplicationTest.java b/src/test/java/com/rbkmoney/anapiv/AnapiV2ApplicationTest.java new file mode 100644 index 0000000..2a7e3a9 --- /dev/null +++ b/src/test/java/com/rbkmoney/anapiv/AnapiV2ApplicationTest.java @@ -0,0 +1,16 @@ +package com.rbkmoney.anapiv; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = AnapiV2Application.class) +public class AnapiV2ApplicationTest { + + @Test + public void contextLoads() { + + } +} diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml new file mode 100644 index 0000000..e1f57ed --- /dev/null +++ b/src/test/resources/logback-test.xml @@ -0,0 +1,10 @@ + + + + + + + + + + From 9e404528ef703295b79562ba10db707b01034cde Mon Sep 17 00:00:00 2001 From: echerniak Date: Wed, 8 Sep 2021 14:06:41 +0300 Subject: [PATCH 02/37] WIP. Payments dirty implementation --- pom.xml | 83 ++++++- .../v2}/AnapiV2Application.java | 2 +- .../anapi/v2/config/ApplicationConfig.java | 24 ++ .../anapi/v2/controller/SearchController.java | 228 ++++++++++++++++++ .../anapi/v2/service/SearchService.java | 145 +++++++++++ src/main/resources/application.yml | 5 + .../v2}/AnapiV2ApplicationTest.java | 2 +- 7 files changed, 486 insertions(+), 3 deletions(-) rename src/main/java/com/rbkmoney/{anapiv => anapi/v2}/AnapiV2Application.java (92%) create mode 100644 src/main/java/com/rbkmoney/anapi/v2/config/ApplicationConfig.java create mode 100644 src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java create mode 100644 src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java rename src/test/java/com/rbkmoney/{anapiv => anapi/v2}/AnapiV2ApplicationTest.java (91%) diff --git a/pom.xml b/pom.xml index 2eddb12..af2fc43 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ jar anapi-v2 - + anapi v2 UTF-8 @@ -24,6 +24,12 @@ 8023 ${server.port} ${management.port} ${env.REGISTRY} + 2.9.2 + 2.5 + 2.12.5 + 2.5.3 + 1.3.2 + 2.3.1 @@ -45,6 +51,31 @@ com.rbkmoney shared-resources + + com.rbkmoney + payout-manager-proto + 1.17-8aa3eb8 + + + + com.rbkmoney + swag-anapi-v2 + + 1.0.0-SNAPSHOT + + + com.rbkmoney + magista-proto + 1.6-a3a5e93 + + + com.rbkmoney.geck + serializer + + + com.rbkmoney + damsel + @@ -76,11 +107,61 @@ javax.servlet javax.servlet-api + + io.swagger + swagger-annotations + 1.6.2 + provided + + + io.springfox + springfox-swagger2 + ${springfox-version} + + + io.springfox + springfox-swagger-ui + ${springfox-version} + org.projectlombok lombok provided + + javax.servlet + servlet-api + ${servlet-api-version} + provided + + + javax.annotation + javax.annotation-api + ${javax-annotation-api-version} + + + javax.validation + validation-api + 2.0.1.Final + provided + + + javax.xml.bind + jaxb-api + ${jaxb-version} + + + com.google.code.findbugs + jsr305 + 3.0.2 + provided + + + org.openapitools + jackson-databind-nullable + 0.2.1 + provided + diff --git a/src/main/java/com/rbkmoney/anapiv/AnapiV2Application.java b/src/main/java/com/rbkmoney/anapi/v2/AnapiV2Application.java similarity index 92% rename from src/main/java/com/rbkmoney/anapiv/AnapiV2Application.java rename to src/main/java/com/rbkmoney/anapi/v2/AnapiV2Application.java index 7f1be4f..f7cb15a 100644 --- a/src/main/java/com/rbkmoney/anapiv/AnapiV2Application.java +++ b/src/main/java/com/rbkmoney/anapi/v2/AnapiV2Application.java @@ -1,4 +1,4 @@ -package com.rbkmoney.anapiv; +package com.rbkmoney.anapi.v2; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; diff --git a/src/main/java/com/rbkmoney/anapi/v2/config/ApplicationConfig.java b/src/main/java/com/rbkmoney/anapi/v2/config/ApplicationConfig.java new file mode 100644 index 0000000..070e5a5 --- /dev/null +++ b/src/main/java/com/rbkmoney/anapi/v2/config/ApplicationConfig.java @@ -0,0 +1,24 @@ +package com.rbkmoney.anapi.v2.config; + +import com.rbkmoney.magista.MerchantStatisticsServiceSrv; +import com.rbkmoney.woody.thrift.impl.http.THSpawnClientBuilder; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.Resource; + +import java.io.IOException; + +@Configuration +public class ApplicationConfig { + + @Bean + public MerchantStatisticsServiceSrv.Iface magistaClient( + @Value("${service.magista.url}") Resource resource, + @Value("${service.magista.networkTimeout}") int networkTimeout + ) throws IOException { + return new THSpawnClientBuilder() + .withNetworkTimeout(networkTimeout) + .withAddress(resource.getURI()).build(MerchantStatisticsServiceSrv.Iface.class); + } +} diff --git a/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java b/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java new file mode 100644 index 0000000..d4e7c12 --- /dev/null +++ b/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java @@ -0,0 +1,228 @@ +package com.rbkmoney.anapi.v2.controller; + +import com.rbkmoney.anapi.v2.service.SearchService; +import com.rbkmoney.damsel.domain.*; +import com.rbkmoney.geck.common.util.TypeUtil; +import com.rbkmoney.magista.CommonSearchQueryParams; +import com.rbkmoney.magista.PaymentParams; +import com.rbkmoney.magista.PaymentSearchQuery; +import com.rbkmoney.openapi.anapi_v2.api.*; +import com.rbkmoney.openapi.anapi_v2.model.*; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.context.request.NativeWebRequest; + +import javax.validation.Valid; +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Optional; + +@Controller +@RequiredArgsConstructor +public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesApi, PayoutsApi, RefundsApi { + + private final SearchService searchService; + + @Override + public Optional getRequest() { + return PaymentsApi.super.getRequest(); + } + + @GetMapping( + value = "/payments", + produces = {"application/json; charset=utf-8"} + ) + @Override + public ResponseEntity searchPayments(String xRequestID, + String partyID, + OffsetDateTime fromTime, + OffsetDateTime toTime, + Integer limit, + String xRequestDeadline, + String shopID, + List shopIDs, + String paymentInstitutionRealm, + List invoiceIDs, + String paymentStatus, + String paymentFlow, + String paymentMethod, + String paymentTerminalProvider, + String invoiceID, + String paymentID, + String externalID, + String payerEmail, + String payerIP, + String payerFingerprint, + String customerID, + String first6, + String last4, + String rrn, + String approvalCode, + BankCardTokenProvider bankCardTokenProvider, + BankCardPaymentSystem bankCardPaymentSystem, + Long paymentAmountFrom, + Long paymentAmountTo, + List excludedShops, + String continuationToken) { + PaymentSearchQuery query = new PaymentSearchQuery() + .setCommonSearchQueryParams( + new CommonSearchQueryParams() + .setContinuationToken(continuationToken) + .setFromTime(TypeUtil.temporalToString(fromTime.toLocalDateTime())) + .setToTime(TypeUtil.temporalToString(toTime.toLocalDateTime())) + .setLimit(limit) + .setPartyId(partyID) + .setShopIds(shopIDs) + ) + .setPaymentParams( + new PaymentParams() + .setPaymentAmountFrom(paymentAmountFrom) + .setPaymentAmountTo(paymentAmountTo) + .setPaymentEmail(payerEmail) + .setPaymentApprovalCode(approvalCode) + .setPaymentCustomerId(customerID) + .setPaymentFingerprint(payerFingerprint) + .setPaymentFirst6(first6) + .setPaymentLast4(last4) + .setPaymentId(paymentID) + .setPaymentIp(payerIP) + .setPaymentRrn(rrn) + .setPaymentStatus(getStatus(paymentStatus)) + .setPaymentSystem(LegacyBankCardPaymentSystem.valueOf(bankCardPaymentSystem.getValue()) + ) + ) + .setExcludedShopIds(excludedShops) + .setExternalId(externalID) + .setInvoiceIds(invoiceIDs); + return ResponseEntity.ok(searchService.search(query)); + } + + private com.rbkmoney.damsel.domain.InvoicePaymentStatus getStatus(String paymentStatus) { + var status = Enum.valueOf(PaymentStatus.StatusEnum.class, paymentStatus); + var invoicePaymentStatus = new com.rbkmoney.damsel.domain.InvoicePaymentStatus(); + switch (status) { + case PENDING -> invoicePaymentStatus.setPending(new InvoicePaymentPending()); + case PROCESSED -> invoicePaymentStatus.setProcessed(new InvoicePaymentProcessed()); + case CAPTURED -> invoicePaymentStatus.setCaptured(new InvoicePaymentCaptured()); + case CANCELLED -> invoicePaymentStatus.setCancelled(new InvoicePaymentCancelled()); + case REFUNDED -> invoicePaymentStatus.setRefunded(new InvoicePaymentRefunded()); + case FAILED -> invoicePaymentStatus.setFailed(new InvoicePaymentFailed()); + } + return invoicePaymentStatus; + } + + @GetMapping( + value = "/chargebacks", + produces = {"application/json; charset=utf-8"} + ) + @Override + public ResponseEntity searchChargebacks(String xRequestID, + @NotNull @Size(min = 1, max = 40) @Valid String partyID, + @NotNull @Valid OffsetDateTime fromTime, + @NotNull @Valid OffsetDateTime toTime, + @NotNull @Min(1L) @Max(1000L) @Valid Integer limit, + String xRequestDeadline, + @Size(min = 1, max = 40) @Valid String shopID, + @Valid List shopIDs, + @Valid String paymentInstitutionRealm, + @Min(0L) @Valid Integer offset, + @Size(min = 1, max = 40) @Valid String invoiceID, + @Size(min = 1, max = 40) @Valid String paymentID, + @Size(min = 1, max = 40) @Valid String chargebackID, + @Valid List chargebackStatuses, + @Valid List chargebackStages, + @Valid List chargebackCategories, + @Valid String continuationToken) { + return ChargebacksApi.super + .searchChargebacks(xRequestID, partyID, fromTime, toTime, limit, xRequestDeadline, shopID, shopIDs, + paymentInstitutionRealm, offset, invoiceID, paymentID, chargebackID, chargebackStatuses, + chargebackStages, chargebackCategories, continuationToken); + } + + @GetMapping( + value = "/invoices", + produces = {"application/json; charset=utf-8"} + ) + @Override + public ResponseEntity searchInvoices(String xRequestID, + @NotNull @Size(min = 1, max = 40) @Valid String partyID, + @NotNull @Valid OffsetDateTime fromTime, + @NotNull @Valid OffsetDateTime toTime, + @NotNull @Min(1L) @Max(1000L) @Valid Integer limit, + String xRequestDeadline, + @Size(min = 1, max = 40) @Valid String shopID, + @Valid List shopIDs, + @Valid String paymentInstitutionRealm, + @Valid List invoiceIDs, + @Valid String invoiceStatus, + @Size(min = 1, max = 40) @Valid String invoiceID, + @Size(min = 1, max = 40) @Valid String externalID, + @Min(1L) @Valid Long invoiceAmountFrom, + @Min(1L) @Valid Long invoiceAmountTo, + @Valid List excludedShops, + @Valid String continuationToken) { + return InvoicesApi.super + .searchInvoices(xRequestID, partyID, fromTime, toTime, limit, xRequestDeadline, shopID, shopIDs, + paymentInstitutionRealm, invoiceIDs, invoiceStatus, invoiceID, externalID, invoiceAmountFrom, + invoiceAmountTo, excludedShops, continuationToken); + } + + @GetMapping( + value = "/payouts", + produces = {"application/json; charset=utf-8"} + ) + @Override + public ResponseEntity searchPayouts(String xRequestID, + @NotNull @Size(min = 1, max = 40) @Valid String partyID, + @NotNull @Valid OffsetDateTime fromTime, + @NotNull @Valid OffsetDateTime toTime, + @NotNull @Min(1L) @Max(1000L) @Valid Integer limit, + String xRequestDeadline, + @Size(min = 1, max = 40) @Valid String shopID, + @Valid List shopIDs, + @Valid String paymentInstitutionRealm, + @Min(0L) @Valid Integer offset, + @Size(min = 1, max = 40) @Valid String payoutID, + @Valid String payoutToolType, + @Valid List excludedShops, + @Valid String continuationToken) { + return PayoutsApi.super + .searchPayouts(xRequestID, partyID, fromTime, toTime, limit, xRequestDeadline, shopID, shopIDs, + paymentInstitutionRealm, offset, payoutID, payoutToolType, excludedShops, continuationToken); + } + + @GetMapping( + value = "/refunds", + produces = {"application/json; charset=utf-8"} + ) + @Override + public ResponseEntity searchRefunds(String xRequestID, + @NotNull @Size(min = 1, max = 40) @Valid String partyID, + @NotNull @Valid OffsetDateTime fromTime, + @NotNull @Valid OffsetDateTime toTime, + @NotNull @Min(1L) @Max(1000L) @Valid Integer limit, + String xRequestDeadline, + @Size(min = 1, max = 40) @Valid String shopID, + @Valid List shopIDs, + @Valid String paymentInstitutionRealm, + @Min(0L) @Valid Integer offset, + @Valid List invoiceIDs, + @Size(min = 1, max = 40) @Valid String invoiceID, + @Size(min = 1, max = 40) @Valid String paymentID, + @Size(min = 1, max = 40) @Valid String refundID, + @Size(min = 1, max = 40) @Valid String externalID, + @Valid String refundStatus, + @Valid List excludedShops, + @Valid String continuationToken) { + return RefundsApi.super + .searchRefunds(xRequestID, partyID, fromTime, toTime, limit, xRequestDeadline, shopID, shopIDs, + paymentInstitutionRealm, offset, invoiceIDs, invoiceID, paymentID, refundID, externalID, + refundStatus, excludedShops, continuationToken); + } +} diff --git a/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java b/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java new file mode 100644 index 0000000..fab9ae0 --- /dev/null +++ b/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java @@ -0,0 +1,145 @@ +package com.rbkmoney.anapi.v2.service; + +import com.rbkmoney.geck.common.util.TypeUtil; +import com.rbkmoney.magista.*; +import com.rbkmoney.openapi.anapi_v2.model.Payer; +import com.rbkmoney.openapi.anapi_v2.model.*; +import lombok.RequiredArgsConstructor; +import org.apache.thrift.TException; +import org.springframework.stereotype.Service; + +import java.time.ZoneOffset; +import java.util.ArrayList; +import java.util.List; + +@Service +@RequiredArgsConstructor +public class SearchService { + + private final MerchantStatisticsServiceSrv.Iface magistaClient; + + public InlineResponse20010 search(PaymentSearchQuery searchQuery) { + try { + StatPaymentResponse magistaResponse = magistaClient.searchPayments(searchQuery); + List results = new ArrayList<>(magistaResponse.getPaymentsSize()); + for (StatPayment payment : magistaResponse.getPayments()) { + + PaymentSearchResult result = new PaymentSearchResult() + .amount(payment.getAmount()) + .createdAt(TypeUtil.stringToInstant(payment.getCreatedAt()).atOffset(ZoneOffset.UTC)) + .currency(payment.getCurrencySymbolicCode()) + .externalID(payment.getExternalId()) + .fee(payment.getFee()) + .flow(new PaymentFlow() + .type(payment.getFlow().isSetHold() ? PaymentFlow.TypeEnum.PAYMENTFLOWHOLD : + PaymentFlow.TypeEnum.PAYMENTFLOWINSTANT)) + .geoLocationInfo(new GeoLocationInfo() + .cityGeoID(payment.getLocationInfo().getCityGeoId()) + .countryGeoID(payment.getLocationInfo().getCountryGeoId())) + .id(payment.getId()) + .invoiceID(payment.getInvoiceId()) + .makeRecurrent(payment.isMakeRecurrent()) + .payer(getPayer(payment)) + .shopID(payment.getShopId()) + .shortID(payment.getShortId()) + .status(getStatus(payment.getStatus())) + .statusChangedAt(TypeUtil.stringToInstant(getAt(payment.getStatus())) + .atOffset(ZoneOffset.UTC)) + .transactionInfo(new TransactionInfo() + .approvalCode(payment.getAdditionalTransactionInfo().getApprovalCode()) + .rrn(payment.getAdditionalTransactionInfo().getRrn()) + ); + results.add(result); + } + + return new InlineResponse20010() + .result(results) + .continuationToken(magistaResponse.getContinuationToken()); + } catch (TException e) { + e.printStackTrace(); + } + //TODO: Error processing; + return null; + } + + private Payer getPayer(StatPayment payment) { + var statPayer = payment.getPayer(); + Payer payer = new Payer(); + + if (statPayer.isSetCustomer()) { + return payer.payerType(Payer.PayerTypeEnum.CUSTOMERPAYER); + } + + if (statPayer.isSetPaymentResource()) { + return payer.payerType(Payer.PayerTypeEnum.PAYMENTRESOURCEPAYER); + } + + if (statPayer.isSetRecurrent()) { + return payer.payerType(Payer.PayerTypeEnum.RECURRENTPAYER); + } + + return null; + } + + private PaymentSearchResult.StatusEnum getStatus(InvoicePaymentStatus status) { + if (status.isSetCancelled()) { + return PaymentSearchResult.StatusEnum.CANCELLED; + } + + if (status.isSetCaptured()) { + return PaymentSearchResult.StatusEnum.CAPTURED; + } + + if (status.isSetChargedBack()) { + //TODO: Clearify + } + + if (status.isSetFailed()) { + return PaymentSearchResult.StatusEnum.PROCESSED; + } + + if (status.isSetPending()) { + return PaymentSearchResult.StatusEnum.PENDING; + } + + if (status.isSetProcessed()) { + return PaymentSearchResult.StatusEnum.PROCESSED; + } + + if (status.isSetRefunded()) { + return PaymentSearchResult.StatusEnum.REFUNDED; + } + + throw new IllegalArgumentException(""); + + } + + private String getAt(InvoicePaymentStatus status) { + if (status.isSetCancelled()) { + return status.getCancelled().getAt(); + } + + if (status.isSetCaptured()) { + return status.getCaptured().getAt(); + } + + if (status.isSetChargedBack()) { + //TODO: Clearify + } + + if (status.isSetFailed()) { + return status.getFailed().getAt(); + } + + if (status.isSetProcessed()) { + return status.getProcessed().getAt(); + } + + if (status.isSetRefunded()) { + return status.getRefunded().getAt(); + } + + return null; + + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 824ab89..ed3e8da 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -25,6 +25,11 @@ management: exposure: include: health,info,prometheus +service: + magista: + url: http://magista:8022/stat + networkTimeout: 5000 + spring: application: name: '@project.name@' diff --git a/src/test/java/com/rbkmoney/anapiv/AnapiV2ApplicationTest.java b/src/test/java/com/rbkmoney/anapi/v2/AnapiV2ApplicationTest.java similarity index 91% rename from src/test/java/com/rbkmoney/anapiv/AnapiV2ApplicationTest.java rename to src/test/java/com/rbkmoney/anapi/v2/AnapiV2ApplicationTest.java index 2a7e3a9..959553b 100644 --- a/src/test/java/com/rbkmoney/anapiv/AnapiV2ApplicationTest.java +++ b/src/test/java/com/rbkmoney/anapi/v2/AnapiV2ApplicationTest.java @@ -1,4 +1,4 @@ -package com.rbkmoney.anapiv; +package com.rbkmoney.anapi.v2; import org.junit.Test; import org.junit.runner.RunWith; From 1f3b3de468dc92a73accfaab4dfe58ff70b63486 Mon Sep 17 00:00:00 2001 From: echerniak Date: Wed, 8 Sep 2021 20:26:37 +0300 Subject: [PATCH 03/37] WIP.Mapping for payments params updated --- .../anapi/v2/controller/SearchController.java | 229 +++++++++++++----- .../anapi/v2/service/SearchService.java | 18 +- 2 files changed, 186 insertions(+), 61 deletions(-) diff --git a/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java b/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java index d4e7c12..37e05ff 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java +++ b/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java @@ -1,11 +1,21 @@ package com.rbkmoney.anapi.v2.controller; import com.rbkmoney.anapi.v2.service.SearchService; +import com.rbkmoney.damsel.domain.BankCard; +import com.rbkmoney.damsel.domain.InvoicePaymentCancelled; +import com.rbkmoney.damsel.domain.InvoicePaymentCaptured; +import com.rbkmoney.damsel.domain.InvoicePaymentFailed; +import com.rbkmoney.damsel.domain.InvoicePaymentFlow; +import com.rbkmoney.damsel.domain.InvoicePaymentFlowHold; +import com.rbkmoney.damsel.domain.InvoicePaymentFlowInstant; +import com.rbkmoney.damsel.domain.InvoicePaymentPending; +import com.rbkmoney.damsel.domain.InvoicePaymentProcessed; +import com.rbkmoney.damsel.domain.InvoicePaymentRefunded; +import com.rbkmoney.damsel.domain.PaymentTerminal; +import com.rbkmoney.damsel.domain.PaymentTool; import com.rbkmoney.damsel.domain.*; import com.rbkmoney.geck.common.util.TypeUtil; -import com.rbkmoney.magista.CommonSearchQueryParams; -import com.rbkmoney.magista.PaymentParams; -import com.rbkmoney.magista.PaymentSearchQuery; +import com.rbkmoney.magista.*; import com.rbkmoney.openapi.anapi_v2.api.*; import com.rbkmoney.openapi.anapi_v2.model.*; import lombok.RequiredArgsConstructor; @@ -14,14 +24,14 @@ import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.context.request.NativeWebRequest; +import javax.annotation.Nullable; import javax.validation.Valid; -import javax.validation.constraints.Max; -import javax.validation.constraints.Min; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; +import javax.validation.constraints.*; import java.time.OffsetDateTime; +import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; @Controller @RequiredArgsConstructor @@ -34,54 +44,49 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp return PaymentsApi.super.getRequest(); } - @GetMapping( - value = "/payments", - produces = {"application/json; charset=utf-8"} - ) @Override public ResponseEntity searchPayments(String xRequestID, - String partyID, - OffsetDateTime fromTime, - OffsetDateTime toTime, - Integer limit, + @NotNull @Size(min = 1, max = 40) @Valid String partyID, + @NotNull @Valid OffsetDateTime fromTime, + @NotNull @Valid OffsetDateTime toTime, + @NotNull @Min(1L) @Max(1000L) @Valid Integer limit, String xRequestDeadline, - String shopID, - List shopIDs, - String paymentInstitutionRealm, - List invoiceIDs, - String paymentStatus, - String paymentFlow, - String paymentMethod, - String paymentTerminalProvider, - String invoiceID, - String paymentID, - String externalID, - String payerEmail, - String payerIP, - String payerFingerprint, - String customerID, - String first6, - String last4, - String rrn, - String approvalCode, - BankCardTokenProvider bankCardTokenProvider, - BankCardPaymentSystem bankCardPaymentSystem, - Long paymentAmountFrom, - Long paymentAmountTo, - List excludedShops, - String continuationToken) { + @Size(min = 1, max = 40) @Valid String shopID, + @Valid List shopIDs, + @Valid String paymentInstitutionRealm, + @Valid List invoiceIDs, + @Valid String paymentStatus, @Valid String paymentFlow, + @Valid String paymentMethod, + @Valid String paymentTerminalProvider, + @Size(min = 1, max = 40) @Valid String invoiceID, + @Size(min = 1, max = 40) @Valid String paymentID, + @Size(min = 1, max = 40) @Valid String externalID, + @Size(max = 100) @Email @Valid String payerEmail, + @Size(max = 45) @Valid String payerIP, + @Size(max = 1000) @Valid String payerFingerprint, + @Size(min = 1, max = 40) @Valid String customerID, + @Pattern(regexp = "^\\d{6}$") @Valid String first6, + @Pattern(regexp = "^\\d{4}$") @Valid String last4, + @Pattern(regexp = "^[a-zA-Z0-9]{12}$") @Valid String rrn, + @Size(min = 1, max = 40) @Valid String approvalCode, + @Valid BankCardTokenProvider bankCardTokenProvider, + @Valid BankCardPaymentSystem bankCardPaymentSystem, + @Min(1L) @Valid Long paymentAmountFrom, + @Min(1L) @Valid Long paymentAmountTo, + @Valid List excludedShops, + @Valid String continuationToken) { + //TODO: clearify mapping for paymentInstitutionRealm, xRequestID, xRequestDeadline PaymentSearchQuery query = new PaymentSearchQuery() .setCommonSearchQueryParams( - new CommonSearchQueryParams() - .setContinuationToken(continuationToken) - .setFromTime(TypeUtil.temporalToString(fromTime.toLocalDateTime())) - .setToTime(TypeUtil.temporalToString(toTime.toLocalDateTime())) - .setLimit(limit) - .setPartyId(partyID) - .setShopIds(shopIDs) - ) + fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), continuationToken)) .setPaymentParams( new PaymentParams() + .setPaymentTool(mapToPaymentTool(paymentMethod)) + .setPaymentFlow(mapToInvoicePaymentFlow(paymentFlow)) + .setPaymentTerminalProvider( + LegacyTerminalPaymentProvider.valueOf(paymentTerminalProvider)) + .setPaymentTokenProvider( + LegacyBankCardTokenProvider.valueOf(bankCardTokenProvider.getValue())) .setPaymentAmountFrom(paymentAmountFrom) .setPaymentAmountTo(paymentAmountTo) .setPaymentEmail(payerEmail) @@ -94,13 +99,54 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp .setPaymentIp(payerIP) .setPaymentRrn(rrn) .setPaymentStatus(getStatus(paymentStatus)) - .setPaymentSystem(LegacyBankCardPaymentSystem.valueOf(bankCardPaymentSystem.getValue()) - ) + .setPaymentSystem(LegacyBankCardPaymentSystem.valueOf(bankCardPaymentSystem.getValue())) ) .setExcludedShopIds(excludedShops) .setExternalId(externalID) - .setInvoiceIds(invoiceIDs); - return ResponseEntity.ok(searchService.search(query)); + .setInvoiceIds(merge(invoiceID, invoiceIDs)); + return ResponseEntity.ok(searchService.findPayments(query)); + } + + private PaymentTool mapToPaymentTool(String paymentMethod) { + var paymentTool = new PaymentTool(); + switch (paymentMethod) { + case "bankCard" -> paymentTool.setBankCard(new BankCard()); + case "paymentTerminal" -> paymentTool.setPaymentTerminal(new PaymentTerminal()); + default -> throw new IllegalArgumentException(""); + } + + return paymentTool; + } + + private List merge(@Nullable String id, @Nullable List ids) { + if (id != null) { + if (ids == null) { + ids = new ArrayList<>(); + } + ids.add(id); + } + return ids; + } + + private InvoicePaymentFlow mapToInvoicePaymentFlow(String paymentFlow) { + var invoicePaymentFlow = new InvoicePaymentFlow(); + switch (paymentFlow) { + case "instant" -> invoicePaymentFlow.setInstant(new InvoicePaymentFlowInstant()); + case "hold" -> invoicePaymentFlow.setHold(new InvoicePaymentFlowHold()); + default -> throw new IllegalArgumentException(""); + } + return invoicePaymentFlow; + } + + private CommonSearchQueryParams fillCommonParams(OffsetDateTime fromTime, OffsetDateTime toTime, Integer limit, + String partyId, List shopIDs, String continuationToken) { + return new CommonSearchQueryParams() + .setContinuationToken(continuationToken) + .setFromTime(TypeUtil.temporalToString(fromTime.toLocalDateTime())) + .setToTime(TypeUtil.temporalToString(toTime.toLocalDateTime())) + .setLimit(limit) + .setPartyId(partyId) + .setShopIds(shopIDs); } private com.rbkmoney.damsel.domain.InvoicePaymentStatus getStatus(String paymentStatus) { @@ -139,10 +185,63 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Valid List chargebackStages, @Valid List chargebackCategories, @Valid String continuationToken) { - return ChargebacksApi.super - .searchChargebacks(xRequestID, partyID, fromTime, toTime, limit, xRequestDeadline, shopID, shopIDs, - paymentInstitutionRealm, offset, invoiceID, paymentID, chargebackID, chargebackStatuses, - chargebackStages, chargebackCategories, continuationToken); + ChargebackSearchQuery query = new ChargebackSearchQuery() + .setCommonSearchQueryParams( + fillCommonParams(fromTime, toTime, limit, partyID, shopIDs, continuationToken)) + .setPaymentId(paymentID) + .setChargebackId(chargebackID) + .setChargebackCategories(chargebackCategories.stream() + .map(this::mapToDamselCategory) + .collect(Collectors.toList())) + .setChargebackStatuses(chargebackStatuses.stream() + .map(this::mapToDamselStatus) + .collect(Collectors.toList()) + ) + .setChargebackStages(chargebackStages.stream() + .map(this::mapToDamselStage) + .collect(Collectors.toList()) + ); + return ResponseEntity.ok(searchService.findChargebacks(query)); + } + + private InvoicePaymentChargebackStage mapToDamselStage(String stage) { + var damselStage = new InvoicePaymentChargebackStage(); + switch (stage) { + case "chargeback" -> damselStage.setChargeback(new InvoicePaymentChargebackStageChargeback()); + case "pre_arbitration" -> damselStage.setPreArbitration(new InvoicePaymentChargebackStagePreArbitration()); + case "arbitration" -> damselStage.setArbitration(new InvoicePaymentChargebackStageArbitration()); + default -> throw new IllegalArgumentException(""); + } + + return damselStage; + } + + private InvoicePaymentChargebackStatus mapToDamselStatus(String status) { + var damselStatus = new InvoicePaymentChargebackStatus(); + switch (status) { + case "pending" -> damselStatus.setPending(new InvoicePaymentChargebackPending()); + case "accepted" -> damselStatus.setAccepted(new InvoicePaymentChargebackAccepted()); + case "rejected" -> damselStatus.setRejected(new InvoicePaymentChargebackRejected()); + case "cancelled" -> damselStatus.setCancelled(new InvoicePaymentChargebackCancelled()); + default -> throw new IllegalArgumentException(""); + } + + return damselStatus; + } + + private InvoicePaymentChargebackCategory mapToDamselCategory(String category) { + var damselCategory = new InvoicePaymentChargebackCategory(); + switch (category) { + case "fraud" -> damselCategory.setFraud(new InvoicePaymentChargebackCategoryFraud()); + case "dispute" -> damselCategory.setDispute(new InvoicePaymentChargebackCategoryDispute()); + case "authorisation" -> damselCategory + .setAuthorisation(new InvoicePaymentChargebackCategoryAuthorisation()); + case "processing_error" -> damselCategory + .setProcessingError(new InvoicePaymentChargebackCategoryProcessingError()); + default -> throw new IllegalArgumentException(""); + } + + return damselCategory; } @GetMapping( @@ -167,10 +266,20 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Min(1L) @Valid Long invoiceAmountTo, @Valid List excludedShops, @Valid String continuationToken) { - return InvoicesApi.super - .searchInvoices(xRequestID, partyID, fromTime, toTime, limit, xRequestDeadline, shopID, shopIDs, - paymentInstitutionRealm, invoiceIDs, invoiceStatus, invoiceID, externalID, invoiceAmountFrom, - invoiceAmountTo, excludedShops, continuationToken); + InvoiceSearchQuery query = new InvoiceSearchQuery() + .setCommonSearchQueryParams( + fillCommonParams(fromTime, toTime, limit, partyID, shopIDs, continuationToken)) + .setPaymentParams( + new PaymentParams() + .setPaymentAmountFrom(invoiceAmountFrom) + .setPaymentAmountTo(invoiceAmountTo) + .setPaymentId(invoiceID) + .setPaymentStatus(getStatus(invoiceStatus)) + + ) + .setInvoiceIds(invoiceIDs) + .setExternalId(externalID); + return ResponseEntity.ok(searchService.findInvoices(query)); } @GetMapping( diff --git a/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java b/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java index fab9ae0..5d3cee7 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java +++ b/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java @@ -18,7 +18,7 @@ public class SearchService { private final MerchantStatisticsServiceSrv.Iface magistaClient; - public InlineResponse20010 search(PaymentSearchQuery searchQuery) { + public InlineResponse20010 findPayments(PaymentSearchQuery searchQuery) { try { StatPaymentResponse magistaResponse = magistaClient.searchPayments(searchQuery); List results = new ArrayList<>(magistaResponse.getPaymentsSize()); @@ -62,6 +62,22 @@ public class SearchService { return null; } + public InlineResponse2008 findChargebacks(ChargebackSearchQuery query) { + return null; + } + + public InlineResponse2009 findInvoices(InvoiceSearchQuery query) { + return null; + } + + public InlineResponse20011 findPayouts(PayoutSearchQuery query) { + return null; + } + + public InlineResponse20012 findRefunds(RefundSearchQuery query) { + return null; + } + private Payer getPayer(StatPayment payment) { var statPayer = payment.getPayer(); Payer payer = new Payer(); From c87a13b1e07163ebea6f3e48f22bf2876a046b86 Mon Sep 17 00:00:00 2001 From: echerniak Date: Thu, 9 Sep 2021 14:37:16 +0300 Subject: [PATCH 04/37] WIP. Controller impl --- .../anapi/v2/controller/SearchController.java | 168 ++++-------------- .../anapi/v2/service/SearchService.java | 2 - .../rbkmoney/anapi/v2/util/CommonUtil.java | 22 +++ .../rbkmoney/anapi/v2/util/DamselUtil.java | 130 ++++++++++++++ 4 files changed, 190 insertions(+), 132 deletions(-) create mode 100644 src/main/java/com/rbkmoney/anapi/v2/util/CommonUtil.java create mode 100644 src/main/java/com/rbkmoney/anapi/v2/util/DamselUtil.java diff --git a/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java b/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java index 37e05ff..2e883a9 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java +++ b/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java @@ -1,20 +1,10 @@ package com.rbkmoney.anapi.v2.controller; import com.rbkmoney.anapi.v2.service.SearchService; -import com.rbkmoney.damsel.domain.BankCard; -import com.rbkmoney.damsel.domain.InvoicePaymentCancelled; -import com.rbkmoney.damsel.domain.InvoicePaymentCaptured; -import com.rbkmoney.damsel.domain.InvoicePaymentFailed; -import com.rbkmoney.damsel.domain.InvoicePaymentFlow; -import com.rbkmoney.damsel.domain.InvoicePaymentFlowHold; -import com.rbkmoney.damsel.domain.InvoicePaymentFlowInstant; -import com.rbkmoney.damsel.domain.InvoicePaymentPending; -import com.rbkmoney.damsel.domain.InvoicePaymentProcessed; -import com.rbkmoney.damsel.domain.InvoicePaymentRefunded; -import com.rbkmoney.damsel.domain.PaymentTerminal; -import com.rbkmoney.damsel.domain.PaymentTool; -import com.rbkmoney.damsel.domain.*; -import com.rbkmoney.geck.common.util.TypeUtil; +import com.rbkmoney.anapi.v2.util.DamselUtil; +import com.rbkmoney.damsel.domain.LegacyBankCardPaymentSystem; +import com.rbkmoney.damsel.domain.LegacyBankCardTokenProvider; +import com.rbkmoney.damsel.domain.LegacyTerminalPaymentProvider; import com.rbkmoney.magista.*; import com.rbkmoney.openapi.anapi_v2.api.*; import com.rbkmoney.openapi.anapi_v2.model.*; @@ -24,15 +14,16 @@ import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.context.request.NativeWebRequest; -import javax.annotation.Nullable; import javax.validation.Valid; import javax.validation.constraints.*; import java.time.OffsetDateTime; -import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; +import static com.rbkmoney.anapi.v2.util.CommonUtil.merge; +import static com.rbkmoney.anapi.v2.util.DamselUtil.*; + @Controller @RequiredArgsConstructor public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesApi, PayoutsApi, RefundsApi { @@ -75,7 +66,7 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Min(1L) @Valid Long paymentAmountTo, @Valid List excludedShops, @Valid String continuationToken) { - //TODO: clearify mapping for paymentInstitutionRealm, xRequestID, xRequestDeadline + //TODO: clarify mapping for paymentInstitutionRealm, xRequestID, xRequestDeadline PaymentSearchQuery query = new PaymentSearchQuery() .setCommonSearchQueryParams( fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), continuationToken)) @@ -107,62 +98,6 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp return ResponseEntity.ok(searchService.findPayments(query)); } - private PaymentTool mapToPaymentTool(String paymentMethod) { - var paymentTool = new PaymentTool(); - switch (paymentMethod) { - case "bankCard" -> paymentTool.setBankCard(new BankCard()); - case "paymentTerminal" -> paymentTool.setPaymentTerminal(new PaymentTerminal()); - default -> throw new IllegalArgumentException(""); - } - - return paymentTool; - } - - private List merge(@Nullable String id, @Nullable List ids) { - if (id != null) { - if (ids == null) { - ids = new ArrayList<>(); - } - ids.add(id); - } - return ids; - } - - private InvoicePaymentFlow mapToInvoicePaymentFlow(String paymentFlow) { - var invoicePaymentFlow = new InvoicePaymentFlow(); - switch (paymentFlow) { - case "instant" -> invoicePaymentFlow.setInstant(new InvoicePaymentFlowInstant()); - case "hold" -> invoicePaymentFlow.setHold(new InvoicePaymentFlowHold()); - default -> throw new IllegalArgumentException(""); - } - return invoicePaymentFlow; - } - - private CommonSearchQueryParams fillCommonParams(OffsetDateTime fromTime, OffsetDateTime toTime, Integer limit, - String partyId, List shopIDs, String continuationToken) { - return new CommonSearchQueryParams() - .setContinuationToken(continuationToken) - .setFromTime(TypeUtil.temporalToString(fromTime.toLocalDateTime())) - .setToTime(TypeUtil.temporalToString(toTime.toLocalDateTime())) - .setLimit(limit) - .setPartyId(partyId) - .setShopIds(shopIDs); - } - - private com.rbkmoney.damsel.domain.InvoicePaymentStatus getStatus(String paymentStatus) { - var status = Enum.valueOf(PaymentStatus.StatusEnum.class, paymentStatus); - var invoicePaymentStatus = new com.rbkmoney.damsel.domain.InvoicePaymentStatus(); - switch (status) { - case PENDING -> invoicePaymentStatus.setPending(new InvoicePaymentPending()); - case PROCESSED -> invoicePaymentStatus.setProcessed(new InvoicePaymentProcessed()); - case CAPTURED -> invoicePaymentStatus.setCaptured(new InvoicePaymentCaptured()); - case CANCELLED -> invoicePaymentStatus.setCancelled(new InvoicePaymentCancelled()); - case REFUNDED -> invoicePaymentStatus.setRefunded(new InvoicePaymentRefunded()); - case FAILED -> invoicePaymentStatus.setFailed(new InvoicePaymentFailed()); - } - return invoicePaymentStatus; - } - @GetMapping( value = "/chargebacks", produces = {"application/json; charset=utf-8"} @@ -185,65 +120,27 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Valid List chargebackStages, @Valid List chargebackCategories, @Valid String continuationToken) { + //TODO: clarify mapping for paymentInstitutionRealm, xRequestID, xRequestDeadline, offset ChargebackSearchQuery query = new ChargebackSearchQuery() .setCommonSearchQueryParams( - fillCommonParams(fromTime, toTime, limit, partyID, shopIDs, continuationToken)) + fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), continuationToken)) + .setInvoiceIds(List.of(invoiceID)) .setPaymentId(paymentID) .setChargebackId(chargebackID) - .setChargebackCategories(chargebackCategories.stream() - .map(this::mapToDamselCategory) - .collect(Collectors.toList())) .setChargebackStatuses(chargebackStatuses.stream() - .map(this::mapToDamselStatus) + .map(DamselUtil::mapToDamselStatus) .collect(Collectors.toList()) ) .setChargebackStages(chargebackStages.stream() - .map(this::mapToDamselStage) + .map(DamselUtil::mapToDamselStage) .collect(Collectors.toList()) - ); + ) + .setChargebackCategories(chargebackCategories.stream() + .map(DamselUtil::mapToDamselCategory) + .collect(Collectors.toList())); return ResponseEntity.ok(searchService.findChargebacks(query)); } - private InvoicePaymentChargebackStage mapToDamselStage(String stage) { - var damselStage = new InvoicePaymentChargebackStage(); - switch (stage) { - case "chargeback" -> damselStage.setChargeback(new InvoicePaymentChargebackStageChargeback()); - case "pre_arbitration" -> damselStage.setPreArbitration(new InvoicePaymentChargebackStagePreArbitration()); - case "arbitration" -> damselStage.setArbitration(new InvoicePaymentChargebackStageArbitration()); - default -> throw new IllegalArgumentException(""); - } - - return damselStage; - } - - private InvoicePaymentChargebackStatus mapToDamselStatus(String status) { - var damselStatus = new InvoicePaymentChargebackStatus(); - switch (status) { - case "pending" -> damselStatus.setPending(new InvoicePaymentChargebackPending()); - case "accepted" -> damselStatus.setAccepted(new InvoicePaymentChargebackAccepted()); - case "rejected" -> damselStatus.setRejected(new InvoicePaymentChargebackRejected()); - case "cancelled" -> damselStatus.setCancelled(new InvoicePaymentChargebackCancelled()); - default -> throw new IllegalArgumentException(""); - } - - return damselStatus; - } - - private InvoicePaymentChargebackCategory mapToDamselCategory(String category) { - var damselCategory = new InvoicePaymentChargebackCategory(); - switch (category) { - case "fraud" -> damselCategory.setFraud(new InvoicePaymentChargebackCategoryFraud()); - case "dispute" -> damselCategory.setDispute(new InvoicePaymentChargebackCategoryDispute()); - case "authorisation" -> damselCategory - .setAuthorisation(new InvoicePaymentChargebackCategoryAuthorisation()); - case "processing_error" -> damselCategory - .setProcessingError(new InvoicePaymentChargebackCategoryProcessingError()); - default -> throw new IllegalArgumentException(""); - } - - return damselCategory; - } - @GetMapping( value = "/invoices", produces = {"application/json; charset=utf-8"} @@ -266,18 +163,18 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Min(1L) @Valid Long invoiceAmountTo, @Valid List excludedShops, @Valid String continuationToken) { + //TODO: clarify mapping for paymentInstitutionRealm, xRequestID, xRequestDeadline, excludedShops InvoiceSearchQuery query = new InvoiceSearchQuery() .setCommonSearchQueryParams( - fillCommonParams(fromTime, toTime, limit, partyID, shopIDs, continuationToken)) + fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), continuationToken)) .setPaymentParams( new PaymentParams() .setPaymentAmountFrom(invoiceAmountFrom) .setPaymentAmountTo(invoiceAmountTo) - .setPaymentId(invoiceID) .setPaymentStatus(getStatus(invoiceStatus)) ) - .setInvoiceIds(invoiceIDs) + .setInvoiceIds(merge(invoiceID, invoiceIDs)) .setExternalId(externalID); return ResponseEntity.ok(searchService.findInvoices(query)); } @@ -301,9 +198,14 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Valid String payoutToolType, @Valid List excludedShops, @Valid String continuationToken) { - return PayoutsApi.super - .searchPayouts(xRequestID, partyID, fromTime, toTime, limit, xRequestDeadline, shopID, shopIDs, - paymentInstitutionRealm, offset, payoutID, payoutToolType, excludedShops, continuationToken); + //TODO: clarify mapping for paymentInstitutionRealm, xRequestID, xRequestDeadline, excludedShops, + //offset + setStatuses + PayoutSearchQuery query = new PayoutSearchQuery() + .setCommonSearchQueryParams( + fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), continuationToken)) + .setPayoutId(payoutID) + .setPayoutType(mapToDamselPayoutToolInfo(payoutToolType)); + return ResponseEntity.ok(searchService.findPayouts(query)); } @GetMapping( @@ -329,9 +231,15 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Valid String refundStatus, @Valid List excludedShops, @Valid String continuationToken) { - return RefundsApi.super - .searchRefunds(xRequestID, partyID, fromTime, toTime, limit, xRequestDeadline, shopID, shopIDs, - paymentInstitutionRealm, offset, invoiceIDs, invoiceID, paymentID, refundID, externalID, - refundStatus, excludedShops, continuationToken); + //TODO: clarify mapping for paymentInstitutionRealm, xRequestID, xRequestDeadline, excludedShops, offset + RefundSearchQuery query = new RefundSearchQuery() + .setCommonSearchQueryParams( + fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), continuationToken)) + .setRefundStatus(getRefundStatus(refundStatus)) + .setInvoiceIds(merge(invoiceID, invoiceIDs)) + .setExternalId(externalID) + .setPaymentId(paymentID) + .setRefundId(refundID); + return ResponseEntity.ok(searchService.findRefunds(query)); } } diff --git a/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java b/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java index 5d3cee7..90cd3fa 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java +++ b/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java @@ -23,7 +23,6 @@ public class SearchService { StatPaymentResponse magistaResponse = magistaClient.searchPayments(searchQuery); List results = new ArrayList<>(magistaResponse.getPaymentsSize()); for (StatPayment payment : magistaResponse.getPayments()) { - PaymentSearchResult result = new PaymentSearchResult() .amount(payment.getAmount()) .createdAt(TypeUtil.stringToInstant(payment.getCreatedAt()).atOffset(ZoneOffset.UTC)) @@ -51,7 +50,6 @@ public class SearchService { ); results.add(result); } - return new InlineResponse20010() .result(results) .continuationToken(magistaResponse.getContinuationToken()); diff --git a/src/main/java/com/rbkmoney/anapi/v2/util/CommonUtil.java b/src/main/java/com/rbkmoney/anapi/v2/util/CommonUtil.java new file mode 100644 index 0000000..a4a9c3d --- /dev/null +++ b/src/main/java/com/rbkmoney/anapi/v2/util/CommonUtil.java @@ -0,0 +1,22 @@ +package com.rbkmoney.anapi.v2.util; + +import lombok.experimental.UtilityClass; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.List; + +@UtilityClass +public class CommonUtil { + + public static List merge(@Nullable String id, @Nullable List ids) { + if (id != null) { + if (ids == null) { + ids = new ArrayList<>(); + } + ids.add(id); + } + return ids; + } + +} diff --git a/src/main/java/com/rbkmoney/anapi/v2/util/DamselUtil.java b/src/main/java/com/rbkmoney/anapi/v2/util/DamselUtil.java new file mode 100644 index 0000000..bc8bc2d --- /dev/null +++ b/src/main/java/com/rbkmoney/anapi/v2/util/DamselUtil.java @@ -0,0 +1,130 @@ +package com.rbkmoney.anapi.v2.util; + +import com.rbkmoney.damsel.domain.*; +import com.rbkmoney.geck.common.util.TypeUtil; +import com.rbkmoney.magista.CommonSearchQueryParams; +import com.rbkmoney.openapi.anapi_v2.model.*; +import lombok.experimental.UtilityClass; + +import java.time.OffsetDateTime; +import java.util.List; + +@UtilityClass +public class DamselUtil { + + public static PaymentTool mapToPaymentTool(String paymentMethod) { + var paymentTool = new PaymentTool(); + switch (paymentMethod) { + case "bankCard" -> paymentTool.setBankCard(new BankCard()); + case "paymentTerminal" -> paymentTool.setPaymentTerminal(new PaymentTerminal()); + default -> throw new IllegalArgumentException(""); + } + + return paymentTool; + } + + public static InvoicePaymentFlow mapToInvoicePaymentFlow(String paymentFlow) { + var invoicePaymentFlow = new InvoicePaymentFlow(); + switch (paymentFlow) { + case "instant" -> invoicePaymentFlow.setInstant(new InvoicePaymentFlowInstant()); + case "hold" -> invoicePaymentFlow.setHold(new InvoicePaymentFlowHold()); + default -> throw new IllegalArgumentException(""); + } + return invoicePaymentFlow; + } + + public static CommonSearchQueryParams fillCommonParams(OffsetDateTime fromTime, OffsetDateTime toTime, + Integer limit, + String partyId, List shopIDs, + String continuationToken) { + return new CommonSearchQueryParams() + .setContinuationToken(continuationToken) + .setFromTime(TypeUtil.temporalToString(fromTime.toLocalDateTime())) + .setToTime(TypeUtil.temporalToString(toTime.toLocalDateTime())) + .setLimit(limit) + .setPartyId(partyId) + .setShopIds(shopIDs); + } + + public static com.rbkmoney.damsel.domain.InvoicePaymentStatus getStatus(String paymentStatus) { + var status = Enum.valueOf(PaymentStatus.StatusEnum.class, paymentStatus); + var invoicePaymentStatus = new com.rbkmoney.damsel.domain.InvoicePaymentStatus(); + switch (status) { + case PENDING -> invoicePaymentStatus.setPending(new InvoicePaymentPending()); + case PROCESSED -> invoicePaymentStatus.setProcessed(new InvoicePaymentProcessed()); + case CAPTURED -> invoicePaymentStatus.setCaptured(new InvoicePaymentCaptured()); + case CANCELLED -> invoicePaymentStatus.setCancelled(new InvoicePaymentCancelled()); + case REFUNDED -> invoicePaymentStatus.setRefunded(new InvoicePaymentRefunded()); + case FAILED -> invoicePaymentStatus.setFailed(new InvoicePaymentFailed()); + } + return invoicePaymentStatus; + } + + public static InvoicePaymentChargebackStage mapToDamselStage(String chargebackStage) { + var stage = Enum.valueOf(ChargebackStage.class, chargebackStage); + var damselStage = new InvoicePaymentChargebackStage(); + switch (stage) { + case CHARGEBACK -> damselStage.setChargeback(new InvoicePaymentChargebackStageChargeback()); + case PRE_ARBITRATION -> damselStage.setPreArbitration(new InvoicePaymentChargebackStagePreArbitration()); + case ARBITRATION -> damselStage.setArbitration(new InvoicePaymentChargebackStageArbitration()); + default -> throw new IllegalArgumentException(""); + } + + return damselStage; + } + + public static InvoicePaymentChargebackStatus mapToDamselStatus(String chargebackStatus) { + var status = Enum.valueOf(ChargebackStatus.class, chargebackStatus); + var damselStatus = new InvoicePaymentChargebackStatus(); + switch (status) { + case PENDING -> damselStatus.setPending(new InvoicePaymentChargebackPending()); + case ACCEPTED -> damselStatus.setAccepted(new InvoicePaymentChargebackAccepted()); + case REJECTED -> damselStatus.setRejected(new InvoicePaymentChargebackRejected()); + case CANCELLED -> damselStatus.setCancelled(new InvoicePaymentChargebackCancelled()); + default -> throw new IllegalArgumentException(""); + } + + return damselStatus; + } + + public static InvoicePaymentChargebackCategory mapToDamselCategory(String chargebackCategory) { + var category = Enum.valueOf(ChargebackCategory.class, chargebackCategory); + var damselCategory = new InvoicePaymentChargebackCategory(); + switch (category) { + case FRAUD -> damselCategory.setFraud(new InvoicePaymentChargebackCategoryFraud()); + case DISPUTE -> damselCategory.setDispute(new InvoicePaymentChargebackCategoryDispute()); + case AUTHORISATION -> damselCategory + .setAuthorisation(new InvoicePaymentChargebackCategoryAuthorisation()); + case PROCESSING_ERROR -> damselCategory + .setProcessingError(new InvoicePaymentChargebackCategoryProcessingError()); + default -> throw new IllegalArgumentException(""); + } + + return damselCategory; + } + + public static PayoutToolInfo mapToDamselPayoutToolInfo(String payoutToolType) { + var payoutToolInfo = new PayoutToolInfo(); + switch (payoutToolType) { + case "PayoutAccount" -> payoutToolInfo + .setRussianBankAccount(new RussianBankAccount()); //TODO: Russian or international? + case "Wallet" -> payoutToolInfo.setWalletInfo(new WalletInfo()); + case "PaymentInstitutionAccount" -> payoutToolInfo + .setPaymentInstitutionAccount(new PaymentInstitutionAccount()); + default -> throw new IllegalArgumentException(""); + } + + return payoutToolInfo; + } + + public static InvoicePaymentRefundStatus getRefundStatus(String refundStatus) { + var invoicePaymentRefundStatus = new InvoicePaymentRefundStatus(); + switch (Enum.valueOf(RefundStatus.StatusEnum.class, refundStatus)) { + case PENDING -> invoicePaymentRefundStatus.setPending(new InvoicePaymentRefundPending()); + case SUCCEEDED -> invoicePaymentRefundStatus.setSucceeded(new InvoicePaymentRefundSucceeded()); + case FAILED -> invoicePaymentRefundStatus.setFailed(new InvoicePaymentRefundFailed()); + default -> throw new IllegalArgumentException(""); + } + return invoicePaymentRefundStatus; + } +} From 81667b79ca5c0afbf9230c07bf1e7bf985c10f1e Mon Sep 17 00:00:00 2001 From: echerniak Date: Mon, 13 Sep 2021 12:28:01 +0300 Subject: [PATCH 05/37] WIP. Intermediate commit --- .../anapi/v2/service/SearchService.java | 246 +++++++++++++++++- 1 file changed, 244 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java b/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java index 90cd3fa..537c0d5 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java +++ b/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java @@ -1,6 +1,9 @@ package com.rbkmoney.anapi.v2.service; +import com.rbkmoney.damsel.domain.InvoicePaymentChargebackCategory; +import com.rbkmoney.damsel.domain.PayoutToolInfo; import com.rbkmoney.geck.common.util.TypeUtil; +import com.rbkmoney.magista.InvoiceStatus; import com.rbkmoney.magista.*; import com.rbkmoney.openapi.anapi_v2.model.Payer; import com.rbkmoney.openapi.anapi_v2.model.*; @@ -11,6 +14,7 @@ import org.springframework.stereotype.Service; import java.time.ZoneOffset; import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; @Service @RequiredArgsConstructor @@ -18,9 +22,9 @@ public class SearchService { private final MerchantStatisticsServiceSrv.Iface magistaClient; - public InlineResponse20010 findPayments(PaymentSearchQuery searchQuery) { + public InlineResponse20010 findPayments(PaymentSearchQuery query) { try { - StatPaymentResponse magistaResponse = magistaClient.searchPayments(searchQuery); + StatPaymentResponse magistaResponse = magistaClient.searchPayments(query); List results = new ArrayList<>(magistaResponse.getPaymentsSize()); for (StatPayment payment : magistaResponse.getPayments()) { PaymentSearchResult result = new PaymentSearchResult() @@ -61,21 +65,259 @@ public class SearchService { } public InlineResponse2008 findChargebacks(ChargebackSearchQuery query) { + try { + StatChargebackResponse magistaResponse = magistaClient.searchChargebacks(query); + List results = new ArrayList<>(magistaResponse.getChargebacksSize()); + for (StatChargeback chargeback : magistaResponse.getChargebacks()) { + Chargeback result = new Chargeback() + .bodyAmount(chargeback.getAmount()) + .createdAt(TypeUtil.stringToInstant(chargeback.getCreatedAt()).atOffset(ZoneOffset.UTC)) + .chargebackId(chargeback.getChargebackId()) + .fee(chargeback.getFee()) + .chargebackReason(new ChargebackReason() + .category(mapToCategory(chargeback.getChargebackReason().getCategory())) + .code(chargeback.getChargebackReason().getCode())) + .content(new Content().data(chargeback.getContent().getData()) + .type(chargeback.getContent().getType())) + .bodyCurrency(chargeback.getCurrencyCode().getSymbolicCode()); + results.add(result); + } + return new InlineResponse2008() + .result(results) + .continuationToken(magistaResponse.getContinuationToken()); + } catch (TException e) { + e.printStackTrace(); + } + //TODO: Error processing; + return null; + } + + private ChargebackCategory mapToCategory(InvoicePaymentChargebackCategory chargebackCategory) { + if (chargebackCategory.isSetAuthorisation()) { + return ChargebackCategory.AUTHORISATION; + } + + if (chargebackCategory.isSetDispute()) { + return ChargebackCategory.DISPUTE; + } + + if (chargebackCategory.isSetFraud()) { + return ChargebackCategory.FRAUD; + } + + if (chargebackCategory.isSetProcessingError()) { + return ChargebackCategory.PROCESSING_ERROR; + } + return null; } public InlineResponse2009 findInvoices(InvoiceSearchQuery query) { + try { + StatInvoiceResponse magistaResponse = magistaClient.searchInvoices(query); + List results = new ArrayList<>(magistaResponse.getInvoicesSize()); + for (StatInvoice invoice : magistaResponse.getInvoices()) { + Invoice result = new Invoice() + .amount(invoice.getAmount()) + .createdAt(TypeUtil.stringToInstant(invoice.getCreatedAt()).atOffset(ZoneOffset.UTC)) + .currency(invoice.getCurrencySymbolicCode()) + .externalID(invoice.getExternalId()) + .cart(invoice.getCart().getLines().stream().map(invoiceLine -> new InvoiceLine() + .cost(invoiceLine.getQuantity() * invoiceLine.getPrice().getAmount()) + .price(invoiceLine.getPrice().getAmount()) + .product(invoiceLine.getProduct()) + //.getTaxMode() + ).collect(Collectors.toList())) + .description(invoice.getDescription()) + .dueDate(TypeUtil.stringToInstant(invoice.getDue()).atOffset(ZoneOffset.UTC)) + .id(invoice.getId()) + .product(invoice.getProduct()) + //.reason() + .shopID(invoice.getShopId()) + .status(mapToStatus(invoice.getStatus())); + results.add(result); + } + return new InlineResponse2009() + .result(results) + .continuationToken(magistaResponse.getContinuationToken()); + } catch (TException e) { + e.printStackTrace(); + } + //TODO: Error processing; return null; } + private Invoice.StatusEnum mapToStatus(InvoiceStatus status) { + if (status.isSetFulfilled()) { + return Invoice.StatusEnum.FULFILLED; + } + + if (status.isSetPaid()) { + return Invoice.StatusEnum.PAID; + } + + if (status.isSetUnpaid()) { + return Invoice.StatusEnum.UNPAID; + } + + if (status.isSetCancelled()) { + return Invoice.StatusEnum.CANCELLED; + } + + throw new IllegalArgumentException(""); + } + public InlineResponse20011 findPayouts(PayoutSearchQuery query) { + try { + StatPayoutResponse magistaResponse = magistaClient.searchPayouts(query); + List results = new ArrayList<>(magistaResponse.getPayoutsSize()); + for (StatPayout payout : magistaResponse.getPayouts()) { + Payout result = new Payout() + .amount(payout.getAmount()) + .createdAt(TypeUtil.stringToInstant(payout.getCreatedAt()).atOffset(ZoneOffset.UTC)) + .currency(payout.getCurrencySymbolicCode()) + .fee(payout.getFee()) + // .cancellationDetails + .id(payout.getId()) + .payoutToolDetails(mapToPayoutToolDetails(payout.getPayoutToolInfo())) + .shopID(payout.getShopId()) + .status(mapToStatus(payout.getStatus())) + .cancellationDetails( + payout.getStatus().isSetCancelled() ? payout.getStatus().getCancelled().getDetails() : + null); + results.add(result); + } + return new InlineResponse20011() + .result(results) + .continuationToken(magistaResponse.getContinuationToken()); + } catch (TException e) { + e.printStackTrace(); + } + //TODO: Error processing; return null; } + private String mapToStatus(PayoutStatus status) { + if (status.isSetCancelled()) { + return "Cancelled"; + } + + if (status.isSetPaid()) { + return "Paid"; + } + + if (status.isSetConfirmed()) { + return "Confirmed"; + } + + if (status.isSetUnpaid()) { + return "Unpaid"; + } + + throw new IllegalArgumentException(""); + } + + private PayoutToolDetails mapToPayoutToolDetails(PayoutToolInfo payoutToolInfo) { + if (payoutToolInfo.isSetRussianBankAccount()) { + var account = payoutToolInfo.getRussianBankAccount(); + return new PayoutToolDetailsBankAccount() + .account(account.getAccount()) + .bankBik(account.getBankBik()) + .bankName(account.getBankName()) + .bankPostAccount(account.getBankPostAccount()) + .detailsType("PayoutToolDetailsBankAccount"); + } + + if (payoutToolInfo.isSetInternationalBankAccount()) { + var account = payoutToolInfo.getInternationalBankAccount(); + return new PayoutToolDetailsInternationalBankAccount() + .iban(account.getIban()) + .number(account.getNumber()) + .bankDetails(new InternationalBankDetails() + .name(account.getBank().getName()) + .bic(account.getBank().getBic()) + .countryCode(account.getBank().getCountry().name()) + .address(account.getBank().getAddress()) + .abartn(account.getBank().getAbaRtn())) + .correspondentBankAccount(mapToInternationalCorrespondentBankAccount(account)) + .detailsType("PayoutToolDetailsInternationalBankAccount"); + } + + if (payoutToolInfo.isSetPaymentInstitutionAccount()) { + return new PayoutToolDetailsPaymentInstitutionAccount() + .detailsType("PayoutToolDetailsPaymentInstitutionAccount"); + } + + if (payoutToolInfo.isSetWalletInfo()) { + return new PayoutToolDetailsWalletInfo() + .walletID(payoutToolInfo.getWalletInfo().getWalletId()) + .detailsType("PayoutToolDetailsWalletInfo"); + } + + throw new IllegalArgumentException(""); + + } + + private InternationalCorrespondentBankAccount mapToInternationalCorrespondentBankAccount( + com.rbkmoney.damsel.domain.InternationalBankAccount account) { + var details = account.getBank(); + return new InternationalCorrespondentBankAccount() + .bankDetails(new InternationalBankDetails() + .name(details.getName()) + .bic(details.getBic()) + .countryCode(details.getCountry().name()) + .address(details.getAddress()) + .abartn(details.getAbaRtn())) + .iban(account.getIban()) + .number(account.getNumber()) + .correspondentBankAccount( + mapToInternationalCorrespondentBankAccount(account.getCorrespondentAccount())); + } + public InlineResponse20012 findRefunds(RefundSearchQuery query) { + try { + StatRefundResponse magistaResponse = magistaClient.searchRefunds(query); + List results = new ArrayList<>(magistaResponse.getRefundsSize()); + for (StatRefund refund : magistaResponse.getRefunds()) { + RefundSearchResult result = new RefundSearchResult() + .amount(refund.getAmount()) + .createdAt(TypeUtil.stringToInstant(refund.getCreatedAt()).atOffset(ZoneOffset.UTC)) + .currency(refund.getCurrencySymbolicCode()) + // .cancellationDetails + .id(refund.getId()) + .shopID(refund.getShopId()) + .status(mapToStatus(refund.getStatus())) + .externalID(refund.getExternalId()) + .error(new RefundStatusError() + .code()); + results.add(result); + } + return new InlineResponse20012() + .result(results) + .continuationToken(magistaResponse.getContinuationToken()); + } catch (TException e) { + e.printStackTrace(); + } + //TODO: Error processing; return null; } + private RefundSearchResult.StatusEnum mapToStatus(InvoicePaymentRefundStatus status) { + if (status.isSetPending()) { + return RefundSearchResult.StatusEnum.PENDING; + } + + if (status.isSetFailed()) { + return RefundSearchResult.StatusEnum.FAILED; + } + + if (status.isSetSucceeded()) { + return RefundSearchResult.StatusEnum.SUCCEEDED; + } + + throw new IllegalArgumentException(""); + } + private Payer getPayer(StatPayment payment) { var statPayer = payment.getPayer(); Payer payer = new Payer(); From 41479becc62f351cdd7724100b1e4916d35b850c Mon Sep 17 00:00:00 2001 From: echerniak Date: Mon, 13 Sep 2021 15:37:11 +0300 Subject: [PATCH 06/37] WIP.Inermediate commit --- .../anapi/v2/service/SearchService.java | 50 ++++-------------- .../rbkmoney/anapi/v2/util/OpenApiUtil.java | 51 +++++++++++++++++++ 2 files changed, 61 insertions(+), 40 deletions(-) create mode 100644 src/main/java/com/rbkmoney/anapi/v2/util/OpenApiUtil.java diff --git a/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java b/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java index 537c0d5..5ff69f1 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java +++ b/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java @@ -16,6 +16,8 @@ import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; +import static com.rbkmoney.anapi.v2.util.OpenApiUtil.mapToCategory; + @Service @RequiredArgsConstructor public class SearchService { @@ -92,25 +94,7 @@ public class SearchService { return null; } - private ChargebackCategory mapToCategory(InvoicePaymentChargebackCategory chargebackCategory) { - if (chargebackCategory.isSetAuthorisation()) { - return ChargebackCategory.AUTHORISATION; - } - if (chargebackCategory.isSetDispute()) { - return ChargebackCategory.DISPUTE; - } - - if (chargebackCategory.isSetFraud()) { - return ChargebackCategory.FRAUD; - } - - if (chargebackCategory.isSetProcessingError()) { - return ChargebackCategory.PROCESSING_ERROR; - } - - return null; - } public InlineResponse2009 findInvoices(InvoiceSearchQuery query) { try { @@ -147,26 +131,6 @@ public class SearchService { return null; } - private Invoice.StatusEnum mapToStatus(InvoiceStatus status) { - if (status.isSetFulfilled()) { - return Invoice.StatusEnum.FULFILLED; - } - - if (status.isSetPaid()) { - return Invoice.StatusEnum.PAID; - } - - if (status.isSetUnpaid()) { - return Invoice.StatusEnum.UNPAID; - } - - if (status.isSetCancelled()) { - return Invoice.StatusEnum.CANCELLED; - } - - throw new IllegalArgumentException(""); - } - public InlineResponse20011 findPayouts(PayoutSearchQuery query) { try { StatPayoutResponse magistaResponse = magistaClient.searchPayouts(query); @@ -288,8 +252,14 @@ public class SearchService { .shopID(refund.getShopId()) .status(mapToStatus(refund.getStatus())) .externalID(refund.getExternalId()) - .error(new RefundStatusError() - .code()); + .error(refund.getStatus().isSetFailed() ? + new RefundStatusError() + .code(refund.getStatus().getFailed().getFailure().getFailure().getCode()) + .message(refund.getStatus().getFailed().getFailure().getFailure().getReason()) + : null) + .invoiceID(refund.getInvoiceId()) + .paymentID(refund.getPaymentId()) + .reason(refund.getReason()); results.add(result); } return new InlineResponse20012() diff --git a/src/main/java/com/rbkmoney/anapi/v2/util/OpenApiUtil.java b/src/main/java/com/rbkmoney/anapi/v2/util/OpenApiUtil.java new file mode 100644 index 0000000..130fcc6 --- /dev/null +++ b/src/main/java/com/rbkmoney/anapi/v2/util/OpenApiUtil.java @@ -0,0 +1,51 @@ +package com.rbkmoney.anapi.v2.util; + +import com.rbkmoney.damsel.domain.InvoicePaymentChargebackCategory; +import com.rbkmoney.magista.InvoiceStatus; +import com.rbkmoney.openapi.anapi_v2.model.ChargebackCategory; +import com.rbkmoney.openapi.anapi_v2.model.Invoice; +import lombok.experimental.UtilityClass; + +@UtilityClass +public class OpenApiUtil { + + public static ChargebackCategory mapToCategory(InvoicePaymentChargebackCategory chargebackCategory) { + if (chargebackCategory.isSetAuthorisation()) { + return ChargebackCategory.AUTHORISATION; + } + + if (chargebackCategory.isSetDispute()) { + return ChargebackCategory.DISPUTE; + } + + if (chargebackCategory.isSetFraud()) { + return ChargebackCategory.FRAUD; + } + + if (chargebackCategory.isSetProcessingError()) { + return ChargebackCategory.PROCESSING_ERROR; + } + + return null; + } + + public static Invoice.StatusEnum mapToStatus(InvoiceStatus status) { + if (status.isSetFulfilled()) { + return Invoice.StatusEnum.FULFILLED; + } + + if (status.isSetPaid()) { + return Invoice.StatusEnum.PAID; + } + + if (status.isSetUnpaid()) { + return Invoice.StatusEnum.UNPAID; + } + + if (status.isSetCancelled()) { + return Invoice.StatusEnum.CANCELLED; + } + + throw new IllegalArgumentException(""); + } +} From aa7d751c5ab57a0372640def05e0dd38b35abfc7 Mon Sep 17 00:00:00 2001 From: echerniak Date: Tue, 14 Sep 2021 13:23:25 +0300 Subject: [PATCH 07/37] WIP. Nullable fields check added --- .../anapi/v2/controller/SearchController.java | 57 ++-- .../anapi/v2/service/SearchService.java | 281 +++++------------- .../rbkmoney/anapi/v2/util/DamselUtil.java | 1 + .../rbkmoney/anapi/v2/util/OpenApiUtil.java | 188 +++++++++++- 4 files changed, 297 insertions(+), 230 deletions(-) diff --git a/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java b/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java index 2e883a9..0cbf48a 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java +++ b/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java @@ -36,12 +36,12 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp } @Override - public ResponseEntity searchPayments(String xRequestID, + public ResponseEntity searchPayments(String xrequestID, @NotNull @Size(min = 1, max = 40) @Valid String partyID, @NotNull @Valid OffsetDateTime fromTime, @NotNull @Valid OffsetDateTime toTime, @NotNull @Min(1L) @Max(1000L) @Valid Integer limit, - String xRequestDeadline, + String xrequestDeadline, @Size(min = 1, max = 40) @Valid String shopID, @Valid List shopIDs, @Valid String paymentInstitutionRealm, @@ -66,18 +66,22 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Min(1L) @Valid Long paymentAmountTo, @Valid List excludedShops, @Valid String continuationToken) { - //TODO: clarify mapping for paymentInstitutionRealm, xRequestID, xRequestDeadline + //TODO: clarify mapping for paymentInstitutionRealm, xrequestID, xrequestDeadline PaymentSearchQuery query = new PaymentSearchQuery() .setCommonSearchQueryParams( fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), continuationToken)) .setPaymentParams( new PaymentParams() - .setPaymentTool(mapToPaymentTool(paymentMethod)) - .setPaymentFlow(mapToInvoicePaymentFlow(paymentFlow)) + .setPaymentTool(paymentMethod != null ? mapToPaymentTool(paymentMethod) : null) + .setPaymentFlow(paymentFlow != null ? mapToInvoicePaymentFlow(paymentFlow) : null) .setPaymentTerminalProvider( - LegacyTerminalPaymentProvider.valueOf(paymentTerminalProvider)) + paymentTerminalProvider != null + ? LegacyTerminalPaymentProvider.valueOf(paymentTerminalProvider) : null) .setPaymentTokenProvider( - LegacyBankCardTokenProvider.valueOf(bankCardTokenProvider.getValue())) + bankCardTokenProvider != null + ? + LegacyBankCardTokenProvider.valueOf(bankCardTokenProvider.getValue()) : + null) .setPaymentAmountFrom(paymentAmountFrom) .setPaymentAmountTo(paymentAmountTo) .setPaymentEmail(payerEmail) @@ -89,8 +93,9 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp .setPaymentId(paymentID) .setPaymentIp(payerIP) .setPaymentRrn(rrn) - .setPaymentStatus(getStatus(paymentStatus)) - .setPaymentSystem(LegacyBankCardPaymentSystem.valueOf(bankCardPaymentSystem.getValue())) + .setPaymentStatus(paymentStatus != null ? getStatus(paymentStatus) : null) + .setPaymentSystem(bankCardPaymentSystem != null + ? LegacyBankCardPaymentSystem.valueOf(bankCardPaymentSystem.getValue()) : null) ) .setExcludedShopIds(excludedShops) .setExternalId(externalID) @@ -103,12 +108,12 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp produces = {"application/json; charset=utf-8"} ) @Override - public ResponseEntity searchChargebacks(String xRequestID, + public ResponseEntity searchChargebacks(String xrequestID, @NotNull @Size(min = 1, max = 40) @Valid String partyID, @NotNull @Valid OffsetDateTime fromTime, @NotNull @Valid OffsetDateTime toTime, @NotNull @Min(1L) @Max(1000L) @Valid Integer limit, - String xRequestDeadline, + String xrequestDeadline, @Size(min = 1, max = 40) @Valid String shopID, @Valid List shopIDs, @Valid String paymentInstitutionRealm, @@ -116,15 +121,16 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Size(min = 1, max = 40) @Valid String invoiceID, @Size(min = 1, max = 40) @Valid String paymentID, @Size(min = 1, max = 40) @Valid String chargebackID, + //TODO: checkIF null or empty list @Valid List chargebackStatuses, @Valid List chargebackStages, @Valid List chargebackCategories, @Valid String continuationToken) { - //TODO: clarify mapping for paymentInstitutionRealm, xRequestID, xRequestDeadline, offset + //TODO: clarify mapping for paymentInstitutionRealm, xrequestID, xrequestDeadline, offset ChargebackSearchQuery query = new ChargebackSearchQuery() .setCommonSearchQueryParams( fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), continuationToken)) - .setInvoiceIds(List.of(invoiceID)) + .setInvoiceIds(invoiceID != null ? List.of(invoiceID) : null) .setPaymentId(paymentID) .setChargebackId(chargebackID) .setChargebackStatuses(chargebackStatuses.stream() @@ -146,12 +152,12 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp produces = {"application/json; charset=utf-8"} ) @Override - public ResponseEntity searchInvoices(String xRequestID, + public ResponseEntity searchInvoices(String xrequestID, @NotNull @Size(min = 1, max = 40) @Valid String partyID, @NotNull @Valid OffsetDateTime fromTime, @NotNull @Valid OffsetDateTime toTime, @NotNull @Min(1L) @Max(1000L) @Valid Integer limit, - String xRequestDeadline, + String xrequestDeadline, @Size(min = 1, max = 40) @Valid String shopID, @Valid List shopIDs, @Valid String paymentInstitutionRealm, @@ -163,7 +169,7 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Min(1L) @Valid Long invoiceAmountTo, @Valid List excludedShops, @Valid String continuationToken) { - //TODO: clarify mapping for paymentInstitutionRealm, xRequestID, xRequestDeadline, excludedShops + //TODO: clarify mapping for paymentInstitutionRealm, xrequestID, xrequestDeadline, excludedShops InvoiceSearchQuery query = new InvoiceSearchQuery() .setCommonSearchQueryParams( fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), continuationToken)) @@ -171,8 +177,7 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp new PaymentParams() .setPaymentAmountFrom(invoiceAmountFrom) .setPaymentAmountTo(invoiceAmountTo) - .setPaymentStatus(getStatus(invoiceStatus)) - + .setPaymentStatus(invoiceStatus != null ? getStatus(invoiceStatus) : null) ) .setInvoiceIds(merge(invoiceID, invoiceIDs)) .setExternalId(externalID); @@ -184,12 +189,12 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp produces = {"application/json; charset=utf-8"} ) @Override - public ResponseEntity searchPayouts(String xRequestID, + public ResponseEntity searchPayouts(String xrequestID, @NotNull @Size(min = 1, max = 40) @Valid String partyID, @NotNull @Valid OffsetDateTime fromTime, @NotNull @Valid OffsetDateTime toTime, @NotNull @Min(1L) @Max(1000L) @Valid Integer limit, - String xRequestDeadline, + String xrequestDeadline, @Size(min = 1, max = 40) @Valid String shopID, @Valid List shopIDs, @Valid String paymentInstitutionRealm, @@ -198,13 +203,13 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Valid String payoutToolType, @Valid List excludedShops, @Valid String continuationToken) { - //TODO: clarify mapping for paymentInstitutionRealm, xRequestID, xRequestDeadline, excludedShops, + //TODO: clarify mapping for paymentInstitutionRealm, xrequestID, xrequestDeadline, excludedShops, //offset + setStatuses PayoutSearchQuery query = new PayoutSearchQuery() .setCommonSearchQueryParams( fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), continuationToken)) .setPayoutId(payoutID) - .setPayoutType(mapToDamselPayoutToolInfo(payoutToolType)); + .setPayoutType(payoutToolType != null ? mapToDamselPayoutToolInfo(payoutToolType) : null); return ResponseEntity.ok(searchService.findPayouts(query)); } @@ -213,12 +218,12 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp produces = {"application/json; charset=utf-8"} ) @Override - public ResponseEntity searchRefunds(String xRequestID, + public ResponseEntity searchRefunds(String xrequestID, @NotNull @Size(min = 1, max = 40) @Valid String partyID, @NotNull @Valid OffsetDateTime fromTime, @NotNull @Valid OffsetDateTime toTime, @NotNull @Min(1L) @Max(1000L) @Valid Integer limit, - String xRequestDeadline, + String xrequestDeadline, @Size(min = 1, max = 40) @Valid String shopID, @Valid List shopIDs, @Valid String paymentInstitutionRealm, @@ -231,11 +236,11 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Valid String refundStatus, @Valid List excludedShops, @Valid String continuationToken) { - //TODO: clarify mapping for paymentInstitutionRealm, xRequestID, xRequestDeadline, excludedShops, offset + //TODO: clarify mapping for paymentInstitutionRealm, xrequestID, xrequestDeadline, excludedShops, offset RefundSearchQuery query = new RefundSearchQuery() .setCommonSearchQueryParams( fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), continuationToken)) - .setRefundStatus(getRefundStatus(refundStatus)) + .setRefundStatus(refundStatus != null ? getRefundStatus(refundStatus) : null) .setInvoiceIds(merge(invoiceID, invoiceIDs)) .setExternalId(externalID) .setPaymentId(paymentID) diff --git a/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java b/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java index 5ff69f1..46190d8 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java +++ b/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java @@ -1,22 +1,20 @@ package com.rbkmoney.anapi.v2.service; -import com.rbkmoney.damsel.domain.InvoicePaymentChargebackCategory; -import com.rbkmoney.damsel.domain.PayoutToolInfo; +import com.rbkmoney.anapi.v2.util.OpenApiUtil; import com.rbkmoney.geck.common.util.TypeUtil; -import com.rbkmoney.magista.InvoiceStatus; import com.rbkmoney.magista.*; -import com.rbkmoney.openapi.anapi_v2.model.Payer; import com.rbkmoney.openapi.anapi_v2.model.*; import lombok.RequiredArgsConstructor; import org.apache.thrift.TException; import org.springframework.stereotype.Service; +import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; -import static com.rbkmoney.anapi.v2.util.OpenApiUtil.mapToCategory; +import static com.rbkmoney.anapi.v2.util.OpenApiUtil.*; @Service @RequiredArgsConstructor @@ -38,22 +36,22 @@ public class SearchService { .flow(new PaymentFlow() .type(payment.getFlow().isSetHold() ? PaymentFlow.TypeEnum.PAYMENTFLOWHOLD : PaymentFlow.TypeEnum.PAYMENTFLOWINSTANT)) - .geoLocationInfo(new GeoLocationInfo() + .geoLocationInfo(payment.getLocationInfo() != null ? new GeoLocationInfo() .cityGeoID(payment.getLocationInfo().getCityGeoId()) - .countryGeoID(payment.getLocationInfo().getCountryGeoId())) + .countryGeoID(payment.getLocationInfo().getCountryGeoId()) + : null) .id(payment.getId()) .invoiceID(payment.getInvoiceId()) .makeRecurrent(payment.isMakeRecurrent()) .payer(getPayer(payment)) .shopID(payment.getShopId()) .shortID(payment.getShortId()) - .status(getStatus(payment.getStatus())) - .statusChangedAt(TypeUtil.stringToInstant(getAt(payment.getStatus())) - .atOffset(ZoneOffset.UTC)) - .transactionInfo(new TransactionInfo() + .transactionInfo(payment.getAdditionalTransactionInfo() != null + ? new TransactionInfo() .approvalCode(payment.getAdditionalTransactionInfo().getApprovalCode()) .rrn(payment.getAdditionalTransactionInfo().getRrn()) - ); + : null); + fillStatusInfo(payment, result); results.add(result); } return new InlineResponse20010() @@ -66,6 +64,59 @@ public class SearchService { return null; } + private void fillStatusInfo(StatPayment payment, PaymentSearchResult result) { + var status = payment.getStatus(); + if (status.isSetCancelled()) { + OffsetDateTime createdAt = status.getCancelled().getAt() != null + ? TypeUtil.stringToInstant(status.getCancelled().getAt()).atOffset(ZoneOffset.UTC) + : null; + result.status(PaymentSearchResult.StatusEnum.CANCELLED) + .createdAt(createdAt); + } + + if (status.isSetCaptured()) { + OffsetDateTime createdAt = status.getCaptured().getAt() != null + ? TypeUtil.stringToInstant(status.getCaptured().getAt()).atOffset(ZoneOffset.UTC) + : null; + result.status(PaymentSearchResult.StatusEnum.CAPTURED) + .createdAt(createdAt); + } + + if (status.isSetChargedBack()) { + //TODO: Clearify + } + + if (status.isSetFailed()) { + OffsetDateTime createdAt = status.getFailed().getAt() != null + ? TypeUtil.stringToInstant(status.getFailed().getAt()).atOffset(ZoneOffset.UTC) + : null; + result.status(PaymentSearchResult.StatusEnum.FAILED) + .createdAt(createdAt); + } + + if (status.isSetPending()) { + result.status(PaymentSearchResult.StatusEnum.PENDING); + } + + if (status.isSetProcessed()) { + OffsetDateTime createdAt = status.getProcessed().getAt() != null + ? TypeUtil.stringToInstant(status.getProcessed().getAt()).atOffset(ZoneOffset.UTC) + : null; + result.status(PaymentSearchResult.StatusEnum.PROCESSED) + .createdAt(createdAt); + } + + if (status.isSetRefunded()) { + OffsetDateTime createdAt = status.getRefunded().getAt() != null + ? TypeUtil.stringToInstant(status.getRefunded().getAt()).atOffset(ZoneOffset.UTC) + : null; + result.status(PaymentSearchResult.StatusEnum.REFUNDED) + .createdAt(createdAt); + } + + throw new IllegalArgumentException(""); + } + public InlineResponse2008 findChargebacks(ChargebackSearchQuery query) { try { StatChargebackResponse magistaResponse = magistaClient.searchChargebacks(query); @@ -76,11 +127,13 @@ public class SearchService { .createdAt(TypeUtil.stringToInstant(chargeback.getCreatedAt()).atOffset(ZoneOffset.UTC)) .chargebackId(chargeback.getChargebackId()) .fee(chargeback.getFee()) - .chargebackReason(new ChargebackReason() + .chargebackReason(chargeback.getChargebackReason() != null + ? new ChargebackReason() .category(mapToCategory(chargeback.getChargebackReason().getCategory())) - .code(chargeback.getChargebackReason().getCode())) - .content(new Content().data(chargeback.getContent().getData()) - .type(chargeback.getContent().getType())) + .code(chargeback.getChargebackReason().getCode()) : null) + .content(chargeback.getContent() != null + ? new Content().data(chargeback.getContent().getData()) + .type(chargeback.getContent().getType()) : null) .bodyCurrency(chargeback.getCurrencyCode().getSymbolicCode()); results.add(result); } @@ -95,7 +148,6 @@ public class SearchService { } - public InlineResponse2009 findInvoices(InvoiceSearchQuery query) { try { StatInvoiceResponse magistaResponse = magistaClient.searchInvoices(query); @@ -106,19 +158,20 @@ public class SearchService { .createdAt(TypeUtil.stringToInstant(invoice.getCreatedAt()).atOffset(ZoneOffset.UTC)) .currency(invoice.getCurrencySymbolicCode()) .externalID(invoice.getExternalId()) - .cart(invoice.getCart().getLines().stream().map(invoiceLine -> new InvoiceLine() + .cart(invoice.getCart() != null + ? invoice.getCart().getLines().stream().map(invoiceLine -> new InvoiceLine() .cost(invoiceLine.getQuantity() * invoiceLine.getPrice().getAmount()) .price(invoiceLine.getPrice().getAmount()) .product(invoiceLine.getProduct()) //.getTaxMode() - ).collect(Collectors.toList())) + ).collect(Collectors.toList()) : null) .description(invoice.getDescription()) .dueDate(TypeUtil.stringToInstant(invoice.getDue()).atOffset(ZoneOffset.UTC)) .id(invoice.getId()) .product(invoice.getProduct()) //.reason() .shopID(invoice.getShopId()) - .status(mapToStatus(invoice.getStatus())); + .status(OpenApiUtil.mapToInvoiceStatus(invoice.getStatus())); results.add(result); } return new InlineResponse2009() @@ -141,11 +194,10 @@ public class SearchService { .createdAt(TypeUtil.stringToInstant(payout.getCreatedAt()).atOffset(ZoneOffset.UTC)) .currency(payout.getCurrencySymbolicCode()) .fee(payout.getFee()) - // .cancellationDetails .id(payout.getId()) .payoutToolDetails(mapToPayoutToolDetails(payout.getPayoutToolInfo())) .shopID(payout.getShopId()) - .status(mapToStatus(payout.getStatus())) + .status(OpenApiUtil.mapToPayoutStatus(payout.getStatus())) .cancellationDetails( payout.getStatus().isSetCancelled() ? payout.getStatus().getCancelled().getDetails() : null); @@ -161,83 +213,6 @@ public class SearchService { return null; } - private String mapToStatus(PayoutStatus status) { - if (status.isSetCancelled()) { - return "Cancelled"; - } - - if (status.isSetPaid()) { - return "Paid"; - } - - if (status.isSetConfirmed()) { - return "Confirmed"; - } - - if (status.isSetUnpaid()) { - return "Unpaid"; - } - - throw new IllegalArgumentException(""); - } - - private PayoutToolDetails mapToPayoutToolDetails(PayoutToolInfo payoutToolInfo) { - if (payoutToolInfo.isSetRussianBankAccount()) { - var account = payoutToolInfo.getRussianBankAccount(); - return new PayoutToolDetailsBankAccount() - .account(account.getAccount()) - .bankBik(account.getBankBik()) - .bankName(account.getBankName()) - .bankPostAccount(account.getBankPostAccount()) - .detailsType("PayoutToolDetailsBankAccount"); - } - - if (payoutToolInfo.isSetInternationalBankAccount()) { - var account = payoutToolInfo.getInternationalBankAccount(); - return new PayoutToolDetailsInternationalBankAccount() - .iban(account.getIban()) - .number(account.getNumber()) - .bankDetails(new InternationalBankDetails() - .name(account.getBank().getName()) - .bic(account.getBank().getBic()) - .countryCode(account.getBank().getCountry().name()) - .address(account.getBank().getAddress()) - .abartn(account.getBank().getAbaRtn())) - .correspondentBankAccount(mapToInternationalCorrespondentBankAccount(account)) - .detailsType("PayoutToolDetailsInternationalBankAccount"); - } - - if (payoutToolInfo.isSetPaymentInstitutionAccount()) { - return new PayoutToolDetailsPaymentInstitutionAccount() - .detailsType("PayoutToolDetailsPaymentInstitutionAccount"); - } - - if (payoutToolInfo.isSetWalletInfo()) { - return new PayoutToolDetailsWalletInfo() - .walletID(payoutToolInfo.getWalletInfo().getWalletId()) - .detailsType("PayoutToolDetailsWalletInfo"); - } - - throw new IllegalArgumentException(""); - - } - - private InternationalCorrespondentBankAccount mapToInternationalCorrespondentBankAccount( - com.rbkmoney.damsel.domain.InternationalBankAccount account) { - var details = account.getBank(); - return new InternationalCorrespondentBankAccount() - .bankDetails(new InternationalBankDetails() - .name(details.getName()) - .bic(details.getBic()) - .countryCode(details.getCountry().name()) - .address(details.getAddress()) - .abartn(details.getAbaRtn())) - .iban(account.getIban()) - .number(account.getNumber()) - .correspondentBankAccount( - mapToInternationalCorrespondentBankAccount(account.getCorrespondentAccount())); - } - public InlineResponse20012 findRefunds(RefundSearchQuery query) { try { StatRefundResponse magistaResponse = magistaClient.searchRefunds(query); @@ -247,15 +222,15 @@ public class SearchService { .amount(refund.getAmount()) .createdAt(TypeUtil.stringToInstant(refund.getCreatedAt()).atOffset(ZoneOffset.UTC)) .currency(refund.getCurrencySymbolicCode()) - // .cancellationDetails .id(refund.getId()) .shopID(refund.getShopId()) - .status(mapToStatus(refund.getStatus())) + .status(mapToRefundStatus(refund.getStatus())) .externalID(refund.getExternalId()) - .error(refund.getStatus().isSetFailed() ? - new RefundStatusError() - .code(refund.getStatus().getFailed().getFailure().getFailure().getCode()) - .message(refund.getStatus().getFailed().getFailure().getFailure().getReason()) + .error(refund.getStatus().isSetFailed() + && refund.getStatus().getFailed().getFailure().isSetFailure() + ? new RefundStatusError() + .code(refund.getStatus().getFailed().getFailure().getFailure().getCode()) + .message(refund.getStatus().getFailed().getFailure().getFailure().getReason()) : null) .invoiceID(refund.getInvoiceId()) .paymentID(refund.getPaymentId()) @@ -272,100 +247,4 @@ public class SearchService { return null; } - private RefundSearchResult.StatusEnum mapToStatus(InvoicePaymentRefundStatus status) { - if (status.isSetPending()) { - return RefundSearchResult.StatusEnum.PENDING; - } - - if (status.isSetFailed()) { - return RefundSearchResult.StatusEnum.FAILED; - } - - if (status.isSetSucceeded()) { - return RefundSearchResult.StatusEnum.SUCCEEDED; - } - - throw new IllegalArgumentException(""); - } - - private Payer getPayer(StatPayment payment) { - var statPayer = payment.getPayer(); - Payer payer = new Payer(); - - if (statPayer.isSetCustomer()) { - return payer.payerType(Payer.PayerTypeEnum.CUSTOMERPAYER); - } - - if (statPayer.isSetPaymentResource()) { - return payer.payerType(Payer.PayerTypeEnum.PAYMENTRESOURCEPAYER); - } - - if (statPayer.isSetRecurrent()) { - return payer.payerType(Payer.PayerTypeEnum.RECURRENTPAYER); - } - - return null; - } - - private PaymentSearchResult.StatusEnum getStatus(InvoicePaymentStatus status) { - if (status.isSetCancelled()) { - return PaymentSearchResult.StatusEnum.CANCELLED; - } - - if (status.isSetCaptured()) { - return PaymentSearchResult.StatusEnum.CAPTURED; - } - - if (status.isSetChargedBack()) { - //TODO: Clearify - } - - if (status.isSetFailed()) { - return PaymentSearchResult.StatusEnum.PROCESSED; - } - - if (status.isSetPending()) { - return PaymentSearchResult.StatusEnum.PENDING; - } - - if (status.isSetProcessed()) { - return PaymentSearchResult.StatusEnum.PROCESSED; - } - - if (status.isSetRefunded()) { - return PaymentSearchResult.StatusEnum.REFUNDED; - } - - throw new IllegalArgumentException(""); - - } - - private String getAt(InvoicePaymentStatus status) { - if (status.isSetCancelled()) { - return status.getCancelled().getAt(); - } - - if (status.isSetCaptured()) { - return status.getCaptured().getAt(); - } - - if (status.isSetChargedBack()) { - //TODO: Clearify - } - - if (status.isSetFailed()) { - return status.getFailed().getAt(); - } - - if (status.isSetProcessed()) { - return status.getProcessed().getAt(); - } - - if (status.isSetRefunded()) { - return status.getRefunded().getAt(); - } - - return null; - - } } diff --git a/src/main/java/com/rbkmoney/anapi/v2/util/DamselUtil.java b/src/main/java/com/rbkmoney/anapi/v2/util/DamselUtil.java index bc8bc2d..37bb27c 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/util/DamselUtil.java +++ b/src/main/java/com/rbkmoney/anapi/v2/util/DamselUtil.java @@ -56,6 +56,7 @@ public class DamselUtil { case CANCELLED -> invoicePaymentStatus.setCancelled(new InvoicePaymentCancelled()); case REFUNDED -> invoicePaymentStatus.setRefunded(new InvoicePaymentRefunded()); case FAILED -> invoicePaymentStatus.setFailed(new InvoicePaymentFailed()); + default -> throw new IllegalArgumentException(""); } return invoicePaymentStatus; } diff --git a/src/main/java/com/rbkmoney/anapi/v2/util/OpenApiUtil.java b/src/main/java/com/rbkmoney/anapi/v2/util/OpenApiUtil.java index 130fcc6..aeba28a 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/util/OpenApiUtil.java +++ b/src/main/java/com/rbkmoney/anapi/v2/util/OpenApiUtil.java @@ -1,9 +1,11 @@ package com.rbkmoney.anapi.v2.util; import com.rbkmoney.damsel.domain.InvoicePaymentChargebackCategory; +import com.rbkmoney.damsel.domain.PayoutToolInfo; import com.rbkmoney.magista.InvoiceStatus; -import com.rbkmoney.openapi.anapi_v2.model.ChargebackCategory; -import com.rbkmoney.openapi.anapi_v2.model.Invoice; +import com.rbkmoney.magista.*; +import com.rbkmoney.openapi.anapi_v2.model.Payer; +import com.rbkmoney.openapi.anapi_v2.model.*; import lombok.experimental.UtilityClass; @UtilityClass @@ -29,7 +31,7 @@ public class OpenApiUtil { return null; } - public static Invoice.StatusEnum mapToStatus(InvoiceStatus status) { + public static Invoice.StatusEnum mapToInvoiceStatus(InvoiceStatus status) { if (status.isSetFulfilled()) { return Invoice.StatusEnum.FULFILLED; } @@ -48,4 +50,184 @@ public class OpenApiUtil { throw new IllegalArgumentException(""); } + + public static String mapToPayoutStatus(PayoutStatus status) { + if (status.isSetCancelled()) { + return "Cancelled"; + } + + if (status.isSetPaid()) { + return "Paid"; + } + + if (status.isSetConfirmed()) { + return "Confirmed"; + } + + if (status.isSetUnpaid()) { + return "Unpaid"; + } + + throw new IllegalArgumentException(""); + } + + public static PayoutToolDetails mapToPayoutToolDetails(PayoutToolInfo payoutToolInfo) { + if (payoutToolInfo.isSetRussianBankAccount()) { + var account = payoutToolInfo.getRussianBankAccount(); + return new PayoutToolDetailsBankAccount() + .account(account.getAccount()) + .bankBik(account.getBankBik()) + .bankName(account.getBankName()) + .bankPostAccount(account.getBankPostAccount()) + .detailsType("PayoutToolDetailsBankAccount"); + } + + if (payoutToolInfo.isSetInternationalBankAccount()) { + var account = payoutToolInfo.getInternationalBankAccount(); + return new PayoutToolDetailsInternationalBankAccount() + .iban(account.getIban()) + .number(account.getNumber()) + .bankDetails(account.getBank() != null + ? new InternationalBankDetails() + .name(account.getBank().getName()) + .bic(account.getBank().getBic()) + .countryCode(account.getBank().getCountry() != null + ? account.getBank().getCountry().name() : null) + .address(account.getBank().getAddress()) + .abartn(account.getBank().getAbaRtn()) + : null) + .correspondentBankAccount(mapToInternationalCorrespondentBankAccount(account)) + .detailsType("PayoutToolDetailsInternationalBankAccount"); + } + + if (payoutToolInfo.isSetPaymentInstitutionAccount()) { + return new PayoutToolDetailsPaymentInstitutionAccount() + .detailsType("PayoutToolDetailsPaymentInstitutionAccount"); + } + + if (payoutToolInfo.isSetWalletInfo()) { + return new PayoutToolDetailsWalletInfo() + .walletID(payoutToolInfo.getWalletInfo().getWalletId()) + .detailsType("PayoutToolDetailsWalletInfo"); + } + + throw new IllegalArgumentException(""); + + } + + public static InternationalCorrespondentBankAccount mapToInternationalCorrespondentBankAccount( + com.rbkmoney.damsel.domain.InternationalBankAccount account) { + var details = account.getBank(); + return new InternationalCorrespondentBankAccount() + .bankDetails(details != null + ? new InternationalBankDetails() + .name(details.getName()) + .bic(details.getBic()) + .countryCode(details.getCountry().name()) + .address(details.getAddress()) + .abartn(details.getAbaRtn()) + : null) + .iban(account.getIban()) + .number(account.getNumber()) + .correspondentBankAccount(account.getCorrespondentAccount() != null + ? mapToInternationalCorrespondentBankAccount(account.getCorrespondentAccount()) + : null); + } + + public static RefundSearchResult.StatusEnum mapToRefundStatus(InvoicePaymentRefundStatus status) { + if (status.isSetPending()) { + return RefundSearchResult.StatusEnum.PENDING; + } + + if (status.isSetFailed()) { + return RefundSearchResult.StatusEnum.FAILED; + } + + if (status.isSetSucceeded()) { + return RefundSearchResult.StatusEnum.SUCCEEDED; + } + + throw new IllegalArgumentException(""); + } + + public static Payer getPayer(StatPayment payment) { + var statPayer = payment.getPayer(); + Payer payer = new Payer(); + + if (statPayer.isSetCustomer()) { + return payer.payerType(Payer.PayerTypeEnum.CUSTOMERPAYER); + } + + if (statPayer.isSetPaymentResource()) { + return payer.payerType(Payer.PayerTypeEnum.PAYMENTRESOURCEPAYER); + } + + if (statPayer.isSetRecurrent()) { + return payer.payerType(Payer.PayerTypeEnum.RECURRENTPAYER); + } + + return null; + } + + public static PaymentSearchResult.StatusEnum mapToPaymentStatus(InvoicePaymentStatus status) { + if (status.isSetCancelled()) { + return PaymentSearchResult.StatusEnum.CANCELLED; + } + + if (status.isSetCaptured()) { + return PaymentSearchResult.StatusEnum.CAPTURED; + } + + if (status.isSetChargedBack()) { + //TODO: Clearify + } + + if (status.isSetFailed()) { + return PaymentSearchResult.StatusEnum.PROCESSED; + } + + if (status.isSetPending()) { + return PaymentSearchResult.StatusEnum.PENDING; + } + + if (status.isSetProcessed()) { + return PaymentSearchResult.StatusEnum.PROCESSED; + } + + if (status.isSetRefunded()) { + return PaymentSearchResult.StatusEnum.REFUNDED; + } + + throw new IllegalArgumentException(""); + + } + + public static String getAt(InvoicePaymentStatus status) { + if (status.isSetCancelled()) { + return status.getCancelled().getAt(); + } + + if (status.isSetCaptured()) { + return status.getCaptured().getAt(); + } + + if (status.isSetChargedBack()) { + //TODO: Clearify + } + + if (status.isSetFailed()) { + return status.getFailed().getAt(); + } + + if (status.isSetProcessed()) { + return status.getProcessed().getAt(); + } + + if (status.isSetRefunded()) { + return status.getRefunded().getAt(); + } + + return null; + + } } From b10c72003ca116976ab51ca755f403f3e472023e Mon Sep 17 00:00:00 2001 From: echerniak Date: Tue, 14 Sep 2021 18:46:12 +0300 Subject: [PATCH 08/37] WIP.Error handling --- pom.xml | 6 +- .../anapi/v2/controller/SearchController.java | 237 ++++++++++++------ .../rbkmoney/anapi/v2/util/DamselUtil.java | 24 +- 3 files changed, 183 insertions(+), 84 deletions(-) diff --git a/pom.xml b/pom.xml index af2fc43..b8c33f2 100644 --- a/pom.xml +++ b/pom.xml @@ -66,7 +66,7 @@ com.rbkmoney magista-proto - 1.6-a3a5e93 + 1.9-1fc32d2 com.rbkmoney.geck @@ -82,6 +82,10 @@ org.springframework.boot spring-boot-starter + + org.springframework.boot + spring-boot-starter-validation + org.springframework.boot spring-boot-starter-web diff --git a/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java b/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java index 0cbf48a..2a065f7 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java +++ b/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java @@ -9,21 +9,28 @@ import com.rbkmoney.magista.*; import com.rbkmoney.openapi.anapi_v2.api.*; import com.rbkmoney.openapi.anapi_v2.model.*; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.context.request.NativeWebRequest; +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; import javax.validation.Valid; import javax.validation.constraints.*; import java.time.OffsetDateTime; import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; import static com.rbkmoney.anapi.v2.util.CommonUtil.merge; import static com.rbkmoney.anapi.v2.util.DamselUtil.*; +@Slf4j @Controller @RequiredArgsConstructor public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesApi, PayoutsApi, RefundsApi { @@ -67,39 +74,57 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Valid List excludedShops, @Valid String continuationToken) { //TODO: clarify mapping for paymentInstitutionRealm, xrequestID, xrequestDeadline - PaymentSearchQuery query = new PaymentSearchQuery() - .setCommonSearchQueryParams( - fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), continuationToken)) - .setPaymentParams( - new PaymentParams() - .setPaymentTool(paymentMethod != null ? mapToPaymentTool(paymentMethod) : null) - .setPaymentFlow(paymentFlow != null ? mapToInvoicePaymentFlow(paymentFlow) : null) - .setPaymentTerminalProvider( - paymentTerminalProvider != null - ? LegacyTerminalPaymentProvider.valueOf(paymentTerminalProvider) : null) - .setPaymentTokenProvider( - bankCardTokenProvider != null - ? - LegacyBankCardTokenProvider.valueOf(bankCardTokenProvider.getValue()) : - null) - .setPaymentAmountFrom(paymentAmountFrom) - .setPaymentAmountTo(paymentAmountTo) - .setPaymentEmail(payerEmail) - .setPaymentApprovalCode(approvalCode) - .setPaymentCustomerId(customerID) - .setPaymentFingerprint(payerFingerprint) - .setPaymentFirst6(first6) - .setPaymentLast4(last4) - .setPaymentId(paymentID) - .setPaymentIp(payerIP) - .setPaymentRrn(rrn) - .setPaymentStatus(paymentStatus != null ? getStatus(paymentStatus) : null) - .setPaymentSystem(bankCardPaymentSystem != null - ? LegacyBankCardPaymentSystem.valueOf(bankCardPaymentSystem.getValue()) : null) - ) - .setExcludedShopIds(excludedShops) - .setExternalId(externalID) - .setInvoiceIds(merge(invoiceID, invoiceIDs)); + PaymentSearchQuery query; + try { + query = new PaymentSearchQuery() + .setCommonSearchQueryParams( + fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), + continuationToken)) + .setExcludedShopIds(excludedShops) + .setExternalId(externalID) + .setInvoiceIds(merge(invoiceID, invoiceIDs)); + + PaymentParams paymentParams = new PaymentParams() + .setPaymentTool(paymentMethod != null ? mapToPaymentTool(paymentMethod) : null) + .setPaymentFlow(paymentFlow != null ? mapToInvoicePaymentFlow(paymentFlow) : null) + .setPaymentTerminalProvider( + paymentTerminalProvider != null + ? LegacyTerminalPaymentProvider.valueOf(paymentTerminalProvider) : + null) + .setPaymentTokenProvider( + bankCardTokenProvider != null + ? + LegacyBankCardTokenProvider + .valueOf(bankCardTokenProvider.getValue()) : + null) + .setPaymentEmail(payerEmail) + .setPaymentApprovalCode(approvalCode) + .setPaymentCustomerId(customerID) + .setPaymentFingerprint(payerFingerprint) + .setPaymentFirst6(first6) + .setPaymentLast4(last4) + .setPaymentId(paymentID) + .setPaymentIp(payerIP) + .setPaymentRrn(rrn) + .setPaymentStatus(paymentStatus != null ? getStatus(paymentStatus) : null) + .setPaymentSystem(bankCardPaymentSystem != null + ? LegacyBankCardPaymentSystem.valueOf(bankCardPaymentSystem.getValue()) : + null); + if (paymentAmountFrom != null) { + paymentParams.setPaymentAmountFrom(paymentAmountFrom); + } + if (paymentAmountTo != null) { + paymentParams.setPaymentAmountTo(paymentAmountTo); + } + query.setPaymentParams(paymentParams); + } catch (IllegalArgumentException e) { + log.error(e.getMessage()); + return new ResponseEntity( + new DefaultLogicError() + .code(DefaultLogicError.CodeEnum.INVALIDREQUEST) + .message(e.getMessage()), + HttpStatus.BAD_REQUEST); + } return ResponseEntity.ok(searchService.findPayments(query)); } @@ -121,29 +146,45 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Size(min = 1, max = 40) @Valid String invoiceID, @Size(min = 1, max = 40) @Valid String paymentID, @Size(min = 1, max = 40) @Valid String chargebackID, - //TODO: checkIF null or empty list @Valid List chargebackStatuses, @Valid List chargebackStages, @Valid List chargebackCategories, @Valid String continuationToken) { //TODO: clarify mapping for paymentInstitutionRealm, xrequestID, xrequestDeadline, offset - ChargebackSearchQuery query = new ChargebackSearchQuery() - .setCommonSearchQueryParams( - fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), continuationToken)) - .setInvoiceIds(invoiceID != null ? List.of(invoiceID) : null) - .setPaymentId(paymentID) - .setChargebackId(chargebackID) - .setChargebackStatuses(chargebackStatuses.stream() - .map(DamselUtil::mapToDamselStatus) - .collect(Collectors.toList()) - ) - .setChargebackStages(chargebackStages.stream() - .map(DamselUtil::mapToDamselStage) - .collect(Collectors.toList()) - ) - .setChargebackCategories(chargebackCategories.stream() - .map(DamselUtil::mapToDamselCategory) - .collect(Collectors.toList())); + ChargebackSearchQuery query; + try { + query = new ChargebackSearchQuery() + .setCommonSearchQueryParams( + fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), + continuationToken)) + .setInvoiceIds(invoiceID != null ? List.of(invoiceID) : null) + .setPaymentId(paymentID) + .setChargebackId(chargebackID) + .setChargebackStatuses(chargebackStatuses != null + ? chargebackStatuses.stream() + .map(DamselUtil::mapToDamselStatus) + .collect(Collectors.toList()) + : null + ) + .setChargebackStages(chargebackStages != null + ? chargebackStages.stream() + .map(DamselUtil::mapToDamselStage) + .collect(Collectors.toList()) + : null + ) + .setChargebackCategories(chargebackCategories != null + ? chargebackCategories.stream() + .map(DamselUtil::mapToDamselCategory) + .collect(Collectors.toList()) + : null); + } catch (IllegalArgumentException e) { + log.error(e.getMessage()); + return new ResponseEntity( + new DefaultLogicError() + .code(DefaultLogicError.CodeEnum.INVALIDREQUEST) + .message(e.getMessage()), + HttpStatus.BAD_REQUEST); + } return ResponseEntity.ok(searchService.findChargebacks(query)); } @@ -170,17 +211,28 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Valid List excludedShops, @Valid String continuationToken) { //TODO: clarify mapping for paymentInstitutionRealm, xrequestID, xrequestDeadline, excludedShops - InvoiceSearchQuery query = new InvoiceSearchQuery() - .setCommonSearchQueryParams( - fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), continuationToken)) - .setPaymentParams( - new PaymentParams() - .setPaymentAmountFrom(invoiceAmountFrom) - .setPaymentAmountTo(invoiceAmountTo) - .setPaymentStatus(invoiceStatus != null ? getStatus(invoiceStatus) : null) - ) - .setInvoiceIds(merge(invoiceID, invoiceIDs)) - .setExternalId(externalID); + InvoiceSearchQuery query; + try { + query = new InvoiceSearchQuery() + .setCommonSearchQueryParams( + fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), + continuationToken)) + .setPaymentParams( + new PaymentParams() + .setPaymentAmountFrom(invoiceAmountFrom) + .setPaymentAmountTo(invoiceAmountTo) + .setPaymentStatus(invoiceStatus != null ? getStatus(invoiceStatus) : null) + ) + .setInvoiceIds(merge(invoiceID, invoiceIDs)) + .setExternalId(externalID); + } catch (IllegalArgumentException e) { + log.error(e.getMessage()); + return new ResponseEntity( + new DefaultLogicError() + .code(DefaultLogicError.CodeEnum.INVALIDREQUEST) + .message(e.getMessage()), + HttpStatus.BAD_REQUEST); + } return ResponseEntity.ok(searchService.findInvoices(query)); } @@ -205,11 +257,22 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Valid String continuationToken) { //TODO: clarify mapping for paymentInstitutionRealm, xrequestID, xrequestDeadline, excludedShops, //offset + setStatuses - PayoutSearchQuery query = new PayoutSearchQuery() - .setCommonSearchQueryParams( - fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), continuationToken)) - .setPayoutId(payoutID) - .setPayoutType(payoutToolType != null ? mapToDamselPayoutToolInfo(payoutToolType) : null); + PayoutSearchQuery query; + try { + query = new PayoutSearchQuery() + .setCommonSearchQueryParams( + fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), + continuationToken)) + .setPayoutId(payoutID) + .setPayoutType(payoutToolType != null ? mapToDamselPayoutToolInfo(payoutToolType) : null); + } catch (IllegalArgumentException e) { + log.error(e.getMessage()); + return new ResponseEntity( + new DefaultLogicError() + .code(DefaultLogicError.CodeEnum.INVALIDREQUEST) + .message(e.getMessage()), + HttpStatus.BAD_REQUEST); + } return ResponseEntity.ok(searchService.findPayouts(query)); } @@ -237,14 +300,38 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Valid List excludedShops, @Valid String continuationToken) { //TODO: clarify mapping for paymentInstitutionRealm, xrequestID, xrequestDeadline, excludedShops, offset - RefundSearchQuery query = new RefundSearchQuery() - .setCommonSearchQueryParams( - fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), continuationToken)) - .setRefundStatus(refundStatus != null ? getRefundStatus(refundStatus) : null) - .setInvoiceIds(merge(invoiceID, invoiceIDs)) - .setExternalId(externalID) - .setPaymentId(paymentID) - .setRefundId(refundID); + RefundSearchQuery query; + try { + query = new RefundSearchQuery() + .setCommonSearchQueryParams( + fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), + continuationToken)) + .setRefundStatus(refundStatus != null ? getRefundStatus(refundStatus) : null) + .setInvoiceIds(merge(invoiceID, invoiceIDs)) + .setExternalId(externalID) + .setPaymentId(paymentID) + .setRefundId(refundID); + } catch (IllegalArgumentException e) { + log.error(e.getMessage()); + return new ResponseEntity( + new DefaultLogicError() + .code(DefaultLogicError.CodeEnum.INVALIDREQUEST) + .message(e.getMessage()), + HttpStatus.BAD_REQUEST); + } return ResponseEntity.ok(searchService.findRefunds(query)); } + + @ExceptionHandler({ConstraintViolationException.class}) + public ResponseEntity handleConstraintViolation(Exception ex) { + Set> constraintViolations = + ((ConstraintViolationException) ex).getConstraintViolations(); + String errorMessage = + constraintViolations.stream() + .map(violation -> violation.getPropertyPath() + ": " + violation.getMessage()) + .collect(Collectors.joining(", ")); + return new ResponseEntity<>(new DefaultLogicError() + .code(DefaultLogicError.CodeEnum.INVALIDREQUEST) + .message(errorMessage), HttpStatus.BAD_REQUEST); + } } diff --git a/src/main/java/com/rbkmoney/anapi/v2/util/DamselUtil.java b/src/main/java/com/rbkmoney/anapi/v2/util/DamselUtil.java index 37bb27c..ff0cf50 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/util/DamselUtil.java +++ b/src/main/java/com/rbkmoney/anapi/v2/util/DamselUtil.java @@ -17,7 +17,8 @@ public class DamselUtil { switch (paymentMethod) { case "bankCard" -> paymentTool.setBankCard(new BankCard()); case "paymentTerminal" -> paymentTool.setPaymentTerminal(new PaymentTerminal()); - default -> throw new IllegalArgumentException(""); + default -> throw new IllegalArgumentException( + String.format("Payment method %s cannot be processed", paymentMethod)); } return paymentTool; @@ -28,7 +29,8 @@ public class DamselUtil { switch (paymentFlow) { case "instant" -> invoicePaymentFlow.setInstant(new InvoicePaymentFlowInstant()); case "hold" -> invoicePaymentFlow.setHold(new InvoicePaymentFlowHold()); - default -> throw new IllegalArgumentException(""); + default -> throw new IllegalArgumentException( + String.format("Payment flow %s cannot be processed", paymentFlow)); } return invoicePaymentFlow; } @@ -56,7 +58,8 @@ public class DamselUtil { case CANCELLED -> invoicePaymentStatus.setCancelled(new InvoicePaymentCancelled()); case REFUNDED -> invoicePaymentStatus.setRefunded(new InvoicePaymentRefunded()); case FAILED -> invoicePaymentStatus.setFailed(new InvoicePaymentFailed()); - default -> throw new IllegalArgumentException(""); + default -> throw new IllegalArgumentException( + String.format("Payment status %s cannot be processed", paymentStatus)); } return invoicePaymentStatus; } @@ -68,7 +71,8 @@ public class DamselUtil { case CHARGEBACK -> damselStage.setChargeback(new InvoicePaymentChargebackStageChargeback()); case PRE_ARBITRATION -> damselStage.setPreArbitration(new InvoicePaymentChargebackStagePreArbitration()); case ARBITRATION -> damselStage.setArbitration(new InvoicePaymentChargebackStageArbitration()); - default -> throw new IllegalArgumentException(""); + default -> throw new IllegalArgumentException( + String.format("Chargeback stage %s cannot be processed", chargebackStage)); } return damselStage; @@ -82,7 +86,8 @@ public class DamselUtil { case ACCEPTED -> damselStatus.setAccepted(new InvoicePaymentChargebackAccepted()); case REJECTED -> damselStatus.setRejected(new InvoicePaymentChargebackRejected()); case CANCELLED -> damselStatus.setCancelled(new InvoicePaymentChargebackCancelled()); - default -> throw new IllegalArgumentException(""); + default -> throw new IllegalArgumentException( + String.format("Chargeback status %s cannot be processed", chargebackStatus)); } return damselStatus; @@ -98,7 +103,8 @@ public class DamselUtil { .setAuthorisation(new InvoicePaymentChargebackCategoryAuthorisation()); case PROCESSING_ERROR -> damselCategory .setProcessingError(new InvoicePaymentChargebackCategoryProcessingError()); - default -> throw new IllegalArgumentException(""); + default -> throw new IllegalArgumentException( + String.format("Chargeback category %s cannot be processed", chargebackCategory)); } return damselCategory; @@ -112,7 +118,8 @@ public class DamselUtil { case "Wallet" -> payoutToolInfo.setWalletInfo(new WalletInfo()); case "PaymentInstitutionAccount" -> payoutToolInfo .setPaymentInstitutionAccount(new PaymentInstitutionAccount()); - default -> throw new IllegalArgumentException(""); + default -> throw new IllegalArgumentException( + String.format("PayoutToolType %s cannot be processed", payoutToolType)); } return payoutToolInfo; @@ -124,7 +131,8 @@ public class DamselUtil { case PENDING -> invoicePaymentRefundStatus.setPending(new InvoicePaymentRefundPending()); case SUCCEEDED -> invoicePaymentRefundStatus.setSucceeded(new InvoicePaymentRefundSucceeded()); case FAILED -> invoicePaymentRefundStatus.setFailed(new InvoicePaymentRefundFailed()); - default -> throw new IllegalArgumentException(""); + default -> throw new IllegalArgumentException( + String.format("Refund status %s cannot be processed", refundStatus)); } return invoicePaymentRefundStatus; } From df6da51da2f0048868ee2930f5b954c97c60a820 Mon Sep 17 00:00:00 2001 From: echerniak Date: Wed, 15 Sep 2021 09:57:41 +0300 Subject: [PATCH 09/37] WIP. Error handling --- .../anapi/v2/service/SearchService.java | 279 ++++++++---------- 1 file changed, 127 insertions(+), 152 deletions(-) diff --git a/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java b/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java index 46190d8..c4e8a62 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java +++ b/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java @@ -5,7 +5,7 @@ import com.rbkmoney.geck.common.util.TypeUtil; import com.rbkmoney.magista.*; import com.rbkmoney.openapi.anapi_v2.model.*; import lombok.RequiredArgsConstructor; -import org.apache.thrift.TException; +import lombok.SneakyThrows; import org.springframework.stereotype.Service; import java.time.OffsetDateTime; @@ -22,46 +22,41 @@ public class SearchService { private final MerchantStatisticsServiceSrv.Iface magistaClient; + @SneakyThrows public InlineResponse20010 findPayments(PaymentSearchQuery query) { - try { - StatPaymentResponse magistaResponse = magistaClient.searchPayments(query); - List results = new ArrayList<>(magistaResponse.getPaymentsSize()); - for (StatPayment payment : magistaResponse.getPayments()) { - PaymentSearchResult result = new PaymentSearchResult() - .amount(payment.getAmount()) - .createdAt(TypeUtil.stringToInstant(payment.getCreatedAt()).atOffset(ZoneOffset.UTC)) - .currency(payment.getCurrencySymbolicCode()) - .externalID(payment.getExternalId()) - .fee(payment.getFee()) - .flow(new PaymentFlow() - .type(payment.getFlow().isSetHold() ? PaymentFlow.TypeEnum.PAYMENTFLOWHOLD : - PaymentFlow.TypeEnum.PAYMENTFLOWINSTANT)) - .geoLocationInfo(payment.getLocationInfo() != null ? new GeoLocationInfo() - .cityGeoID(payment.getLocationInfo().getCityGeoId()) - .countryGeoID(payment.getLocationInfo().getCountryGeoId()) - : null) - .id(payment.getId()) - .invoiceID(payment.getInvoiceId()) - .makeRecurrent(payment.isMakeRecurrent()) - .payer(getPayer(payment)) - .shopID(payment.getShopId()) - .shortID(payment.getShortId()) - .transactionInfo(payment.getAdditionalTransactionInfo() != null - ? new TransactionInfo() - .approvalCode(payment.getAdditionalTransactionInfo().getApprovalCode()) - .rrn(payment.getAdditionalTransactionInfo().getRrn()) - : null); - fillStatusInfo(payment, result); - results.add(result); - } - return new InlineResponse20010() - .result(results) - .continuationToken(magistaResponse.getContinuationToken()); - } catch (TException e) { - e.printStackTrace(); + StatPaymentResponse magistaResponse = magistaClient.searchPayments(query); + List results = new ArrayList<>(magistaResponse.getPaymentsSize()); + for (StatPayment payment : magistaResponse.getPayments()) { + PaymentSearchResult result = new PaymentSearchResult() + .amount(payment.getAmount()) + .createdAt(TypeUtil.stringToInstant(payment.getCreatedAt()).atOffset(ZoneOffset.UTC)) + .currency(payment.getCurrencySymbolicCode()) + .externalID(payment.getExternalId()) + .fee(payment.getFee()) + .flow(new PaymentFlow() + .type(payment.getFlow().isSetHold() ? PaymentFlow.TypeEnum.PAYMENTFLOWHOLD : + PaymentFlow.TypeEnum.PAYMENTFLOWINSTANT)) + .geoLocationInfo(payment.getLocationInfo() != null ? new GeoLocationInfo() + .cityGeoID(payment.getLocationInfo().getCityGeoId()) + .countryGeoID(payment.getLocationInfo().getCountryGeoId()) + : null) + .id(payment.getId()) + .invoiceID(payment.getInvoiceId()) + .makeRecurrent(payment.isMakeRecurrent()) + .payer(getPayer(payment)) + .shopID(payment.getShopId()) + .shortID(payment.getShortId()) + .transactionInfo(payment.getAdditionalTransactionInfo() != null + ? new TransactionInfo() + .approvalCode(payment.getAdditionalTransactionInfo().getApprovalCode()) + .rrn(payment.getAdditionalTransactionInfo().getRrn()) + : null); + fillStatusInfo(payment, result); + results.add(result); } - //TODO: Error processing; - return null; + return new InlineResponse20010() + .result(results) + .continuationToken(magistaResponse.getContinuationToken()); } private void fillStatusInfo(StatPayment payment, PaymentSearchResult result) { @@ -117,134 +112,114 @@ public class SearchService { throw new IllegalArgumentException(""); } + @SneakyThrows public InlineResponse2008 findChargebacks(ChargebackSearchQuery query) { - try { - StatChargebackResponse magistaResponse = magistaClient.searchChargebacks(query); - List results = new ArrayList<>(magistaResponse.getChargebacksSize()); - for (StatChargeback chargeback : magistaResponse.getChargebacks()) { - Chargeback result = new Chargeback() - .bodyAmount(chargeback.getAmount()) - .createdAt(TypeUtil.stringToInstant(chargeback.getCreatedAt()).atOffset(ZoneOffset.UTC)) - .chargebackId(chargeback.getChargebackId()) - .fee(chargeback.getFee()) - .chargebackReason(chargeback.getChargebackReason() != null - ? new ChargebackReason() - .category(mapToCategory(chargeback.getChargebackReason().getCategory())) - .code(chargeback.getChargebackReason().getCode()) : null) - .content(chargeback.getContent() != null - ? new Content().data(chargeback.getContent().getData()) - .type(chargeback.getContent().getType()) : null) - .bodyCurrency(chargeback.getCurrencyCode().getSymbolicCode()); - results.add(result); - } - return new InlineResponse2008() - .result(results) - .continuationToken(magistaResponse.getContinuationToken()); - } catch (TException e) { - e.printStackTrace(); + StatChargebackResponse magistaResponse = magistaClient.searchChargebacks(query); + List results = new ArrayList<>(magistaResponse.getChargebacksSize()); + for (StatChargeback chargeback : magistaResponse.getChargebacks()) { + Chargeback result = new Chargeback() + .bodyAmount(chargeback.getAmount()) + .createdAt(TypeUtil.stringToInstant(chargeback.getCreatedAt()).atOffset(ZoneOffset.UTC)) + .chargebackId(chargeback.getChargebackId()) + .fee(chargeback.getFee()) + .chargebackReason(chargeback.getChargebackReason() != null + ? new ChargebackReason() + .category(mapToCategory(chargeback.getChargebackReason().getCategory())) + .code(chargeback.getChargebackReason().getCode()) : null) + .content(chargeback.getContent() != null + ? new Content().data(chargeback.getContent().getData()) + .type(chargeback.getContent().getType()) : null) + .bodyCurrency(chargeback.getCurrencyCode().getSymbolicCode()); + results.add(result); } - //TODO: Error processing; - return null; + return new InlineResponse2008() + .result(results) + .continuationToken(magistaResponse.getContinuationToken()); } + @SneakyThrows public InlineResponse2009 findInvoices(InvoiceSearchQuery query) { - try { - StatInvoiceResponse magistaResponse = magistaClient.searchInvoices(query); - List results = new ArrayList<>(magistaResponse.getInvoicesSize()); - for (StatInvoice invoice : magistaResponse.getInvoices()) { - Invoice result = new Invoice() - .amount(invoice.getAmount()) - .createdAt(TypeUtil.stringToInstant(invoice.getCreatedAt()).atOffset(ZoneOffset.UTC)) - .currency(invoice.getCurrencySymbolicCode()) - .externalID(invoice.getExternalId()) - .cart(invoice.getCart() != null - ? invoice.getCart().getLines().stream().map(invoiceLine -> new InvoiceLine() - .cost(invoiceLine.getQuantity() * invoiceLine.getPrice().getAmount()) - .price(invoiceLine.getPrice().getAmount()) - .product(invoiceLine.getProduct()) - //.getTaxMode() - ).collect(Collectors.toList()) : null) - .description(invoice.getDescription()) - .dueDate(TypeUtil.stringToInstant(invoice.getDue()).atOffset(ZoneOffset.UTC)) - .id(invoice.getId()) - .product(invoice.getProduct()) - //.reason() - .shopID(invoice.getShopId()) - .status(OpenApiUtil.mapToInvoiceStatus(invoice.getStatus())); - results.add(result); - } - return new InlineResponse2009() - .result(results) - .continuationToken(magistaResponse.getContinuationToken()); - } catch (TException e) { - e.printStackTrace(); + StatInvoiceResponse magistaResponse = magistaClient.searchInvoices(query); + List results = new ArrayList<>(magistaResponse.getInvoicesSize()); + for (StatInvoice invoice : magistaResponse.getInvoices()) { + Invoice result = new Invoice() + .amount(invoice.getAmount()) + .createdAt(TypeUtil.stringToInstant(invoice.getCreatedAt()).atOffset(ZoneOffset.UTC)) + .currency(invoice.getCurrencySymbolicCode()) + .externalID(invoice.getExternalId()) + .cart(invoice.getCart() != null + ? invoice.getCart().getLines().stream().map(invoiceLine -> new InvoiceLine() + .cost(invoiceLine.getQuantity() * invoiceLine.getPrice().getAmount()) + .price(invoiceLine.getPrice().getAmount()) + .product(invoiceLine.getProduct()) + //.getTaxMode() + ).collect(Collectors.toList()) : null) + .description(invoice.getDescription()) + .dueDate(TypeUtil.stringToInstant(invoice.getDue()).atOffset(ZoneOffset.UTC)) + .id(invoice.getId()) + .product(invoice.getProduct()) + //.reason() + .shopID(invoice.getShopId()) + .status(OpenApiUtil.mapToInvoiceStatus(invoice.getStatus())); + results.add(result); } - //TODO: Error processing; - return null; + return new InlineResponse2009() + .result(results) + .continuationToken(magistaResponse.getContinuationToken()); } + @SneakyThrows public InlineResponse20011 findPayouts(PayoutSearchQuery query) { - try { - StatPayoutResponse magistaResponse = magistaClient.searchPayouts(query); - List results = new ArrayList<>(magistaResponse.getPayoutsSize()); - for (StatPayout payout : magistaResponse.getPayouts()) { - Payout result = new Payout() - .amount(payout.getAmount()) - .createdAt(TypeUtil.stringToInstant(payout.getCreatedAt()).atOffset(ZoneOffset.UTC)) - .currency(payout.getCurrencySymbolicCode()) - .fee(payout.getFee()) - .id(payout.getId()) - .payoutToolDetails(mapToPayoutToolDetails(payout.getPayoutToolInfo())) - .shopID(payout.getShopId()) - .status(OpenApiUtil.mapToPayoutStatus(payout.getStatus())) - .cancellationDetails( - payout.getStatus().isSetCancelled() ? payout.getStatus().getCancelled().getDetails() : - null); - results.add(result); - } - return new InlineResponse20011() - .result(results) - .continuationToken(magistaResponse.getContinuationToken()); - } catch (TException e) { - e.printStackTrace(); + StatPayoutResponse magistaResponse = magistaClient.searchPayouts(query); + List results = new ArrayList<>(magistaResponse.getPayoutsSize()); + for (StatPayout payout : magistaResponse.getPayouts()) { + Payout result = new Payout() + .amount(payout.getAmount()) + .createdAt(TypeUtil.stringToInstant(payout.getCreatedAt()).atOffset(ZoneOffset.UTC)) + .currency(payout.getCurrencySymbolicCode()) + .fee(payout.getFee()) + .id(payout.getId()) + .payoutToolDetails(mapToPayoutToolDetails(payout.getPayoutToolInfo())) + .shopID(payout.getShopId()) + .status(OpenApiUtil.mapToPayoutStatus(payout.getStatus())) + .cancellationDetails( + payout.getStatus().isSetCancelled() ? payout.getStatus().getCancelled().getDetails() : + null); + results.add(result); } - //TODO: Error processing; - return null; + return new InlineResponse20011() + .result(results) + .continuationToken(magistaResponse.getContinuationToken()); } + @SneakyThrows public InlineResponse20012 findRefunds(RefundSearchQuery query) { - try { - StatRefundResponse magistaResponse = magistaClient.searchRefunds(query); - List results = new ArrayList<>(magistaResponse.getRefundsSize()); - for (StatRefund refund : magistaResponse.getRefunds()) { - RefundSearchResult result = new RefundSearchResult() - .amount(refund.getAmount()) - .createdAt(TypeUtil.stringToInstant(refund.getCreatedAt()).atOffset(ZoneOffset.UTC)) - .currency(refund.getCurrencySymbolicCode()) - .id(refund.getId()) - .shopID(refund.getShopId()) - .status(mapToRefundStatus(refund.getStatus())) - .externalID(refund.getExternalId()) - .error(refund.getStatus().isSetFailed() - && refund.getStatus().getFailed().getFailure().isSetFailure() - ? new RefundStatusError() - .code(refund.getStatus().getFailed().getFailure().getFailure().getCode()) - .message(refund.getStatus().getFailed().getFailure().getFailure().getReason()) - : null) - .invoiceID(refund.getInvoiceId()) - .paymentID(refund.getPaymentId()) - .reason(refund.getReason()); - results.add(result); - } - return new InlineResponse20012() - .result(results) - .continuationToken(magistaResponse.getContinuationToken()); - } catch (TException e) { - e.printStackTrace(); + StatRefundResponse magistaResponse = magistaClient.searchRefunds(query); + List results = new ArrayList<>(magistaResponse.getRefundsSize()); + for (StatRefund refund : magistaResponse.getRefunds()) { + RefundSearchResult result = new RefundSearchResult() + .amount(refund.getAmount()) + .createdAt(TypeUtil.stringToInstant(refund.getCreatedAt()).atOffset(ZoneOffset.UTC)) + .currency(refund.getCurrencySymbolicCode()) + .id(refund.getId()) + .shopID(refund.getShopId()) + .status(mapToRefundStatus(refund.getStatus())) + .externalID(refund.getExternalId()) + .error(refund.getStatus().isSetFailed() + && refund.getStatus().getFailed().getFailure().isSetFailure() + ? new RefundStatusError() + .code(refund.getStatus().getFailed().getFailure().getFailure().getCode()) + .message(refund.getStatus().getFailed().getFailure().getFailure().getReason()) + : null) + .invoiceID(refund.getInvoiceId()) + .paymentID(refund.getPaymentId()) + .reason(refund.getReason()); + results.add(result); } - //TODO: Error processing; - return null; + return new InlineResponse20012() + .result(results) + .continuationToken(magistaResponse.getContinuationToken()); } } From a0be312bd40b6cbc061444ab9546bfe10535aeb9 Mon Sep 17 00:00:00 2001 From: echerniak Date: Wed, 15 Sep 2021 10:44:54 +0300 Subject: [PATCH 10/37] WIP. Utility class refactoring --- .../anapi/v2/service/SearchService.java | 60 +-------- .../rbkmoney/anapi/v2/util/OpenApiUtil.java | 117 ++++++++++-------- 2 files changed, 71 insertions(+), 106 deletions(-) diff --git a/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java b/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java index c4e8a62..eed5cd2 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java +++ b/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java @@ -8,7 +8,6 @@ import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import org.springframework.stereotype.Service; -import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.util.ArrayList; import java.util.List; @@ -51,7 +50,7 @@ public class SearchService { .approvalCode(payment.getAdditionalTransactionInfo().getApprovalCode()) .rrn(payment.getAdditionalTransactionInfo().getRrn()) : null); - fillStatusInfo(payment, result); + fillPaymentStatusInfo(payment, result); results.add(result); } return new InlineResponse20010() @@ -59,59 +58,6 @@ public class SearchService { .continuationToken(magistaResponse.getContinuationToken()); } - private void fillStatusInfo(StatPayment payment, PaymentSearchResult result) { - var status = payment.getStatus(); - if (status.isSetCancelled()) { - OffsetDateTime createdAt = status.getCancelled().getAt() != null - ? TypeUtil.stringToInstant(status.getCancelled().getAt()).atOffset(ZoneOffset.UTC) - : null; - result.status(PaymentSearchResult.StatusEnum.CANCELLED) - .createdAt(createdAt); - } - - if (status.isSetCaptured()) { - OffsetDateTime createdAt = status.getCaptured().getAt() != null - ? TypeUtil.stringToInstant(status.getCaptured().getAt()).atOffset(ZoneOffset.UTC) - : null; - result.status(PaymentSearchResult.StatusEnum.CAPTURED) - .createdAt(createdAt); - } - - if (status.isSetChargedBack()) { - //TODO: Clearify - } - - if (status.isSetFailed()) { - OffsetDateTime createdAt = status.getFailed().getAt() != null - ? TypeUtil.stringToInstant(status.getFailed().getAt()).atOffset(ZoneOffset.UTC) - : null; - result.status(PaymentSearchResult.StatusEnum.FAILED) - .createdAt(createdAt); - } - - if (status.isSetPending()) { - result.status(PaymentSearchResult.StatusEnum.PENDING); - } - - if (status.isSetProcessed()) { - OffsetDateTime createdAt = status.getProcessed().getAt() != null - ? TypeUtil.stringToInstant(status.getProcessed().getAt()).atOffset(ZoneOffset.UTC) - : null; - result.status(PaymentSearchResult.StatusEnum.PROCESSED) - .createdAt(createdAt); - } - - if (status.isSetRefunded()) { - OffsetDateTime createdAt = status.getRefunded().getAt() != null - ? TypeUtil.stringToInstant(status.getRefunded().getAt()).atOffset(ZoneOffset.UTC) - : null; - result.status(PaymentSearchResult.StatusEnum.REFUNDED) - .createdAt(createdAt); - } - - throw new IllegalArgumentException(""); - } - @SneakyThrows public InlineResponse2008 findChargebacks(ChargebackSearchQuery query) { StatChargebackResponse magistaResponse = magistaClient.searchChargebacks(query); @@ -124,7 +70,7 @@ public class SearchService { .fee(chargeback.getFee()) .chargebackReason(chargeback.getChargebackReason() != null ? new ChargebackReason() - .category(mapToCategory(chargeback.getChargebackReason().getCategory())) + .category(mapToChargebackCategory(chargeback.getChargebackReason().getCategory())) .code(chargeback.getChargebackReason().getCode()) : null) .content(chargeback.getContent() != null ? new Content().data(chargeback.getContent().getData()) @@ -204,7 +150,7 @@ public class SearchService { .currency(refund.getCurrencySymbolicCode()) .id(refund.getId()) .shopID(refund.getShopId()) - .status(mapToRefundStatus(refund.getStatus())) + .status(refund.getStatus() != null ? mapToRefundStatus(refund.getStatus()) : null) .externalID(refund.getExternalId()) .error(refund.getStatus().isSetFailed() && refund.getStatus().getFailed().getFailure().isSetFailure() diff --git a/src/main/java/com/rbkmoney/anapi/v2/util/OpenApiUtil.java b/src/main/java/com/rbkmoney/anapi/v2/util/OpenApiUtil.java index aeba28a..246b5e0 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/util/OpenApiUtil.java +++ b/src/main/java/com/rbkmoney/anapi/v2/util/OpenApiUtil.java @@ -1,18 +1,26 @@ package com.rbkmoney.anapi.v2.util; +import com.rbkmoney.damsel.domain.CountryCode; import com.rbkmoney.damsel.domain.InvoicePaymentChargebackCategory; import com.rbkmoney.damsel.domain.PayoutToolInfo; +import com.rbkmoney.geck.common.util.TypeUtil; +import com.rbkmoney.magista.InvoicePaymentRefundStatus; import com.rbkmoney.magista.InvoiceStatus; -import com.rbkmoney.magista.*; -import com.rbkmoney.openapi.anapi_v2.model.Payer; +import com.rbkmoney.magista.PayoutStatus; +import com.rbkmoney.magista.StatPayment; import com.rbkmoney.openapi.anapi_v2.model.*; import lombok.experimental.UtilityClass; +import javax.annotation.Nullable; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; + @UtilityClass public class OpenApiUtil { - public static ChargebackCategory mapToCategory(InvoicePaymentChargebackCategory chargebackCategory) { + public static ChargebackCategory mapToChargebackCategory(InvoicePaymentChargebackCategory chargebackCategory) { if (chargebackCategory.isSetAuthorisation()) { + //TODO: Is it a typo? Could be fixed? (authorization) return ChargebackCategory.AUTHORISATION; } @@ -28,7 +36,8 @@ public class OpenApiUtil { return ChargebackCategory.PROCESSING_ERROR; } - return null; + throw new IllegalArgumentException( + String.format("Chargeback category %s cannot be processed", chargebackCategory)); } public static Invoice.StatusEnum mapToInvoiceStatus(InvoiceStatus status) { @@ -48,7 +57,8 @@ public class OpenApiUtil { return Invoice.StatusEnum.CANCELLED; } - throw new IllegalArgumentException(""); + throw new IllegalArgumentException( + String.format("Invoice status %s cannot be processed", status)); } public static String mapToPayoutStatus(PayoutStatus status) { @@ -68,7 +78,8 @@ public class OpenApiUtil { return "Unpaid"; } - throw new IllegalArgumentException(""); + throw new IllegalArgumentException( + String.format("Payout status %s cannot be processed", status)); } public static PayoutToolDetails mapToPayoutToolDetails(PayoutToolInfo payoutToolInfo) { @@ -91,8 +102,7 @@ public class OpenApiUtil { ? new InternationalBankDetails() .name(account.getBank().getName()) .bic(account.getBank().getBic()) - .countryCode(account.getBank().getCountry() != null - ? account.getBank().getCountry().name() : null) + .countryCode(getCountryCode(account.getBank().getCountry())) .address(account.getBank().getAddress()) .abartn(account.getBank().getAbaRtn()) : null) @@ -111,10 +121,19 @@ public class OpenApiUtil { .detailsType("PayoutToolDetailsWalletInfo"); } - throw new IllegalArgumentException(""); + throw new IllegalArgumentException( + String.format("PayoutToolInfo %s cannot be processed", payoutToolInfo)); } + private static String getCountryCode(@Nullable CountryCode countryCode) { + if (countryCode == null) { + return null; + } + + return countryCode.name(); + } + public static InternationalCorrespondentBankAccount mapToInternationalCorrespondentBankAccount( com.rbkmoney.damsel.domain.InternationalBankAccount account) { var details = account.getBank(); @@ -147,7 +166,8 @@ public class OpenApiUtil { return RefundSearchResult.StatusEnum.SUCCEEDED; } - throw new IllegalArgumentException(""); + throw new IllegalArgumentException( + String.format("Refund status %s cannot be processed", status)); } public static Payer getPayer(StatPayment payment) { @@ -166,16 +186,28 @@ public class OpenApiUtil { return payer.payerType(Payer.PayerTypeEnum.RECURRENTPAYER); } - return null; + throw new IllegalArgumentException( + String.format("Payer %s cannot be processed", statPayer)); } - public static PaymentSearchResult.StatusEnum mapToPaymentStatus(InvoicePaymentStatus status) { + public static void fillPaymentStatusInfo(StatPayment payment, PaymentSearchResult result) { + var status = payment.getStatus(); if (status.isSetCancelled()) { - return PaymentSearchResult.StatusEnum.CANCELLED; + OffsetDateTime createdAt = status.getCancelled().getAt() != null + ? TypeUtil.stringToInstant(status.getCancelled().getAt()).atOffset(ZoneOffset.UTC) + : null; + result.status(PaymentSearchResult.StatusEnum.CANCELLED) + .createdAt(createdAt); + return; } if (status.isSetCaptured()) { - return PaymentSearchResult.StatusEnum.CAPTURED; + OffsetDateTime createdAt = status.getCaptured().getAt() != null + ? TypeUtil.stringToInstant(status.getCaptured().getAt()).atOffset(ZoneOffset.UTC) + : null; + result.status(PaymentSearchResult.StatusEnum.CAPTURED) + .createdAt(createdAt); + return; } if (status.isSetChargedBack()) { @@ -183,51 +215,38 @@ public class OpenApiUtil { } if (status.isSetFailed()) { - return PaymentSearchResult.StatusEnum.PROCESSED; + OffsetDateTime createdAt = status.getFailed().getAt() != null + ? TypeUtil.stringToInstant(status.getFailed().getAt()).atOffset(ZoneOffset.UTC) + : null; + result.status(PaymentSearchResult.StatusEnum.FAILED) + .createdAt(createdAt); + return; } if (status.isSetPending()) { - return PaymentSearchResult.StatusEnum.PENDING; + result.status(PaymentSearchResult.StatusEnum.PENDING); + return; } if (status.isSetProcessed()) { - return PaymentSearchResult.StatusEnum.PROCESSED; + OffsetDateTime createdAt = status.getProcessed().getAt() != null + ? TypeUtil.stringToInstant(status.getProcessed().getAt()).atOffset(ZoneOffset.UTC) + : null; + result.status(PaymentSearchResult.StatusEnum.PROCESSED) + .createdAt(createdAt); + return; } if (status.isSetRefunded()) { - return PaymentSearchResult.StatusEnum.REFUNDED; + OffsetDateTime createdAt = status.getRefunded().getAt() != null + ? TypeUtil.stringToInstant(status.getRefunded().getAt()).atOffset(ZoneOffset.UTC) + : null; + result.status(PaymentSearchResult.StatusEnum.REFUNDED) + .createdAt(createdAt); + return; } - throw new IllegalArgumentException(""); - - } - - public static String getAt(InvoicePaymentStatus status) { - if (status.isSetCancelled()) { - return status.getCancelled().getAt(); - } - - if (status.isSetCaptured()) { - return status.getCaptured().getAt(); - } - - if (status.isSetChargedBack()) { - //TODO: Clearify - } - - if (status.isSetFailed()) { - return status.getFailed().getAt(); - } - - if (status.isSetProcessed()) { - return status.getProcessed().getAt(); - } - - if (status.isSetRefunded()) { - return status.getRefunded().getAt(); - } - - return null; - + throw new IllegalArgumentException( + String.format("Payment status %s cannot be processed", payment.getStatus())); } } From 05f2b62f35013853057c212194ed772c82144bfd Mon Sep 17 00:00:00 2001 From: echerniak Date: Thu, 16 Sep 2021 14:59:35 +0300 Subject: [PATCH 11/37] WIP. Refactoring in progress --- .../rbkmoney/anapi/v2/config/AsyncConfig.java | 20 ++ .../anapi/v2/controller/SearchController.java | 174 +++++++++++------- .../ParamsToPaymentSearchQueryConverter.java | 93 ++++++++++ ...PaymentToPaymentSearchResultConverter.java | 47 +++++ .../v2/exception/BadRequestException.java | 19 ++ .../anapi/v2/service/SearchService.java | 74 +++----- .../rbkmoney/anapi/v2/util/CommonUtil.java | 23 +++ .../rbkmoney/anapi/v2/util/DamselUtil.java | 17 +- 8 files changed, 351 insertions(+), 116 deletions(-) create mode 100644 src/main/java/com/rbkmoney/anapi/v2/config/AsyncConfig.java create mode 100644 src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPaymentSearchQueryConverter.java create mode 100644 src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverter.java create mode 100644 src/main/java/com/rbkmoney/anapi/v2/exception/BadRequestException.java diff --git a/src/main/java/com/rbkmoney/anapi/v2/config/AsyncConfig.java b/src/main/java/com/rbkmoney/anapi/v2/config/AsyncConfig.java new file mode 100644 index 0000000..249ef97 --- /dev/null +++ b/src/main/java/com/rbkmoney/anapi/v2/config/AsyncConfig.java @@ -0,0 +1,20 @@ +package com.rbkmoney.anapi.v2.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.task.SimpleAsyncTaskExecutor; +import org.springframework.scheduling.annotation.AsyncConfigurer; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.Executor; + +@Configuration +@EnableAsync +public class AsyncConfig implements AsyncConfigurer { + + @Override + public Executor getAsyncExecutor() { + return new ThreadPoolTaskExecutor(); + } +} diff --git a/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java b/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java index 2a065f7..cb8edc8 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java +++ b/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java @@ -1,10 +1,9 @@ package com.rbkmoney.anapi.v2.controller; +import com.rbkmoney.anapi.v2.converter.search.request.ParamsToPaymentSearchQueryConverter; +import com.rbkmoney.anapi.v2.exception.BadRequestException; import com.rbkmoney.anapi.v2.service.SearchService; import com.rbkmoney.anapi.v2.util.DamselUtil; -import com.rbkmoney.damsel.domain.LegacyBankCardPaymentSystem; -import com.rbkmoney.damsel.domain.LegacyBankCardTokenProvider; -import com.rbkmoney.damsel.domain.LegacyTerminalPaymentProvider; import com.rbkmoney.magista.*; import com.rbkmoney.openapi.anapi_v2.api.*; import com.rbkmoney.openapi.anapi_v2.model.*; @@ -25,8 +24,12 @@ import java.time.OffsetDateTime; import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.stream.Collectors; +import static com.rbkmoney.anapi.v2.util.CommonUtil.getRequestDeadlineMillis; import static com.rbkmoney.anapi.v2.util.CommonUtil.merge; import static com.rbkmoney.anapi.v2.util.DamselUtil.*; @@ -36,6 +39,7 @@ import static com.rbkmoney.anapi.v2.util.DamselUtil.*; public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesApi, PayoutsApi, RefundsApi { private final SearchService searchService; + private final ParamsToPaymentSearchQueryConverter paymentSearchConverter; @Override public Optional getRequest() { @@ -73,59 +77,57 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Min(1L) @Valid Long paymentAmountTo, @Valid List excludedShops, @Valid String continuationToken) { - //TODO: clarify mapping for paymentInstitutionRealm, xrequestID, xrequestDeadline - PaymentSearchQuery query; - try { - query = new PaymentSearchQuery() - .setCommonSearchQueryParams( - fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), - continuationToken)) - .setExcludedShopIds(excludedShops) - .setExternalId(externalID) - .setInvoiceIds(merge(invoiceID, invoiceIDs)); + //TODO: clarify mapping for paymentInstitutionRealm, xrequestID - PaymentParams paymentParams = new PaymentParams() - .setPaymentTool(paymentMethod != null ? mapToPaymentTool(paymentMethod) : null) - .setPaymentFlow(paymentFlow != null ? mapToInvoicePaymentFlow(paymentFlow) : null) - .setPaymentTerminalProvider( - paymentTerminalProvider != null - ? LegacyTerminalPaymentProvider.valueOf(paymentTerminalProvider) : - null) - .setPaymentTokenProvider( - bankCardTokenProvider != null - ? - LegacyBankCardTokenProvider - .valueOf(bankCardTokenProvider.getValue()) : - null) - .setPaymentEmail(payerEmail) - .setPaymentApprovalCode(approvalCode) - .setPaymentCustomerId(customerID) - .setPaymentFingerprint(payerFingerprint) - .setPaymentFirst6(first6) - .setPaymentLast4(last4) - .setPaymentId(paymentID) - .setPaymentIp(payerIP) - .setPaymentRrn(rrn) - .setPaymentStatus(paymentStatus != null ? getStatus(paymentStatus) : null) - .setPaymentSystem(bankCardPaymentSystem != null - ? LegacyBankCardPaymentSystem.valueOf(bankCardPaymentSystem.getValue()) : - null); - if (paymentAmountFrom != null) { - paymentParams.setPaymentAmountFrom(paymentAmountFrom); + PaymentSearchQuery query = paymentSearchConverter.convert(partyID, + fromTime, + toTime, + limit, + shopID, + shopIDs, + paymentInstitutionRealm, + invoiceIDs, + paymentStatus, paymentFlow, + paymentMethod, + paymentTerminalProvider, + invoiceID, + paymentID, + externalID, + payerEmail, + payerIP, + payerFingerprint, + customerID, + first6, + last4, + rrn, + approvalCode, + bankCardTokenProvider, + bankCardPaymentSystem, + paymentAmountFrom, + paymentAmountTo, + excludedShops, + continuationToken); + try { + InlineResponse20010 response; + if (xrequestDeadline != null) { + response = searchService + .findPayments(query) + .get(getRequestDeadlineMillis(xrequestDeadline), TimeUnit.MILLISECONDS); + } else { + response = searchService.findPayments(query).get(); } - if (paymentAmountTo != null) { - paymentParams.setPaymentAmountTo(paymentAmountTo); - } - query.setPaymentParams(paymentParams); - } catch (IllegalArgumentException e) { + return ResponseEntity.ok(response); + } catch (InterruptedException e) { log.error(e.getMessage()); - return new ResponseEntity( - new DefaultLogicError() - .code(DefaultLogicError.CodeEnum.INVALIDREQUEST) - .message(e.getMessage()), - HttpStatus.BAD_REQUEST); + Thread.currentThread().interrupt(); + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } catch (ExecutionException e) { + log.error(e.getMessage()); + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } catch (TimeoutException e) { + log.error(e.getMessage()); + return new ResponseEntity<>(HttpStatus.REQUEST_TIMEOUT); } - return ResponseEntity.ok(searchService.findPayments(query)); } @GetMapping( @@ -152,7 +154,11 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Valid String continuationToken) { //TODO: clarify mapping for paymentInstitutionRealm, xrequestID, xrequestDeadline, offset ChargebackSearchQuery query; + Long timeoutMillis = null; try { + if (xrequestDeadline != null) { + timeoutMillis = getRequestDeadlineMillis(xrequestDeadline); + } query = new ChargebackSearchQuery() .setCommonSearchQueryParams( fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), @@ -185,7 +191,25 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp .message(e.getMessage()), HttpStatus.BAD_REQUEST); } - return ResponseEntity.ok(searchService.findChargebacks(query)); + try { + InlineResponse2008 response; + if (timeoutMillis != null) { + response = searchService.findChargebacks(query).get(timeoutMillis, TimeUnit.MILLISECONDS); + } else { + response = searchService.findChargebacks(query).get(); + } + return ResponseEntity.ok(response); + } catch (InterruptedException e) { + log.error(e.getMessage()); + Thread.currentThread().interrupt(); + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } catch (ExecutionException e) { + log.error(e.getMessage()); + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } catch (TimeoutException e) { + log.error(e.getMessage()); + return new ResponseEntity<>(HttpStatus.REQUEST_TIMEOUT); + } } @GetMapping( @@ -210,6 +234,17 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Min(1L) @Valid Long invoiceAmountTo, @Valid List excludedShops, @Valid String continuationToken) { + try { + //gives 10 seconds to finish the methods execution + return futureResponse.get(10, TimeUnit.SECONDS); + } catch (TimeoutException te) { + //in case it takes longer we cancel the request and check if the method is not done + if (futureResponse.cancel(true) || !futureResponse.isDone()) { + throw new TestTimeoutException(); + } else { + return futureResponse.get(); + } + } //TODO: clarify mapping for paymentInstitutionRealm, xrequestID, xrequestDeadline, excludedShops InvoiceSearchQuery query; try { @@ -322,16 +357,29 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp return ResponseEntity.ok(searchService.findRefunds(query)); } - @ExceptionHandler({ConstraintViolationException.class}) + @ExceptionHandler({ConstraintViolationException.class, BadRequestException.class, IllegalArgumentException.class}) public ResponseEntity handleConstraintViolation(Exception ex) { - Set> constraintViolations = - ((ConstraintViolationException) ex).getConstraintViolations(); - String errorMessage = - constraintViolations.stream() - .map(violation -> violation.getPropertyPath() + ": " + violation.getMessage()) - .collect(Collectors.joining(", ")); - return new ResponseEntity<>(new DefaultLogicError() - .code(DefaultLogicError.CodeEnum.INVALIDREQUEST) - .message(errorMessage), HttpStatus.BAD_REQUEST); + DefaultLogicError error; + if (ex instanceof ConstraintViolationException) { + Set> constraintViolations = + ((ConstraintViolationException) ex).getConstraintViolations(); + String errorMessage = + constraintViolations.stream() + .map(violation -> violation.getPropertyPath() + ": " + violation.getMessage()) + .collect(Collectors.joining(", ")); + error = new DefaultLogicError() + .code(DefaultLogicError.CodeEnum.INVALIDREQUEST) + .message(errorMessage); + } else if (ex instanceof BadRequestException) { + error = new DefaultLogicError() + .code(((BadRequestException) ex).getErrorCode()) + .message(ex.getMessage()); + } else { + error = new DefaultLogicError() + .code(DefaultLogicError.CodeEnum.INVALIDREQUEST) + .message(ex.getMessage()); + } + + return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST); } } diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPaymentSearchQueryConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPaymentSearchQueryConverter.java new file mode 100644 index 0000000..ac3358e --- /dev/null +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPaymentSearchQueryConverter.java @@ -0,0 +1,93 @@ +package com.rbkmoney.anapi.v2.converter.search.request; + +import com.rbkmoney.damsel.domain.LegacyBankCardPaymentSystem; +import com.rbkmoney.damsel.domain.LegacyBankCardTokenProvider; +import com.rbkmoney.damsel.domain.LegacyTerminalPaymentProvider; +import com.rbkmoney.magista.PaymentParams; +import com.rbkmoney.magista.PaymentSearchQuery; +import com.rbkmoney.openapi.anapi_v2.model.BankCardPaymentSystem; +import com.rbkmoney.openapi.anapi_v2.model.BankCardTokenProvider; +import org.springframework.stereotype.Component; + +import java.time.OffsetDateTime; +import java.util.List; + +import static com.rbkmoney.anapi.v2.util.CommonUtil.merge; +import static com.rbkmoney.anapi.v2.util.DamselUtil.*; + +@Component +public class ParamsToPaymentSearchQueryConverter { + + public PaymentSearchQuery convert(String partyID, + OffsetDateTime fromTime, + OffsetDateTime toTime, + Integer limit, + String shopID, + List shopIDs, + String paymentInstitutionRealm, + List invoiceIDs, + String paymentStatus, String paymentFlow, + String paymentMethod, + String paymentTerminalProvider, + String invoiceID, + String paymentID, + String externalID, + String payerEmail, + String payerIP, + String payerFingerprint, + String customerID, + String first6, + String last4, + String rrn, + String approvalCode, + BankCardTokenProvider bankCardTokenProvider, + BankCardPaymentSystem bankCardPaymentSystem, + Long paymentAmountFrom, + Long paymentAmountTo, + List excludedShops, + String continuationToken) { + + PaymentSearchQuery query = new PaymentSearchQuery() + .setCommonSearchQueryParams( + fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), + continuationToken)) + .setExcludedShopIds(excludedShops) + .setExternalId(externalID) + .setInvoiceIds(merge(invoiceID, invoiceIDs)); + + PaymentParams paymentParams = new PaymentParams() + .setPaymentTool(paymentMethod != null ? mapToPaymentTool(paymentMethod) : null) + .setPaymentFlow(paymentFlow != null ? mapToInvoicePaymentFlow(paymentFlow) : null) + .setPaymentTerminalProvider( + paymentTerminalProvider != null + ? LegacyTerminalPaymentProvider.valueOf(paymentTerminalProvider) : + null) + .setPaymentTokenProvider( + bankCardTokenProvider != null + ? + LegacyBankCardTokenProvider + .valueOf(bankCardTokenProvider.getValue()) : + null) + .setPaymentEmail(payerEmail) + .setPaymentApprovalCode(approvalCode) + .setPaymentCustomerId(customerID) + .setPaymentFingerprint(payerFingerprint) + .setPaymentFirst6(first6) + .setPaymentLast4(last4) + .setPaymentId(paymentID) + .setPaymentIp(payerIP) + .setPaymentRrn(rrn) + .setPaymentStatus(paymentStatus != null ? getStatus(paymentStatus) : null) + .setPaymentSystem(bankCardPaymentSystem != null + ? LegacyBankCardPaymentSystem.valueOf(bankCardPaymentSystem.getValue()) : + null); + if (paymentAmountFrom != null) { + paymentParams.setPaymentAmountFrom(paymentAmountFrom); + } + if (paymentAmountTo != null) { + paymentParams.setPaymentAmountTo(paymentAmountTo); + } + query.setPaymentParams(paymentParams); + return query; + } +} diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverter.java new file mode 100644 index 0000000..0fe19bb --- /dev/null +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverter.java @@ -0,0 +1,47 @@ +package com.rbkmoney.anapi.v2.converter.search.response; + +import com.rbkmoney.geck.common.util.TypeUtil; +import com.rbkmoney.magista.StatPayment; +import com.rbkmoney.openapi.anapi_v2.model.GeoLocationInfo; +import com.rbkmoney.openapi.anapi_v2.model.PaymentFlow; +import com.rbkmoney.openapi.anapi_v2.model.PaymentSearchResult; +import com.rbkmoney.openapi.anapi_v2.model.TransactionInfo; +import org.springframework.stereotype.Component; + +import java.time.ZoneOffset; + +import static com.rbkmoney.anapi.v2.util.OpenApiUtil.fillPaymentStatusInfo; +import static com.rbkmoney.anapi.v2.util.OpenApiUtil.getPayer; + +@Component +public class StatPaymentToPaymentSearchResultConverter { + + public PaymentSearchResult convert(StatPayment payment) { + PaymentSearchResult result = new PaymentSearchResult() + .amount(payment.getAmount()) + .createdAt(TypeUtil.stringToInstant(payment.getCreatedAt()).atOffset(ZoneOffset.UTC)) + .currency(payment.getCurrencySymbolicCode()) + .externalID(payment.getExternalId()) + .fee(payment.getFee()) + .flow(new PaymentFlow() + .type(payment.getFlow().isSetHold() ? PaymentFlow.TypeEnum.PAYMENTFLOWHOLD : + PaymentFlow.TypeEnum.PAYMENTFLOWINSTANT)) + .geoLocationInfo(payment.getLocationInfo() != null ? new GeoLocationInfo() + .cityGeoID(payment.getLocationInfo().getCityGeoId()) + .countryGeoID(payment.getLocationInfo().getCountryGeoId()) + : null) + .id(payment.getId()) + .invoiceID(payment.getInvoiceId()) + .makeRecurrent(payment.isMakeRecurrent()) + .payer(getPayer(payment)) + .shopID(payment.getShopId()) + .shortID(payment.getShortId()) + .transactionInfo(payment.getAdditionalTransactionInfo() != null + ? new TransactionInfo() + .approvalCode(payment.getAdditionalTransactionInfo().getApprovalCode()) + .rrn(payment.getAdditionalTransactionInfo().getRrn()) + : null); + fillPaymentStatusInfo(payment, result); + return result; + } +} diff --git a/src/main/java/com/rbkmoney/anapi/v2/exception/BadRequestException.java b/src/main/java/com/rbkmoney/anapi/v2/exception/BadRequestException.java new file mode 100644 index 0000000..cb941ce --- /dev/null +++ b/src/main/java/com/rbkmoney/anapi/v2/exception/BadRequestException.java @@ -0,0 +1,19 @@ +package com.rbkmoney.anapi.v2.exception; + +import com.rbkmoney.openapi.anapi_v2.model.DefaultLogicError; +import lombok.Getter; + +public class BadRequestException extends IllegalArgumentException { + + @Getter + private DefaultLogicError.CodeEnum errorCode = DefaultLogicError.CodeEnum.INVALIDREQUEST; + + public BadRequestException(String s) { + super(s); + } + + public BadRequestException(String s, DefaultLogicError.CodeEnum errorCode) { + super(s); + this.errorCode = errorCode; + } +} \ No newline at end of file diff --git a/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java b/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java index eed5cd2..d70b30e 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java +++ b/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java @@ -1,16 +1,20 @@ package com.rbkmoney.anapi.v2.service; +import com.rbkmoney.anapi.v2.converter.search.response.StatPaymentToPaymentSearchResultConverter; import com.rbkmoney.anapi.v2.util.OpenApiUtil; import com.rbkmoney.geck.common.util.TypeUtil; import com.rbkmoney.magista.*; import com.rbkmoney.openapi.anapi_v2.model.*; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; +import org.springframework.scheduling.annotation.Async; +import org.springframework.scheduling.annotation.AsyncResult; import org.springframework.stereotype.Service; import java.time.ZoneOffset; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.Future; import java.util.stream.Collectors; import static com.rbkmoney.anapi.v2.util.OpenApiUtil.*; @@ -21,45 +25,22 @@ public class SearchService { private final MerchantStatisticsServiceSrv.Iface magistaClient; + private final StatPaymentToPaymentSearchResultConverter paymentResponseConverter; + + @Async @SneakyThrows - public InlineResponse20010 findPayments(PaymentSearchQuery query) { + public Future findPayments(PaymentSearchQuery query) { StatPaymentResponse magistaResponse = magistaClient.searchPayments(query); - List results = new ArrayList<>(magistaResponse.getPaymentsSize()); - for (StatPayment payment : magistaResponse.getPayments()) { - PaymentSearchResult result = new PaymentSearchResult() - .amount(payment.getAmount()) - .createdAt(TypeUtil.stringToInstant(payment.getCreatedAt()).atOffset(ZoneOffset.UTC)) - .currency(payment.getCurrencySymbolicCode()) - .externalID(payment.getExternalId()) - .fee(payment.getFee()) - .flow(new PaymentFlow() - .type(payment.getFlow().isSetHold() ? PaymentFlow.TypeEnum.PAYMENTFLOWHOLD : - PaymentFlow.TypeEnum.PAYMENTFLOWINSTANT)) - .geoLocationInfo(payment.getLocationInfo() != null ? new GeoLocationInfo() - .cityGeoID(payment.getLocationInfo().getCityGeoId()) - .countryGeoID(payment.getLocationInfo().getCountryGeoId()) - : null) - .id(payment.getId()) - .invoiceID(payment.getInvoiceId()) - .makeRecurrent(payment.isMakeRecurrent()) - .payer(getPayer(payment)) - .shopID(payment.getShopId()) - .shortID(payment.getShortId()) - .transactionInfo(payment.getAdditionalTransactionInfo() != null - ? new TransactionInfo() - .approvalCode(payment.getAdditionalTransactionInfo().getApprovalCode()) - .rrn(payment.getAdditionalTransactionInfo().getRrn()) - : null); - fillPaymentStatusInfo(payment, result); - results.add(result); - } - return new InlineResponse20010() - .result(results) - .continuationToken(magistaResponse.getContinuationToken()); + return new AsyncResult<>(new InlineResponse20010() + .result(magistaResponse.getPayments().stream() + .map(paymentResponseConverter::convert) + .collect(Collectors.toList())) + .continuationToken(magistaResponse.getContinuationToken())); } + @Async @SneakyThrows - public InlineResponse2008 findChargebacks(ChargebackSearchQuery query) { + public Future findChargebacks(ChargebackSearchQuery query) { StatChargebackResponse magistaResponse = magistaClient.searchChargebacks(query); List results = new ArrayList<>(magistaResponse.getChargebacksSize()); for (StatChargeback chargeback : magistaResponse.getChargebacks()) { @@ -78,14 +59,15 @@ public class SearchService { .bodyCurrency(chargeback.getCurrencyCode().getSymbolicCode()); results.add(result); } - return new InlineResponse2008() + return new AsyncResult<>(new InlineResponse2008() .result(results) - .continuationToken(magistaResponse.getContinuationToken()); + .continuationToken(magistaResponse.getContinuationToken())); } + @Async @SneakyThrows - public InlineResponse2009 findInvoices(InvoiceSearchQuery query) { + public Future findInvoices(InvoiceSearchQuery query) { StatInvoiceResponse magistaResponse = magistaClient.searchInvoices(query); List results = new ArrayList<>(magistaResponse.getInvoicesSize()); for (StatInvoice invoice : magistaResponse.getInvoices()) { @@ -110,13 +92,14 @@ public class SearchService { .status(OpenApiUtil.mapToInvoiceStatus(invoice.getStatus())); results.add(result); } - return new InlineResponse2009() + return new AsyncResult<>(new InlineResponse2009() .result(results) - .continuationToken(magistaResponse.getContinuationToken()); + .continuationToken(magistaResponse.getContinuationToken())); } + @Async @SneakyThrows - public InlineResponse20011 findPayouts(PayoutSearchQuery query) { + public Future findPayouts(PayoutSearchQuery query) { StatPayoutResponse magistaResponse = magistaClient.searchPayouts(query); List results = new ArrayList<>(magistaResponse.getPayoutsSize()); for (StatPayout payout : magistaResponse.getPayouts()) { @@ -134,13 +117,14 @@ public class SearchService { null); results.add(result); } - return new InlineResponse20011() + return new AsyncResult<>(new InlineResponse20011() .result(results) - .continuationToken(magistaResponse.getContinuationToken()); + .continuationToken(magistaResponse.getContinuationToken())); } + @Async @SneakyThrows - public InlineResponse20012 findRefunds(RefundSearchQuery query) { + public Future findRefunds(RefundSearchQuery query) { StatRefundResponse magistaResponse = magistaClient.searchRefunds(query); List results = new ArrayList<>(magistaResponse.getRefundsSize()); for (StatRefund refund : magistaResponse.getRefunds()) { @@ -163,9 +147,9 @@ public class SearchService { .reason(refund.getReason()); results.add(result); } - return new InlineResponse20012() + return new AsyncResult<>(new InlineResponse20012() .result(results) - .continuationToken(magistaResponse.getContinuationToken()); + .continuationToken(magistaResponse.getContinuationToken())); } } diff --git a/src/main/java/com/rbkmoney/anapi/v2/util/CommonUtil.java b/src/main/java/com/rbkmoney/anapi/v2/util/CommonUtil.java index a4a9c3d..6bc95b2 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/util/CommonUtil.java +++ b/src/main/java/com/rbkmoney/anapi/v2/util/CommonUtil.java @@ -3,12 +3,19 @@ package com.rbkmoney.anapi.v2.util; import lombok.experimental.UtilityClass; import javax.annotation.Nullable; +import javax.validation.constraints.NotNull; +import java.time.Duration; +import java.time.Instant; +import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.List; +import java.util.regex.Pattern; @UtilityClass public class CommonUtil { + private static final Pattern deadlinePattern = Pattern.compile("\\d+(?:\\.\\d+)?(?:ms|s|m)"); + public static List merge(@Nullable String id, @Nullable List ids) { if (id != null) { if (ids == null) { @@ -19,4 +26,20 @@ public class CommonUtil { return ids; } + public static long getRequestDeadlineMillis(@NotNull String requestDeadLine) { + if (deadlinePattern.matcher(requestDeadLine).matches()) { + //150000ms, 540s, 3.5m, etc + if (requestDeadLine.endsWith("ms")) { + return Long.parseLong(requestDeadLine.substring(0, requestDeadLine.length() - 3)); + } + String duration = "PT" + requestDeadLine.toUpperCase(); + return Duration.parse(duration).toMillis(); + } + + //ISO 8601 + OffsetDateTime odt = OffsetDateTime.parse(requestDeadLine); + return odt.toInstant().toEpochMilli() - Instant.now().toEpochMilli(); + + } + } diff --git a/src/main/java/com/rbkmoney/anapi/v2/util/DamselUtil.java b/src/main/java/com/rbkmoney/anapi/v2/util/DamselUtil.java index ff0cf50..126e358 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/util/DamselUtil.java +++ b/src/main/java/com/rbkmoney/anapi/v2/util/DamselUtil.java @@ -1,5 +1,6 @@ package com.rbkmoney.anapi.v2.util; +import com.rbkmoney.anapi.v2.exception.BadRequestException; import com.rbkmoney.damsel.domain.*; import com.rbkmoney.geck.common.util.TypeUtil; import com.rbkmoney.magista.CommonSearchQueryParams; @@ -17,7 +18,7 @@ public class DamselUtil { switch (paymentMethod) { case "bankCard" -> paymentTool.setBankCard(new BankCard()); case "paymentTerminal" -> paymentTool.setPaymentTerminal(new PaymentTerminal()); - default -> throw new IllegalArgumentException( + default -> throw new BadRequestException( String.format("Payment method %s cannot be processed", paymentMethod)); } @@ -29,7 +30,7 @@ public class DamselUtil { switch (paymentFlow) { case "instant" -> invoicePaymentFlow.setInstant(new InvoicePaymentFlowInstant()); case "hold" -> invoicePaymentFlow.setHold(new InvoicePaymentFlowHold()); - default -> throw new IllegalArgumentException( + default -> throw new BadRequestException( String.format("Payment flow %s cannot be processed", paymentFlow)); } return invoicePaymentFlow; @@ -58,7 +59,7 @@ public class DamselUtil { case CANCELLED -> invoicePaymentStatus.setCancelled(new InvoicePaymentCancelled()); case REFUNDED -> invoicePaymentStatus.setRefunded(new InvoicePaymentRefunded()); case FAILED -> invoicePaymentStatus.setFailed(new InvoicePaymentFailed()); - default -> throw new IllegalArgumentException( + default -> throw new BadRequestException( String.format("Payment status %s cannot be processed", paymentStatus)); } return invoicePaymentStatus; @@ -71,7 +72,7 @@ public class DamselUtil { case CHARGEBACK -> damselStage.setChargeback(new InvoicePaymentChargebackStageChargeback()); case PRE_ARBITRATION -> damselStage.setPreArbitration(new InvoicePaymentChargebackStagePreArbitration()); case ARBITRATION -> damselStage.setArbitration(new InvoicePaymentChargebackStageArbitration()); - default -> throw new IllegalArgumentException( + default -> throw new BadRequestException( String.format("Chargeback stage %s cannot be processed", chargebackStage)); } @@ -86,7 +87,7 @@ public class DamselUtil { case ACCEPTED -> damselStatus.setAccepted(new InvoicePaymentChargebackAccepted()); case REJECTED -> damselStatus.setRejected(new InvoicePaymentChargebackRejected()); case CANCELLED -> damselStatus.setCancelled(new InvoicePaymentChargebackCancelled()); - default -> throw new IllegalArgumentException( + default -> throw new BadRequestException( String.format("Chargeback status %s cannot be processed", chargebackStatus)); } @@ -103,7 +104,7 @@ public class DamselUtil { .setAuthorisation(new InvoicePaymentChargebackCategoryAuthorisation()); case PROCESSING_ERROR -> damselCategory .setProcessingError(new InvoicePaymentChargebackCategoryProcessingError()); - default -> throw new IllegalArgumentException( + default -> throw new BadRequestException( String.format("Chargeback category %s cannot be processed", chargebackCategory)); } @@ -118,7 +119,7 @@ public class DamselUtil { case "Wallet" -> payoutToolInfo.setWalletInfo(new WalletInfo()); case "PaymentInstitutionAccount" -> payoutToolInfo .setPaymentInstitutionAccount(new PaymentInstitutionAccount()); - default -> throw new IllegalArgumentException( + default -> throw new BadRequestException( String.format("PayoutToolType %s cannot be processed", payoutToolType)); } @@ -131,7 +132,7 @@ public class DamselUtil { case PENDING -> invoicePaymentRefundStatus.setPending(new InvoicePaymentRefundPending()); case SUCCEEDED -> invoicePaymentRefundStatus.setSucceeded(new InvoicePaymentRefundSucceeded()); case FAILED -> invoicePaymentRefundStatus.setFailed(new InvoicePaymentRefundFailed()); - default -> throw new IllegalArgumentException( + default -> throw new BadRequestException( String.format("Refund status %s cannot be processed", refundStatus)); } return invoicePaymentRefundStatus; From 465f7d9aa08f64563ebaffe90af4ce91944591eb Mon Sep 17 00:00:00 2001 From: echerniak Date: Thu, 16 Sep 2021 17:09:31 +0300 Subject: [PATCH 12/37] WIP. Controller refactoring --- .../anapi/v2/controller/SearchController.java | 232 ++++++++++-------- ...aramsToChargebackSearchQueryConverter.java | 57 +++++ .../ParamsToInvoiceSearchQueryConverter.java | 45 ++++ .../ParamsToPaymentSearchQueryConverter.java | 2 +- .../ParamsToPayoutSearchQueryConverter.java | 36 +++ .../ParamsToRefundSearchQueryConverter.java | 43 ++++ 6 files changed, 306 insertions(+), 109 deletions(-) create mode 100644 src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToChargebackSearchQueryConverter.java create mode 100644 src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToInvoiceSearchQueryConverter.java create mode 100644 src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPayoutSearchQueryConverter.java create mode 100644 src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToRefundSearchQueryConverter.java diff --git a/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java b/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java index cb8edc8..2cc165b 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java +++ b/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java @@ -1,9 +1,8 @@ package com.rbkmoney.anapi.v2.controller; -import com.rbkmoney.anapi.v2.converter.search.request.ParamsToPaymentSearchQueryConverter; +import com.rbkmoney.anapi.v2.converter.search.request.*; import com.rbkmoney.anapi.v2.exception.BadRequestException; import com.rbkmoney.anapi.v2.service.SearchService; -import com.rbkmoney.anapi.v2.util.DamselUtil; import com.rbkmoney.magista.*; import com.rbkmoney.openapi.anapi_v2.api.*; import com.rbkmoney.openapi.anapi_v2.model.*; @@ -30,8 +29,6 @@ import java.util.concurrent.TimeoutException; import java.util.stream.Collectors; import static com.rbkmoney.anapi.v2.util.CommonUtil.getRequestDeadlineMillis; -import static com.rbkmoney.anapi.v2.util.CommonUtil.merge; -import static com.rbkmoney.anapi.v2.util.DamselUtil.*; @Slf4j @Controller @@ -40,6 +37,10 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp private final SearchService searchService; private final ParamsToPaymentSearchQueryConverter paymentSearchConverter; + private final ParamsToChargebackSearchQueryConverter chargebackSearchConverter; + private final ParamsToInvoiceSearchQueryConverter invoiceSearchConverter; + private final ParamsToPayoutSearchQueryConverter payoutSearchConverter; + private final ParamsToRefundSearchQueryConverter refundSearchConverter; @Override public Optional getRequest() { @@ -77,8 +78,6 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Min(1L) @Valid Long paymentAmountTo, @Valid List excludedShops, @Valid String continuationToken) { - //TODO: clarify mapping for paymentInstitutionRealm, xrequestID - PaymentSearchQuery query = paymentSearchConverter.convert(partyID, fromTime, toTime, @@ -153,48 +152,27 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Valid List chargebackCategories, @Valid String continuationToken) { //TODO: clarify mapping for paymentInstitutionRealm, xrequestID, xrequestDeadline, offset - ChargebackSearchQuery query; - Long timeoutMillis = null; - try { - if (xrequestDeadline != null) { - timeoutMillis = getRequestDeadlineMillis(xrequestDeadline); - } - query = new ChargebackSearchQuery() - .setCommonSearchQueryParams( - fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), - continuationToken)) - .setInvoiceIds(invoiceID != null ? List.of(invoiceID) : null) - .setPaymentId(paymentID) - .setChargebackId(chargebackID) - .setChargebackStatuses(chargebackStatuses != null - ? chargebackStatuses.stream() - .map(DamselUtil::mapToDamselStatus) - .collect(Collectors.toList()) - : null - ) - .setChargebackStages(chargebackStages != null - ? chargebackStages.stream() - .map(DamselUtil::mapToDamselStage) - .collect(Collectors.toList()) - : null - ) - .setChargebackCategories(chargebackCategories != null - ? chargebackCategories.stream() - .map(DamselUtil::mapToDamselCategory) - .collect(Collectors.toList()) - : null); - } catch (IllegalArgumentException e) { - log.error(e.getMessage()); - return new ResponseEntity( - new DefaultLogicError() - .code(DefaultLogicError.CodeEnum.INVALIDREQUEST) - .message(e.getMessage()), - HttpStatus.BAD_REQUEST); - } + ChargebackSearchQuery query = chargebackSearchConverter.convert(partyID, + fromTime, + toTime, + limit, + shopID, + shopIDs, + paymentInstitutionRealm, + offset, + invoiceID, + paymentID, + chargebackID, + chargebackStatuses, + chargebackStages, + chargebackCategories, + continuationToken); try { InlineResponse2008 response; - if (timeoutMillis != null) { - response = searchService.findChargebacks(query).get(timeoutMillis, TimeUnit.MILLISECONDS); + if (xrequestDeadline != null) { + response = searchService + .findChargebacks(query) + .get(getRequestDeadlineMillis(xrequestDeadline), TimeUnit.MILLISECONDS); } else { response = searchService.findChargebacks(query).get(); } @@ -234,41 +212,44 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Min(1L) @Valid Long invoiceAmountTo, @Valid List excludedShops, @Valid String continuationToken) { - try { - //gives 10 seconds to finish the methods execution - return futureResponse.get(10, TimeUnit.SECONDS); - } catch (TimeoutException te) { - //in case it takes longer we cancel the request and check if the method is not done - if (futureResponse.cancel(true) || !futureResponse.isDone()) { - throw new TestTimeoutException(); - } else { - return futureResponse.get(); - } - } //TODO: clarify mapping for paymentInstitutionRealm, xrequestID, xrequestDeadline, excludedShops - InvoiceSearchQuery query; + InvoiceSearchQuery query = invoiceSearchConverter.convert(partyID, + fromTime, + toTime, + limit, + shopID, + shopIDs, + paymentInstitutionRealm, + invoiceIDs, + invoiceStatus, + invoiceID, + externalID, + invoiceAmountFrom, + invoiceAmountTo, + excludedShops, + continuationToken); + try { - query = new InvoiceSearchQuery() - .setCommonSearchQueryParams( - fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), - continuationToken)) - .setPaymentParams( - new PaymentParams() - .setPaymentAmountFrom(invoiceAmountFrom) - .setPaymentAmountTo(invoiceAmountTo) - .setPaymentStatus(invoiceStatus != null ? getStatus(invoiceStatus) : null) - ) - .setInvoiceIds(merge(invoiceID, invoiceIDs)) - .setExternalId(externalID); - } catch (IllegalArgumentException e) { + InlineResponse2009 response; + if (xrequestDeadline != null) { + response = searchService + .findInvoices(query) + .get(getRequestDeadlineMillis(xrequestDeadline), TimeUnit.MILLISECONDS); + } else { + response = searchService.findInvoices(query).get(); + } + return ResponseEntity.ok(response); + } catch (InterruptedException e) { log.error(e.getMessage()); - return new ResponseEntity( - new DefaultLogicError() - .code(DefaultLogicError.CodeEnum.INVALIDREQUEST) - .message(e.getMessage()), - HttpStatus.BAD_REQUEST); + Thread.currentThread().interrupt(); + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } catch (ExecutionException e) { + log.error(e.getMessage()); + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } catch (TimeoutException e) { + log.error(e.getMessage()); + return new ResponseEntity<>(HttpStatus.REQUEST_TIMEOUT); } - return ResponseEntity.ok(searchService.findInvoices(query)); } @GetMapping( @@ -292,23 +273,40 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Valid String continuationToken) { //TODO: clarify mapping for paymentInstitutionRealm, xrequestID, xrequestDeadline, excludedShops, //offset + setStatuses - PayoutSearchQuery query; + PayoutSearchQuery query = payoutSearchConverter.convert(partyID, + fromTime, + toTime, + limit, + shopID, + shopIDs, + paymentInstitutionRealm, + offset, + payoutID, + payoutToolType, + excludedShops, + continuationToken); + try { - query = new PayoutSearchQuery() - .setCommonSearchQueryParams( - fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), - continuationToken)) - .setPayoutId(payoutID) - .setPayoutType(payoutToolType != null ? mapToDamselPayoutToolInfo(payoutToolType) : null); - } catch (IllegalArgumentException e) { + InlineResponse20011 response; + if (xrequestDeadline != null) { + response = searchService + .findPayouts(query) + .get(getRequestDeadlineMillis(xrequestDeadline), TimeUnit.MILLISECONDS); + } else { + response = searchService.findPayouts(query).get(); + } + return ResponseEntity.ok(response); + } catch (InterruptedException e) { log.error(e.getMessage()); - return new ResponseEntity( - new DefaultLogicError() - .code(DefaultLogicError.CodeEnum.INVALIDREQUEST) - .message(e.getMessage()), - HttpStatus.BAD_REQUEST); + Thread.currentThread().interrupt(); + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } catch (ExecutionException e) { + log.error(e.getMessage()); + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } catch (TimeoutException e) { + log.error(e.getMessage()); + return new ResponseEntity<>(HttpStatus.REQUEST_TIMEOUT); } - return ResponseEntity.ok(searchService.findPayouts(query)); } @GetMapping( @@ -335,26 +333,44 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Valid List excludedShops, @Valid String continuationToken) { //TODO: clarify mapping for paymentInstitutionRealm, xrequestID, xrequestDeadline, excludedShops, offset - RefundSearchQuery query; + RefundSearchQuery query = refundSearchConverter.convert(partyID, + fromTime, + toTime, + limit, + shopID, + shopIDs, + paymentInstitutionRealm, + offset, + invoiceIDs, + invoiceID, + paymentID, + refundID, + externalID, + refundStatus, + excludedShops, + continuationToken); + try { - query = new RefundSearchQuery() - .setCommonSearchQueryParams( - fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), - continuationToken)) - .setRefundStatus(refundStatus != null ? getRefundStatus(refundStatus) : null) - .setInvoiceIds(merge(invoiceID, invoiceIDs)) - .setExternalId(externalID) - .setPaymentId(paymentID) - .setRefundId(refundID); - } catch (IllegalArgumentException e) { + InlineResponse20012 response; + if (xrequestDeadline != null) { + response = searchService + .findRefunds(query) + .get(getRequestDeadlineMillis(xrequestDeadline), TimeUnit.MILLISECONDS); + } else { + response = searchService.findRefunds(query).get(); + } + return ResponseEntity.ok(response); + } catch (InterruptedException e) { log.error(e.getMessage()); - return new ResponseEntity( - new DefaultLogicError() - .code(DefaultLogicError.CodeEnum.INVALIDREQUEST) - .message(e.getMessage()), - HttpStatus.BAD_REQUEST); + Thread.currentThread().interrupt(); + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } catch (ExecutionException e) { + log.error(e.getMessage()); + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } catch (TimeoutException e) { + log.error(e.getMessage()); + return new ResponseEntity<>(HttpStatus.REQUEST_TIMEOUT); } - return ResponseEntity.ok(searchService.findRefunds(query)); } @ExceptionHandler({ConstraintViolationException.class, BadRequestException.class, IllegalArgumentException.class}) diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToChargebackSearchQueryConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToChargebackSearchQueryConverter.java new file mode 100644 index 0000000..0980b52 --- /dev/null +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToChargebackSearchQueryConverter.java @@ -0,0 +1,57 @@ +package com.rbkmoney.anapi.v2.converter.search.request; + +import com.rbkmoney.anapi.v2.util.DamselUtil; +import com.rbkmoney.magista.ChargebackSearchQuery; +import org.springframework.stereotype.Component; + +import java.time.OffsetDateTime; +import java.util.List; +import java.util.stream.Collectors; + +import static com.rbkmoney.anapi.v2.util.CommonUtil.merge; +import static com.rbkmoney.anapi.v2.util.DamselUtil.fillCommonParams; + +@Component +public class ParamsToChargebackSearchQueryConverter { + + public ChargebackSearchQuery convert(String partyID, + OffsetDateTime fromTime, + OffsetDateTime toTime, + Integer limit, + String shopID, + List shopIDs, + String paymentInstitutionRealm, + Integer offset, + String invoiceID, + String paymentID, + String chargebackID, + List chargebackStatuses, + List chargebackStages, + List chargebackCategories, + String continuationToken) { + return new ChargebackSearchQuery() + .setCommonSearchQueryParams( + fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), + continuationToken)) + .setInvoiceIds(invoiceID != null ? List.of(invoiceID) : null) + .setPaymentId(paymentID) + .setChargebackId(chargebackID) + .setChargebackStatuses(chargebackStatuses != null + ? chargebackStatuses.stream() + .map(DamselUtil::mapToDamselStatus) + .collect(Collectors.toList()) + : null + ) + .setChargebackStages(chargebackStages != null + ? chargebackStages.stream() + .map(DamselUtil::mapToDamselStage) + .collect(Collectors.toList()) + : null + ) + .setChargebackCategories(chargebackCategories != null + ? chargebackCategories.stream() + .map(DamselUtil::mapToDamselCategory) + .collect(Collectors.toList()) + : null); + } +} diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToInvoiceSearchQueryConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToInvoiceSearchQueryConverter.java new file mode 100644 index 0000000..7b7fb58 --- /dev/null +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToInvoiceSearchQueryConverter.java @@ -0,0 +1,45 @@ +package com.rbkmoney.anapi.v2.converter.search.request; + +import com.rbkmoney.magista.InvoiceSearchQuery; +import com.rbkmoney.magista.PaymentParams; +import org.springframework.stereotype.Component; + +import java.time.OffsetDateTime; +import java.util.List; + +import static com.rbkmoney.anapi.v2.util.CommonUtil.merge; +import static com.rbkmoney.anapi.v2.util.DamselUtil.fillCommonParams; +import static com.rbkmoney.anapi.v2.util.DamselUtil.getStatus; + +@Component +public class ParamsToInvoiceSearchQueryConverter { + + public InvoiceSearchQuery convert(String partyID, + OffsetDateTime fromTime, + OffsetDateTime toTime, + Integer limit, + String shopID, + List shopIDs, + String paymentInstitutionRealm, + List invoiceIDs, + String invoiceStatus, + String invoiceID, + String externalID, + Long invoiceAmountFrom, + Long invoiceAmountTo, + List excludedShops, + String continuationToken) { + return new InvoiceSearchQuery() + .setCommonSearchQueryParams( + fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), + continuationToken)) + .setPaymentParams( + new PaymentParams() + .setPaymentAmountFrom(invoiceAmountFrom) + .setPaymentAmountTo(invoiceAmountTo) + .setPaymentStatus(invoiceStatus != null ? getStatus(invoiceStatus) : null) + ) + .setInvoiceIds(merge(invoiceID, invoiceIDs)) + .setExternalId(externalID); + } +} diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPaymentSearchQueryConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPaymentSearchQueryConverter.java index ac3358e..07791bc 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPaymentSearchQueryConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPaymentSearchQueryConverter.java @@ -46,7 +46,7 @@ public class ParamsToPaymentSearchQueryConverter { Long paymentAmountTo, List excludedShops, String continuationToken) { - +//TODO: clarify mapping for paymentInstitutionRealm PaymentSearchQuery query = new PaymentSearchQuery() .setCommonSearchQueryParams( fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPayoutSearchQueryConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPayoutSearchQueryConverter.java new file mode 100644 index 0000000..a731bb0 --- /dev/null +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPayoutSearchQueryConverter.java @@ -0,0 +1,36 @@ +package com.rbkmoney.anapi.v2.converter.search.request; + +import com.rbkmoney.magista.PayoutSearchQuery; +import org.springframework.stereotype.Component; + +import java.time.OffsetDateTime; +import java.util.List; + +import static com.rbkmoney.anapi.v2.util.CommonUtil.merge; +import static com.rbkmoney.anapi.v2.util.DamselUtil.fillCommonParams; +import static com.rbkmoney.anapi.v2.util.DamselUtil.mapToDamselPayoutToolInfo; + +@Component +public class ParamsToPayoutSearchQueryConverter { + + public PayoutSearchQuery convert(String partyID, + OffsetDateTime fromTime, + OffsetDateTime toTime, + Integer limit, + String shopID, + List shopIDs, + String paymentInstitutionRealm, + Integer offset, + String payoutID, + String payoutToolType, + List excludedShops, + String continuationToken) { + + return new PayoutSearchQuery() + .setCommonSearchQueryParams( + fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), + continuationToken)) + .setPayoutId(payoutID) + .setPayoutType(payoutToolType != null ? mapToDamselPayoutToolInfo(payoutToolType) : null); + } +} diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToRefundSearchQueryConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToRefundSearchQueryConverter.java new file mode 100644 index 0000000..2f85123 --- /dev/null +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToRefundSearchQueryConverter.java @@ -0,0 +1,43 @@ +package com.rbkmoney.anapi.v2.converter.search.request; + +import com.rbkmoney.magista.RefundSearchQuery; +import org.springframework.stereotype.Component; + +import java.time.OffsetDateTime; +import java.util.List; + +import static com.rbkmoney.anapi.v2.util.CommonUtil.merge; +import static com.rbkmoney.anapi.v2.util.DamselUtil.fillCommonParams; +import static com.rbkmoney.anapi.v2.util.DamselUtil.getRefundStatus; + +@Component +public class ParamsToRefundSearchQueryConverter { + + public RefundSearchQuery convert(String partyID, + OffsetDateTime fromTime, + OffsetDateTime toTime, + Integer limit, + String shopID, + List shopIDs, + String paymentInstitutionRealm, + Integer offset, + List invoiceIDs, + String invoiceID, + String paymentID, + String refundID, + String externalID, + String refundStatus, + List excludedShops, + String continuationToken) { + return new RefundSearchQuery() + .setCommonSearchQueryParams( + fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), + continuationToken)) + .setRefundStatus(refundStatus != null ? getRefundStatus(refundStatus) : null) + .setInvoiceIds(merge(invoiceID, invoiceIDs)) + .setExternalId(externalID) + .setPaymentId(paymentID) + .setRefundId(refundID); + } + +} From d41f2a469993ae4938b67c308856f304f43fda82 Mon Sep 17 00:00:00 2001 From: echerniak Date: Thu, 16 Sep 2021 17:52:44 +0300 Subject: [PATCH 13/37] WIP. Converters refactoring + error handling --- .../anapi/v2/controller/SearchController.java | 165 +++++++----------- .../StatChargebackToChargebackConverter.java | 32 ++++ .../StatInvoiceToInvoiceConverter.java | 38 ++++ .../response/StatPayoutToPayoutConverter.java | 31 ++++ ...atRefundToRefundSearchResultConverter.java | 35 ++++ .../anapi/v2/service/SearchService.java | 107 ++---------- 6 files changed, 212 insertions(+), 196 deletions(-) create mode 100644 src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatChargebackToChargebackConverter.java create mode 100644 src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatInvoiceToInvoiceConverter.java create mode 100644 src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPayoutToPayoutConverter.java create mode 100644 src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatRefundToRefundSearchResultConverter.java diff --git a/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java b/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java index 2cc165b..71c8353 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java +++ b/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java @@ -7,6 +7,7 @@ import com.rbkmoney.magista.*; import com.rbkmoney.openapi.anapi_v2.api.*; import com.rbkmoney.openapi.anapi_v2.model.*; import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -47,6 +48,7 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp return PaymentsApi.super.getRequest(); } + @SneakyThrows @Override public ResponseEntity searchPayments(String xrequestID, @NotNull @Size(min = 1, max = 40) @Valid String partyID, @@ -106,29 +108,18 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp paymentAmountTo, excludedShops, continuationToken); - try { - InlineResponse20010 response; - if (xrequestDeadline != null) { - response = searchService - .findPayments(query) - .get(getRequestDeadlineMillis(xrequestDeadline), TimeUnit.MILLISECONDS); - } else { - response = searchService.findPayments(query).get(); - } - return ResponseEntity.ok(response); - } catch (InterruptedException e) { - log.error(e.getMessage()); - Thread.currentThread().interrupt(); - return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); - } catch (ExecutionException e) { - log.error(e.getMessage()); - return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); - } catch (TimeoutException e) { - log.error(e.getMessage()); - return new ResponseEntity<>(HttpStatus.REQUEST_TIMEOUT); + InlineResponse20010 response; + if (xrequestDeadline != null) { + response = searchService + .findPayments(query) + .get(getRequestDeadlineMillis(xrequestDeadline), TimeUnit.MILLISECONDS); + } else { + response = searchService.findPayments(query).get(); } + return ResponseEntity.ok(response); } + @SneakyThrows @GetMapping( value = "/chargebacks", produces = {"application/json; charset=utf-8"} @@ -167,29 +158,18 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp chargebackStages, chargebackCategories, continuationToken); - try { - InlineResponse2008 response; - if (xrequestDeadline != null) { - response = searchService - .findChargebacks(query) - .get(getRequestDeadlineMillis(xrequestDeadline), TimeUnit.MILLISECONDS); - } else { - response = searchService.findChargebacks(query).get(); - } - return ResponseEntity.ok(response); - } catch (InterruptedException e) { - log.error(e.getMessage()); - Thread.currentThread().interrupt(); - return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); - } catch (ExecutionException e) { - log.error(e.getMessage()); - return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); - } catch (TimeoutException e) { - log.error(e.getMessage()); - return new ResponseEntity<>(HttpStatus.REQUEST_TIMEOUT); + InlineResponse2008 response; + if (xrequestDeadline != null) { + response = searchService + .findChargebacks(query) + .get(getRequestDeadlineMillis(xrequestDeadline), TimeUnit.MILLISECONDS); + } else { + response = searchService.findChargebacks(query).get(); } + return ResponseEntity.ok(response); } + @SneakyThrows @GetMapping( value = "/invoices", produces = {"application/json; charset=utf-8"} @@ -228,30 +208,18 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp invoiceAmountTo, excludedShops, continuationToken); - - try { - InlineResponse2009 response; - if (xrequestDeadline != null) { - response = searchService - .findInvoices(query) - .get(getRequestDeadlineMillis(xrequestDeadline), TimeUnit.MILLISECONDS); - } else { - response = searchService.findInvoices(query).get(); - } - return ResponseEntity.ok(response); - } catch (InterruptedException e) { - log.error(e.getMessage()); - Thread.currentThread().interrupt(); - return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); - } catch (ExecutionException e) { - log.error(e.getMessage()); - return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); - } catch (TimeoutException e) { - log.error(e.getMessage()); - return new ResponseEntity<>(HttpStatus.REQUEST_TIMEOUT); + InlineResponse2009 response; + if (xrequestDeadline != null) { + response = searchService + .findInvoices(query) + .get(getRequestDeadlineMillis(xrequestDeadline), TimeUnit.MILLISECONDS); + } else { + response = searchService.findInvoices(query).get(); } + return ResponseEntity.ok(response); } + @SneakyThrows @GetMapping( value = "/payouts", produces = {"application/json; charset=utf-8"} @@ -285,30 +253,18 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp payoutToolType, excludedShops, continuationToken); - - try { - InlineResponse20011 response; - if (xrequestDeadline != null) { - response = searchService - .findPayouts(query) - .get(getRequestDeadlineMillis(xrequestDeadline), TimeUnit.MILLISECONDS); - } else { - response = searchService.findPayouts(query).get(); - } - return ResponseEntity.ok(response); - } catch (InterruptedException e) { - log.error(e.getMessage()); - Thread.currentThread().interrupt(); - return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); - } catch (ExecutionException e) { - log.error(e.getMessage()); - return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); - } catch (TimeoutException e) { - log.error(e.getMessage()); - return new ResponseEntity<>(HttpStatus.REQUEST_TIMEOUT); + InlineResponse20011 response; + if (xrequestDeadline != null) { + response = searchService + .findPayouts(query) + .get(getRequestDeadlineMillis(xrequestDeadline), TimeUnit.MILLISECONDS); + } else { + response = searchService.findPayouts(query).get(); } + return ResponseEntity.ok(response); } + @SneakyThrows @GetMapping( value = "/refunds", produces = {"application/json; charset=utf-8"} @@ -350,33 +306,23 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp excludedShops, continuationToken); - try { - InlineResponse20012 response; - if (xrequestDeadline != null) { - response = searchService - .findRefunds(query) - .get(getRequestDeadlineMillis(xrequestDeadline), TimeUnit.MILLISECONDS); - } else { - response = searchService.findRefunds(query).get(); - } - return ResponseEntity.ok(response); - } catch (InterruptedException e) { - log.error(e.getMessage()); - Thread.currentThread().interrupt(); - return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); - } catch (ExecutionException e) { - log.error(e.getMessage()); - return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); - } catch (TimeoutException e) { - log.error(e.getMessage()); - return new ResponseEntity<>(HttpStatus.REQUEST_TIMEOUT); + InlineResponse20012 response; + if (xrequestDeadline != null) { + response = searchService + .findRefunds(query) + .get(getRequestDeadlineMillis(xrequestDeadline), TimeUnit.MILLISECONDS); + } else { + response = searchService.findRefunds(query).get(); } + return ResponseEntity.ok(response); } - @ExceptionHandler({ConstraintViolationException.class, BadRequestException.class, IllegalArgumentException.class}) + @ExceptionHandler({ConstraintViolationException.class, BadRequestException.class, IllegalArgumentException.class, + InterruptedException.class, ExecutionException.class, TimeoutException.class}) public ResponseEntity handleConstraintViolation(Exception ex) { DefaultLogicError error; if (ex instanceof ConstraintViolationException) { + log.warn("Invalid request: ", ex); Set> constraintViolations = ((ConstraintViolationException) ex).getConstraintViolations(); String errorMessage = @@ -387,13 +333,20 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp .code(DefaultLogicError.CodeEnum.INVALIDREQUEST) .message(errorMessage); } else if (ex instanceof BadRequestException) { + log.warn("Invalid request: ", ex); error = new DefaultLogicError() .code(((BadRequestException) ex).getErrorCode()) .message(ex.getMessage()); + } else if (ex instanceof InterruptedException) { + log.error("Internal error: ", ex); + Thread.currentThread().interrupt(); + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } else if (ex instanceof TimeoutException) { + log.error("Request timeout: ", ex); + return new ResponseEntity<>(HttpStatus.GATEWAY_TIMEOUT); } else { - error = new DefaultLogicError() - .code(DefaultLogicError.CodeEnum.INVALIDREQUEST) - .message(ex.getMessage()); + log.error("Internal error: ", ex); + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST); diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatChargebackToChargebackConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatChargebackToChargebackConverter.java new file mode 100644 index 0000000..b1377da --- /dev/null +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatChargebackToChargebackConverter.java @@ -0,0 +1,32 @@ +package com.rbkmoney.anapi.v2.converter.search.response; + +import com.rbkmoney.geck.common.util.TypeUtil; +import com.rbkmoney.magista.StatChargeback; +import com.rbkmoney.openapi.anapi_v2.model.Chargeback; +import com.rbkmoney.openapi.anapi_v2.model.ChargebackReason; +import com.rbkmoney.openapi.anapi_v2.model.Content; +import org.springframework.stereotype.Component; + +import java.time.ZoneOffset; + +import static com.rbkmoney.anapi.v2.util.OpenApiUtil.mapToChargebackCategory; + +@Component +public class StatChargebackToChargebackConverter { + + public Chargeback convert(StatChargeback chargeback) { + return new Chargeback() + .bodyAmount(chargeback.getAmount()) + .createdAt(TypeUtil.stringToInstant(chargeback.getCreatedAt()).atOffset(ZoneOffset.UTC)) + .chargebackId(chargeback.getChargebackId()) + .fee(chargeback.getFee()) + .chargebackReason(chargeback.getChargebackReason() != null + ? new ChargebackReason() + .category(mapToChargebackCategory(chargeback.getChargebackReason().getCategory())) + .code(chargeback.getChargebackReason().getCode()) : null) + .content(chargeback.getContent() != null + ? new Content().data(chargeback.getContent().getData()) + .type(chargeback.getContent().getType()) : null) + .bodyCurrency(chargeback.getCurrencyCode().getSymbolicCode()); + } +} diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatInvoiceToInvoiceConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatInvoiceToInvoiceConverter.java new file mode 100644 index 0000000..0d8d971 --- /dev/null +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatInvoiceToInvoiceConverter.java @@ -0,0 +1,38 @@ +package com.rbkmoney.anapi.v2.converter.search.response; + +import com.rbkmoney.anapi.v2.util.OpenApiUtil; +import com.rbkmoney.geck.common.util.TypeUtil; +import com.rbkmoney.magista.StatInvoice; +import com.rbkmoney.openapi.anapi_v2.model.Invoice; +import com.rbkmoney.openapi.anapi_v2.model.InvoiceLine; +import org.springframework.stereotype.Component; + +import java.time.ZoneOffset; +import java.util.stream.Collectors; + +@Component +public class StatInvoiceToInvoiceConverter { + + public Invoice convert(StatInvoice invoice) { + return new Invoice() + .amount(invoice.getAmount()) + .createdAt(TypeUtil.stringToInstant(invoice.getCreatedAt()).atOffset(ZoneOffset.UTC)) + .currency(invoice.getCurrencySymbolicCode()) + .externalID(invoice.getExternalId()) + .cart(invoice.getCart() != null + ? invoice.getCart().getLines().stream().map(invoiceLine -> new InvoiceLine() + .cost(invoiceLine.getQuantity() * invoiceLine.getPrice().getAmount()) + .price(invoiceLine.getPrice().getAmount()) + .product(invoiceLine.getProduct()) + //.getTaxMode() + ).collect(Collectors.toList()) : null) + .description(invoice.getDescription()) + .dueDate(TypeUtil.stringToInstant(invoice.getDue()).atOffset(ZoneOffset.UTC)) + .id(invoice.getId()) + .product(invoice.getProduct()) + //.reason() + .shopID(invoice.getShopId()) + .status(OpenApiUtil.mapToInvoiceStatus(invoice.getStatus())); + } + +} diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPayoutToPayoutConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPayoutToPayoutConverter.java new file mode 100644 index 0000000..e2acf5d --- /dev/null +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPayoutToPayoutConverter.java @@ -0,0 +1,31 @@ +package com.rbkmoney.anapi.v2.converter.search.response; + +import com.rbkmoney.anapi.v2.util.OpenApiUtil; +import com.rbkmoney.geck.common.util.TypeUtil; +import com.rbkmoney.magista.StatPayout; +import com.rbkmoney.openapi.anapi_v2.model.Payout; +import org.springframework.stereotype.Component; + +import java.time.ZoneOffset; + +import static com.rbkmoney.anapi.v2.util.OpenApiUtil.mapToPayoutToolDetails; + +@Component +public class StatPayoutToPayoutConverter { + + public Payout convert(StatPayout payout) { + return new Payout() + .amount(payout.getAmount()) + .createdAt(TypeUtil.stringToInstant(payout.getCreatedAt()).atOffset(ZoneOffset.UTC)) + .currency(payout.getCurrencySymbolicCode()) + .fee(payout.getFee()) + .id(payout.getId()) + .payoutToolDetails(mapToPayoutToolDetails(payout.getPayoutToolInfo())) + .shopID(payout.getShopId()) + .status(OpenApiUtil.mapToPayoutStatus(payout.getStatus())) + .cancellationDetails( + payout.getStatus().isSetCancelled() + ? payout.getStatus().getCancelled().getDetails() + : null); + } +} diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatRefundToRefundSearchResultConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatRefundToRefundSearchResultConverter.java new file mode 100644 index 0000000..8a24524 --- /dev/null +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatRefundToRefundSearchResultConverter.java @@ -0,0 +1,35 @@ +package com.rbkmoney.anapi.v2.converter.search.response; + +import com.rbkmoney.geck.common.util.TypeUtil; +import com.rbkmoney.magista.StatRefund; +import com.rbkmoney.openapi.anapi_v2.model.RefundSearchResult; +import com.rbkmoney.openapi.anapi_v2.model.RefundStatusError; +import org.springframework.stereotype.Component; + +import java.time.ZoneOffset; + +import static com.rbkmoney.anapi.v2.util.OpenApiUtil.mapToRefundStatus; + +@Component +public class StatRefundToRefundSearchResultConverter { + + public RefundSearchResult convert(StatRefund refund) { + return new RefundSearchResult() + .amount(refund.getAmount()) + .createdAt(TypeUtil.stringToInstant(refund.getCreatedAt()).atOffset(ZoneOffset.UTC)) + .currency(refund.getCurrencySymbolicCode()) + .id(refund.getId()) + .shopID(refund.getShopId()) + .status(refund.getStatus() != null ? mapToRefundStatus(refund.getStatus()) : null) + .externalID(refund.getExternalId()) + .error(refund.getStatus().isSetFailed() + && refund.getStatus().getFailed().getFailure().isSetFailure() + ? new RefundStatusError() + .code(refund.getStatus().getFailed().getFailure().getFailure().getCode()) + .message(refund.getStatus().getFailed().getFailure().getFailure().getReason()) + : null) + .invoiceID(refund.getInvoiceId()) + .paymentID(refund.getPaymentId()) + .reason(refund.getReason()); + } +} diff --git a/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java b/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java index d70b30e..0674959 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java +++ b/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java @@ -1,8 +1,6 @@ package com.rbkmoney.anapi.v2.service; -import com.rbkmoney.anapi.v2.converter.search.response.StatPaymentToPaymentSearchResultConverter; -import com.rbkmoney.anapi.v2.util.OpenApiUtil; -import com.rbkmoney.geck.common.util.TypeUtil; +import com.rbkmoney.anapi.v2.converter.search.response.*; import com.rbkmoney.magista.*; import com.rbkmoney.openapi.anapi_v2.model.*; import lombok.RequiredArgsConstructor; @@ -11,21 +9,19 @@ import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.AsyncResult; import org.springframework.stereotype.Service; -import java.time.ZoneOffset; -import java.util.ArrayList; -import java.util.List; import java.util.concurrent.Future; import java.util.stream.Collectors; -import static com.rbkmoney.anapi.v2.util.OpenApiUtil.*; - @Service @RequiredArgsConstructor public class SearchService { private final MerchantStatisticsServiceSrv.Iface magistaClient; - private final StatPaymentToPaymentSearchResultConverter paymentResponseConverter; + private final StatChargebackToChargebackConverter chargebackResponseConverter; + private final StatInvoiceToInvoiceConverter invoiceResponseConverter; + private final StatPayoutToPayoutConverter payoutResponseConverter; + private final StatRefundToRefundSearchResultConverter refundResponseConverter; @Async @SneakyThrows @@ -42,25 +38,10 @@ public class SearchService { @SneakyThrows public Future findChargebacks(ChargebackSearchQuery query) { StatChargebackResponse magistaResponse = magistaClient.searchChargebacks(query); - List results = new ArrayList<>(magistaResponse.getChargebacksSize()); - for (StatChargeback chargeback : magistaResponse.getChargebacks()) { - Chargeback result = new Chargeback() - .bodyAmount(chargeback.getAmount()) - .createdAt(TypeUtil.stringToInstant(chargeback.getCreatedAt()).atOffset(ZoneOffset.UTC)) - .chargebackId(chargeback.getChargebackId()) - .fee(chargeback.getFee()) - .chargebackReason(chargeback.getChargebackReason() != null - ? new ChargebackReason() - .category(mapToChargebackCategory(chargeback.getChargebackReason().getCategory())) - .code(chargeback.getChargebackReason().getCode()) : null) - .content(chargeback.getContent() != null - ? new Content().data(chargeback.getContent().getData()) - .type(chargeback.getContent().getType()) : null) - .bodyCurrency(chargeback.getCurrencyCode().getSymbolicCode()); - results.add(result); - } return new AsyncResult<>(new InlineResponse2008() - .result(results) + .result(magistaResponse.getChargebacks().stream() + .map(chargebackResponseConverter::convert) + .collect(Collectors.toList())) .continuationToken(magistaResponse.getContinuationToken())); } @@ -69,31 +50,10 @@ public class SearchService { @SneakyThrows public Future findInvoices(InvoiceSearchQuery query) { StatInvoiceResponse magistaResponse = magistaClient.searchInvoices(query); - List results = new ArrayList<>(magistaResponse.getInvoicesSize()); - for (StatInvoice invoice : magistaResponse.getInvoices()) { - Invoice result = new Invoice() - .amount(invoice.getAmount()) - .createdAt(TypeUtil.stringToInstant(invoice.getCreatedAt()).atOffset(ZoneOffset.UTC)) - .currency(invoice.getCurrencySymbolicCode()) - .externalID(invoice.getExternalId()) - .cart(invoice.getCart() != null - ? invoice.getCart().getLines().stream().map(invoiceLine -> new InvoiceLine() - .cost(invoiceLine.getQuantity() * invoiceLine.getPrice().getAmount()) - .price(invoiceLine.getPrice().getAmount()) - .product(invoiceLine.getProduct()) - //.getTaxMode() - ).collect(Collectors.toList()) : null) - .description(invoice.getDescription()) - .dueDate(TypeUtil.stringToInstant(invoice.getDue()).atOffset(ZoneOffset.UTC)) - .id(invoice.getId()) - .product(invoice.getProduct()) - //.reason() - .shopID(invoice.getShopId()) - .status(OpenApiUtil.mapToInvoiceStatus(invoice.getStatus())); - results.add(result); - } return new AsyncResult<>(new InlineResponse2009() - .result(results) + .result(magistaResponse.getInvoices().stream() + .map(invoiceResponseConverter::convert) + .collect(Collectors.toList())) .continuationToken(magistaResponse.getContinuationToken())); } @@ -101,24 +61,10 @@ public class SearchService { @SneakyThrows public Future findPayouts(PayoutSearchQuery query) { StatPayoutResponse magistaResponse = magistaClient.searchPayouts(query); - List results = new ArrayList<>(magistaResponse.getPayoutsSize()); - for (StatPayout payout : magistaResponse.getPayouts()) { - Payout result = new Payout() - .amount(payout.getAmount()) - .createdAt(TypeUtil.stringToInstant(payout.getCreatedAt()).atOffset(ZoneOffset.UTC)) - .currency(payout.getCurrencySymbolicCode()) - .fee(payout.getFee()) - .id(payout.getId()) - .payoutToolDetails(mapToPayoutToolDetails(payout.getPayoutToolInfo())) - .shopID(payout.getShopId()) - .status(OpenApiUtil.mapToPayoutStatus(payout.getStatus())) - .cancellationDetails( - payout.getStatus().isSetCancelled() ? payout.getStatus().getCancelled().getDetails() : - null); - results.add(result); - } return new AsyncResult<>(new InlineResponse20011() - .result(results) + .result(magistaResponse.getPayouts().stream() + .map(payoutResponseConverter::convert) + .collect(Collectors.toList())) .continuationToken(magistaResponse.getContinuationToken())); } @@ -126,29 +72,10 @@ public class SearchService { @SneakyThrows public Future findRefunds(RefundSearchQuery query) { StatRefundResponse magistaResponse = magistaClient.searchRefunds(query); - List results = new ArrayList<>(magistaResponse.getRefundsSize()); - for (StatRefund refund : magistaResponse.getRefunds()) { - RefundSearchResult result = new RefundSearchResult() - .amount(refund.getAmount()) - .createdAt(TypeUtil.stringToInstant(refund.getCreatedAt()).atOffset(ZoneOffset.UTC)) - .currency(refund.getCurrencySymbolicCode()) - .id(refund.getId()) - .shopID(refund.getShopId()) - .status(refund.getStatus() != null ? mapToRefundStatus(refund.getStatus()) : null) - .externalID(refund.getExternalId()) - .error(refund.getStatus().isSetFailed() - && refund.getStatus().getFailed().getFailure().isSetFailure() - ? new RefundStatusError() - .code(refund.getStatus().getFailed().getFailure().getFailure().getCode()) - .message(refund.getStatus().getFailed().getFailure().getFailure().getReason()) - : null) - .invoiceID(refund.getInvoiceId()) - .paymentID(refund.getPaymentId()) - .reason(refund.getReason()); - results.add(result); - } return new AsyncResult<>(new InlineResponse20012() - .result(results) + .result(magistaResponse.getRefunds().stream() + .map(refundResponseConverter::convert) + .collect(Collectors.toList())) .continuationToken(magistaResponse.getContinuationToken())); } From c5c97c13d5ae7554b7b25b97acdcaee4789d542b Mon Sep 17 00:00:00 2001 From: echerniak Date: Thu, 16 Sep 2021 19:51:53 +0300 Subject: [PATCH 14/37] Deadline processing updated --- pom.xml | 48 +++++++ .../rbkmoney/anapi/v2/config/AsyncConfig.java | 20 --- .../anapi/v2/config/SecurityConfig.java | 135 ++++++++++++++++++ .../rbkmoney/anapi/v2/config/WebConfig.java | 105 ++++++++++++++ .../anapi/v2/controller/SearchController.java | 76 ++++------ .../anapi/v2/exception/DeadlineException.java | 23 +++ .../anapi/v2/service/SearchService.java | 38 ++--- .../rbkmoney/anapi/v2/util/CommonUtil.java | 23 --- .../rbkmoney/anapi/v2/util/DeadlineUtils.java | 111 ++++++++++++++ 9 files changed, 461 insertions(+), 118 deletions(-) delete mode 100644 src/main/java/com/rbkmoney/anapi/v2/config/AsyncConfig.java create mode 100644 src/main/java/com/rbkmoney/anapi/v2/config/SecurityConfig.java create mode 100644 src/main/java/com/rbkmoney/anapi/v2/config/WebConfig.java create mode 100644 src/main/java/com/rbkmoney/anapi/v2/exception/DeadlineException.java create mode 100644 src/main/java/com/rbkmoney/anapi/v2/util/DeadlineUtils.java diff --git a/pom.xml b/pom.xml index b8c33f2..6186cb1 100644 --- a/pom.xml +++ b/pom.xml @@ -78,6 +78,54 @@ + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.security + spring-security-config + 5.5.2 + + + org.springframework.security + spring-security-web + 5.5.2 + + + org.keycloak + keycloak-admin-client + 15.0.2 + + + org.jboss.resteasy + resteasy-client + + + org.jboss.resteasy + resteasy-multipart-provider + + + org.jboss.resteasy + resteasy-jackson2-provider + + + org.jboss.resteasy + resteasy-jaxb-provider + + + + + org.keycloak + keycloak-spring-security-adapter + 15.0.2 + + + org.bouncycastle + bcprov-jdk15on + + + org.springframework.boot spring-boot-starter diff --git a/src/main/java/com/rbkmoney/anapi/v2/config/AsyncConfig.java b/src/main/java/com/rbkmoney/anapi/v2/config/AsyncConfig.java deleted file mode 100644 index 249ef97..0000000 --- a/src/main/java/com/rbkmoney/anapi/v2/config/AsyncConfig.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.rbkmoney.anapi.v2.config; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.task.SimpleAsyncTaskExecutor; -import org.springframework.scheduling.annotation.AsyncConfigurer; -import org.springframework.scheduling.annotation.EnableAsync; -import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; - -import java.util.concurrent.Executor; - -@Configuration -@EnableAsync -public class AsyncConfig implements AsyncConfigurer { - - @Override - public Executor getAsyncExecutor() { - return new ThreadPoolTaskExecutor(); - } -} diff --git a/src/main/java/com/rbkmoney/anapi/v2/config/SecurityConfig.java b/src/main/java/com/rbkmoney/anapi/v2/config/SecurityConfig.java new file mode 100644 index 0000000..de3da53 --- /dev/null +++ b/src/main/java/com/rbkmoney/anapi/v2/config/SecurityConfig.java @@ -0,0 +1,135 @@ +package com.rbkmoney.anapi.v2.config; + +import org.keycloak.adapters.KeycloakConfigResolver; +import org.keycloak.adapters.KeycloakDeployment; +import org.keycloak.adapters.KeycloakDeploymentBuilder; +import org.keycloak.adapters.springsecurity.KeycloakSecurityComponents; +import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter; +import org.keycloak.representations.adapters.config.AdapterConfig; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.FilterType; +import org.springframework.http.HttpMethod; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy; +import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; +import org.springframework.util.StringUtils; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.List; +import java.util.stream.Collectors; + +@Configuration +@EnableWebSecurity +@ComponentScan( + basePackageClasses = KeycloakSecurityComponents.class, + excludeFilters = @ComponentScan.Filter( + type = FilterType.REGEX, + pattern = "org.keycloak.adapters.springsecurity.management.HttpSessionManager")) +@EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true) +@ConditionalOnProperty(value = "auth.enabled", havingValue = "true") +public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter { + + @Value("${keycloak.realm}") + private String keycloakRealmName; + + @Value("${keycloak.resource}") + private String keycloakResourceName; + + @Value("${keycloak.realm-public-key}") + private String keycloakRealmPublicKey; + + @Value("${keycloak.realm-public-key.file-path:}") + private String keycloakRealmPublicKeyFile; + + @Value("${keycloak.auth-server-url}") + private String keycloakAuthServerUrl; + + @Value("${keycloak.ssl-required}") + private String keycloakSSLRequired; + + @Value("${keycloak.not-before}") + private int keycloakTokenNotBefore; + + @Bean + @Override + protected SessionAuthenticationStrategy sessionAuthenticationStrategy() { + return new NullAuthenticatedSessionStrategy(); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + super.configure(http); + http + .cors().and() + .csrf().disable() + .authorizeRequests() + .antMatchers(HttpMethod.OPTIONS, "/**").permitAll() + .antMatchers(HttpMethod.GET, "/**/health").permitAll() + .anyRequest().authenticated(); + } + + @Autowired + public void configureGlobal(AuthenticationManagerBuilder auth) { + auth.authenticationProvider(keycloakAuthenticationProvider()); + } + + @Bean + public KeycloakConfigResolver keycloakConfigResolver() { + return facade -> { + KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(adapterConfig()); + deployment.setNotBefore(keycloakTokenNotBefore); + return deployment; + }; + } + + @Bean + public CorsConfigurationSource corsConfigurationSource() { + CorsConfiguration configuration = new CorsConfiguration(); + configuration.applyPermitDefaultValues(); + configuration.addAllowedMethod(HttpMethod.PUT); + configuration.addAllowedMethod(HttpMethod.DELETE); + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", configuration); + return source; + } + + private AdapterConfig adapterConfig() { + if (StringUtils.hasLength(keycloakRealmPublicKeyFile)) { + keycloakRealmPublicKey = readKeyFromFile(keycloakRealmPublicKeyFile); + } + + AdapterConfig adapterConfig = new AdapterConfig(); + adapterConfig.setRealm(keycloakRealmName); + adapterConfig.setRealmKey(keycloakRealmPublicKey); + adapterConfig.setResource(keycloakResourceName); + adapterConfig.setAuthServerUrl(keycloakAuthServerUrl); + adapterConfig.setUseResourceRoleMappings(true); + adapterConfig.setBearerOnly(true); + adapterConfig.setSslRequired(keycloakSSLRequired); + return adapterConfig; + } + + private String readKeyFromFile(String filePath) { + try { + List strings = Files.readAllLines(Paths.get(filePath)); + strings.remove(strings.size() - 1); + strings.remove(0); + return strings.stream().map(String::trim).collect(Collectors.joining()); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } +} diff --git a/src/main/java/com/rbkmoney/anapi/v2/config/WebConfig.java b/src/main/java/com/rbkmoney/anapi/v2/config/WebConfig.java new file mode 100644 index 0000000..36b1c9b --- /dev/null +++ b/src/main/java/com/rbkmoney/anapi/v2/config/WebConfig.java @@ -0,0 +1,105 @@ +package com.rbkmoney.anapi.v2.config; + +import com.rbkmoney.woody.api.flow.WFlow; +import com.rbkmoney.woody.api.trace.context.metadata.user.UserIdentityEmailExtensionKit; +import com.rbkmoney.woody.api.trace.context.metadata.user.UserIdentityIdExtensionKit; +import com.rbkmoney.woody.api.trace.context.metadata.user.UserIdentityRealmExtensionKit; +import com.rbkmoney.woody.api.trace.context.metadata.user.UserIdentityUsernameExtensionKit; +import org.keycloak.KeycloakSecurityContext; +import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken; +import org.keycloak.representations.AccessToken; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.security.Principal; +import java.time.Instant; +import java.time.temporal.ChronoUnit; + +import static com.rbkmoney.anapi.v2.util.DeadlineUtils.*; +import static com.rbkmoney.woody.api.trace.ContextUtils.setCustomMetadataValue; +import static com.rbkmoney.woody.api.trace.ContextUtils.setDeadline; + +@Configuration +@SuppressWarnings({"ParameterName", "LocalVariableName"}) +public class WebConfig { + + @Bean + public FilterRegistrationBean woodyFilter() { + WFlow woodyFlow = new WFlow(); + Filter filter = new OncePerRequestFilter() { + + @Override + protected void doFilterInternal(HttpServletRequest request, + HttpServletResponse response, + FilterChain filterChain) throws ServletException, IOException { + woodyFlow.createServiceFork( + () -> { + try { + if (request.getUserPrincipal() != null) { + addWoodyContext(request.getUserPrincipal()); + } + + setWoodyDeadline(request); + + filterChain.doFilter(request, response); + } catch (IOException | ServletException e) { + sneakyThrow(e); + } + } + ) + .run(); + } + + private T sneakyThrow(Throwable t) throws E { + throw (E) t; + } + }; + + FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); + filterRegistrationBean.setFilter(filter); + filterRegistrationBean.setOrder(-50); + filterRegistrationBean.setName("woodyFilter"); + filterRegistrationBean.addUrlPatterns("*"); + return filterRegistrationBean; + } + + private void addWoodyContext(Principal principal) { + KeycloakSecurityContext keycloakSecurityContext = + ((KeycloakAuthenticationToken) principal).getAccount().getKeycloakSecurityContext(); + AccessToken accessToken = keycloakSecurityContext.getToken(); + + setCustomMetadataValue(UserIdentityIdExtensionKit.KEY, accessToken.getSubject()); + setCustomMetadataValue(UserIdentityUsernameExtensionKit.KEY, accessToken.getPreferredUsername()); + setCustomMetadataValue(UserIdentityEmailExtensionKit.KEY, accessToken.getEmail()); + setCustomMetadataValue(UserIdentityRealmExtensionKit.KEY, keycloakSecurityContext.getRealm()); + } + + private void setWoodyDeadline(HttpServletRequest request) { + String xRequestDeadline = request.getHeader("X-Request-Deadline"); + String xRequestId = request.getHeader("X-Request-ID"); + if (xRequestDeadline != null) { + setDeadline(getInstant(xRequestDeadline, xRequestId)); + } + } + + private Instant getInstant(String xRequestDeadline, String xRequestId) { + Instant instant; + if (containsRelativeValues(xRequestDeadline, xRequestId)) { + instant = Instant.now() + .plus(extractMilliseconds(xRequestDeadline, xRequestId), ChronoUnit.MILLIS) + .plus(extractSeconds(xRequestDeadline, xRequestId), ChronoUnit.MILLIS) + .plus(extractMinutes(xRequestDeadline, xRequestId), ChronoUnit.MILLIS); + } else { + instant = Instant.parse(xRequestDeadline); + } + return instant; + } +} diff --git a/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java b/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java index 71c8353..9db6628 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java +++ b/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java @@ -25,15 +25,16 @@ import java.util.List; import java.util.Optional; import java.util.Set; import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.stream.Collectors; -import static com.rbkmoney.anapi.v2.util.CommonUtil.getRequestDeadlineMillis; +import static com.rbkmoney.anapi.v2.util.DeadlineUtils.checkDeadline; + @Slf4j @Controller @RequiredArgsConstructor +@SuppressWarnings("ParameterName") public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesApi, PayoutsApi, RefundsApi { private final SearchService searchService; @@ -50,12 +51,12 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @SneakyThrows @Override - public ResponseEntity searchPayments(String xrequestID, + public ResponseEntity searchPayments(String xRequestID, @NotNull @Size(min = 1, max = 40) @Valid String partyID, @NotNull @Valid OffsetDateTime fromTime, @NotNull @Valid OffsetDateTime toTime, @NotNull @Min(1L) @Max(1000L) @Valid Integer limit, - String xrequestDeadline, + String xRequestDeadline, @Size(min = 1, max = 40) @Valid String shopID, @Valid List shopIDs, @Valid String paymentInstitutionRealm, @@ -80,6 +81,7 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Min(1L) @Valid Long paymentAmountTo, @Valid List excludedShops, @Valid String continuationToken) { + checkDeadline(xRequestDeadline, xRequestID); PaymentSearchQuery query = paymentSearchConverter.convert(partyID, fromTime, toTime, @@ -108,14 +110,7 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp paymentAmountTo, excludedShops, continuationToken); - InlineResponse20010 response; - if (xrequestDeadline != null) { - response = searchService - .findPayments(query) - .get(getRequestDeadlineMillis(xrequestDeadline), TimeUnit.MILLISECONDS); - } else { - response = searchService.findPayments(query).get(); - } + InlineResponse20010 response = searchService.findPayments(query); return ResponseEntity.ok(response); } @@ -125,12 +120,12 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp produces = {"application/json; charset=utf-8"} ) @Override - public ResponseEntity searchChargebacks(String xrequestID, + public ResponseEntity searchChargebacks(String xRequestID, @NotNull @Size(min = 1, max = 40) @Valid String partyID, @NotNull @Valid OffsetDateTime fromTime, @NotNull @Valid OffsetDateTime toTime, @NotNull @Min(1L) @Max(1000L) @Valid Integer limit, - String xrequestDeadline, + String xRequestDeadline, @Size(min = 1, max = 40) @Valid String shopID, @Valid List shopIDs, @Valid String paymentInstitutionRealm, @@ -143,6 +138,7 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Valid List chargebackCategories, @Valid String continuationToken) { //TODO: clarify mapping for paymentInstitutionRealm, xrequestID, xrequestDeadline, offset + checkDeadline(xRequestDeadline, xRequestID); ChargebackSearchQuery query = chargebackSearchConverter.convert(partyID, fromTime, toTime, @@ -158,14 +154,8 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp chargebackStages, chargebackCategories, continuationToken); - InlineResponse2008 response; - if (xrequestDeadline != null) { - response = searchService - .findChargebacks(query) - .get(getRequestDeadlineMillis(xrequestDeadline), TimeUnit.MILLISECONDS); - } else { - response = searchService.findChargebacks(query).get(); - } + InlineResponse2008 response = searchService + .findChargebacks(query); return ResponseEntity.ok(response); } @@ -175,12 +165,12 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp produces = {"application/json; charset=utf-8"} ) @Override - public ResponseEntity searchInvoices(String xrequestID, + public ResponseEntity searchInvoices(String xRequestID, @NotNull @Size(min = 1, max = 40) @Valid String partyID, @NotNull @Valid OffsetDateTime fromTime, @NotNull @Valid OffsetDateTime toTime, @NotNull @Min(1L) @Max(1000L) @Valid Integer limit, - String xrequestDeadline, + String xRequestDeadline, @Size(min = 1, max = 40) @Valid String shopID, @Valid List shopIDs, @Valid String paymentInstitutionRealm, @@ -193,6 +183,7 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Valid List excludedShops, @Valid String continuationToken) { //TODO: clarify mapping for paymentInstitutionRealm, xrequestID, xrequestDeadline, excludedShops + checkDeadline(xRequestDeadline, xRequestID); InvoiceSearchQuery query = invoiceSearchConverter.convert(partyID, fromTime, toTime, @@ -208,14 +199,7 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp invoiceAmountTo, excludedShops, continuationToken); - InlineResponse2009 response; - if (xrequestDeadline != null) { - response = searchService - .findInvoices(query) - .get(getRequestDeadlineMillis(xrequestDeadline), TimeUnit.MILLISECONDS); - } else { - response = searchService.findInvoices(query).get(); - } + InlineResponse2009 response = searchService.findInvoices(query); return ResponseEntity.ok(response); } @@ -225,12 +209,12 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp produces = {"application/json; charset=utf-8"} ) @Override - public ResponseEntity searchPayouts(String xrequestID, + public ResponseEntity searchPayouts(String xRequestID, @NotNull @Size(min = 1, max = 40) @Valid String partyID, @NotNull @Valid OffsetDateTime fromTime, @NotNull @Valid OffsetDateTime toTime, @NotNull @Min(1L) @Max(1000L) @Valid Integer limit, - String xrequestDeadline, + String xRequestDeadline, @Size(min = 1, max = 40) @Valid String shopID, @Valid List shopIDs, @Valid String paymentInstitutionRealm, @@ -241,6 +225,7 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Valid String continuationToken) { //TODO: clarify mapping for paymentInstitutionRealm, xrequestID, xrequestDeadline, excludedShops, //offset + setStatuses + checkDeadline(xRequestDeadline, xRequestID); PayoutSearchQuery query = payoutSearchConverter.convert(partyID, fromTime, toTime, @@ -253,14 +238,7 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp payoutToolType, excludedShops, continuationToken); - InlineResponse20011 response; - if (xrequestDeadline != null) { - response = searchService - .findPayouts(query) - .get(getRequestDeadlineMillis(xrequestDeadline), TimeUnit.MILLISECONDS); - } else { - response = searchService.findPayouts(query).get(); - } + InlineResponse20011 response = searchService.findPayouts(query); return ResponseEntity.ok(response); } @@ -270,12 +248,12 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp produces = {"application/json; charset=utf-8"} ) @Override - public ResponseEntity searchRefunds(String xrequestID, + public ResponseEntity searchRefunds(String xRequestID, @NotNull @Size(min = 1, max = 40) @Valid String partyID, @NotNull @Valid OffsetDateTime fromTime, @NotNull @Valid OffsetDateTime toTime, @NotNull @Min(1L) @Max(1000L) @Valid Integer limit, - String xrequestDeadline, + String xRequestDeadline, @Size(min = 1, max = 40) @Valid String shopID, @Valid List shopIDs, @Valid String paymentInstitutionRealm, @@ -289,6 +267,7 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Valid List excludedShops, @Valid String continuationToken) { //TODO: clarify mapping for paymentInstitutionRealm, xrequestID, xrequestDeadline, excludedShops, offset + checkDeadline(xRequestDeadline, xRequestID); RefundSearchQuery query = refundSearchConverter.convert(partyID, fromTime, toTime, @@ -306,14 +285,7 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp excludedShops, continuationToken); - InlineResponse20012 response; - if (xrequestDeadline != null) { - response = searchService - .findRefunds(query) - .get(getRequestDeadlineMillis(xrequestDeadline), TimeUnit.MILLISECONDS); - } else { - response = searchService.findRefunds(query).get(); - } + InlineResponse20012 response = searchService.findRefunds(query); return ResponseEntity.ok(response); } diff --git a/src/main/java/com/rbkmoney/anapi/v2/exception/DeadlineException.java b/src/main/java/com/rbkmoney/anapi/v2/exception/DeadlineException.java new file mode 100644 index 0000000..5ec07f9 --- /dev/null +++ b/src/main/java/com/rbkmoney/anapi/v2/exception/DeadlineException.java @@ -0,0 +1,23 @@ +package com.rbkmoney.anapi.v2.exception; + +public class DeadlineException extends RuntimeException { + + public DeadlineException() { + } + + public DeadlineException(String message) { + super(message); + } + + public DeadlineException(String message, Throwable cause) { + super(message, cause); + } + + public DeadlineException(Throwable cause) { + super(cause); + } + + public DeadlineException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java b/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java index 0674959..f6a1a7a 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java +++ b/src/main/java/com/rbkmoney/anapi/v2/service/SearchService.java @@ -5,11 +5,8 @@ import com.rbkmoney.magista.*; import com.rbkmoney.openapi.anapi_v2.model.*; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; -import org.springframework.scheduling.annotation.Async; -import org.springframework.scheduling.annotation.AsyncResult; import org.springframework.stereotype.Service; -import java.util.concurrent.Future; import java.util.stream.Collectors; @Service @@ -23,60 +20,55 @@ public class SearchService { private final StatPayoutToPayoutConverter payoutResponseConverter; private final StatRefundToRefundSearchResultConverter refundResponseConverter; - @Async @SneakyThrows - public Future findPayments(PaymentSearchQuery query) { + public InlineResponse20010 findPayments(PaymentSearchQuery query) { StatPaymentResponse magistaResponse = magistaClient.searchPayments(query); - return new AsyncResult<>(new InlineResponse20010() + return new InlineResponse20010() .result(magistaResponse.getPayments().stream() .map(paymentResponseConverter::convert) .collect(Collectors.toList())) - .continuationToken(magistaResponse.getContinuationToken())); + .continuationToken(magistaResponse.getContinuationToken()); } - @Async @SneakyThrows - public Future findChargebacks(ChargebackSearchQuery query) { + public InlineResponse2008 findChargebacks(ChargebackSearchQuery query) { StatChargebackResponse magistaResponse = magistaClient.searchChargebacks(query); - return new AsyncResult<>(new InlineResponse2008() + return new InlineResponse2008() .result(magistaResponse.getChargebacks().stream() .map(chargebackResponseConverter::convert) .collect(Collectors.toList())) - .continuationToken(magistaResponse.getContinuationToken())); + .continuationToken(magistaResponse.getContinuationToken()); } - @Async @SneakyThrows - public Future findInvoices(InvoiceSearchQuery query) { + public InlineResponse2009 findInvoices(InvoiceSearchQuery query) { StatInvoiceResponse magistaResponse = magistaClient.searchInvoices(query); - return new AsyncResult<>(new InlineResponse2009() + return new InlineResponse2009() .result(magistaResponse.getInvoices().stream() .map(invoiceResponseConverter::convert) .collect(Collectors.toList())) - .continuationToken(magistaResponse.getContinuationToken())); + .continuationToken(magistaResponse.getContinuationToken()); } - @Async @SneakyThrows - public Future findPayouts(PayoutSearchQuery query) { + public InlineResponse20011 findPayouts(PayoutSearchQuery query) { StatPayoutResponse magistaResponse = magistaClient.searchPayouts(query); - return new AsyncResult<>(new InlineResponse20011() + return new InlineResponse20011() .result(magistaResponse.getPayouts().stream() .map(payoutResponseConverter::convert) .collect(Collectors.toList())) - .continuationToken(magistaResponse.getContinuationToken())); + .continuationToken(magistaResponse.getContinuationToken()); } - @Async @SneakyThrows - public Future findRefunds(RefundSearchQuery query) { + public InlineResponse20012 findRefunds(RefundSearchQuery query) { StatRefundResponse magistaResponse = magistaClient.searchRefunds(query); - return new AsyncResult<>(new InlineResponse20012() + return new InlineResponse20012() .result(magistaResponse.getRefunds().stream() .map(refundResponseConverter::convert) .collect(Collectors.toList())) - .continuationToken(magistaResponse.getContinuationToken())); + .continuationToken(magistaResponse.getContinuationToken()); } } diff --git a/src/main/java/com/rbkmoney/anapi/v2/util/CommonUtil.java b/src/main/java/com/rbkmoney/anapi/v2/util/CommonUtil.java index 6bc95b2..a4a9c3d 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/util/CommonUtil.java +++ b/src/main/java/com/rbkmoney/anapi/v2/util/CommonUtil.java @@ -3,19 +3,12 @@ package com.rbkmoney.anapi.v2.util; import lombok.experimental.UtilityClass; import javax.annotation.Nullable; -import javax.validation.constraints.NotNull; -import java.time.Duration; -import java.time.Instant; -import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.List; -import java.util.regex.Pattern; @UtilityClass public class CommonUtil { - private static final Pattern deadlinePattern = Pattern.compile("\\d+(?:\\.\\d+)?(?:ms|s|m)"); - public static List merge(@Nullable String id, @Nullable List ids) { if (id != null) { if (ids == null) { @@ -26,20 +19,4 @@ public class CommonUtil { return ids; } - public static long getRequestDeadlineMillis(@NotNull String requestDeadLine) { - if (deadlinePattern.matcher(requestDeadLine).matches()) { - //150000ms, 540s, 3.5m, etc - if (requestDeadLine.endsWith("ms")) { - return Long.parseLong(requestDeadLine.substring(0, requestDeadLine.length() - 3)); - } - String duration = "PT" + requestDeadLine.toUpperCase(); - return Duration.parse(duration).toMillis(); - } - - //ISO 8601 - OffsetDateTime odt = OffsetDateTime.parse(requestDeadLine); - return odt.toInstant().toEpochMilli() - Instant.now().toEpochMilli(); - - } - } diff --git a/src/main/java/com/rbkmoney/anapi/v2/util/DeadlineUtils.java b/src/main/java/com/rbkmoney/anapi/v2/util/DeadlineUtils.java new file mode 100644 index 0000000..86b0245 --- /dev/null +++ b/src/main/java/com/rbkmoney/anapi/v2/util/DeadlineUtils.java @@ -0,0 +1,111 @@ +package com.rbkmoney.anapi.v2.util; + +import com.rbkmoney.anapi.v2.exception.DeadlineException; +import lombok.experimental.UtilityClass; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@UtilityClass +@SuppressWarnings("ParameterName") +public class DeadlineUtils { + + public static void checkDeadline(String xRequestDeadline, String xRequestId) { + if (xRequestDeadline == null) { + return; + } + if (containsRelativeValues(xRequestDeadline, xRequestId)) { + return; + } + try { + Instant instant = Instant.parse(xRequestDeadline); + + if (Instant.now().isAfter(instant)) { + throw new DeadlineException(String.format("Deadline has expired, xRequestId=%s ", xRequestId)); + } + } catch (Exception ex) { + throw new DeadlineException( + String.format("Deadline has invalid 'Instant' format, xRequestId=%s ", xRequestId)); + } + } + + public static boolean containsRelativeValues(String xRequestDeadline, String xRequestId) { + return (extractMinutes(xRequestDeadline, xRequestId) + extractSeconds(xRequestDeadline, xRequestId) + + extractMilliseconds(xRequestDeadline, xRequestId)) > 0; + } + + public static Long extractMinutes(String xRequestDeadline, String xRequestId) { + String format = "minutes"; + + checkNegativeValues(xRequestDeadline, xRequestId, "([-][0-9]+([.][0-9]+)?(?!ms)[m])", format); + + Double minutes = extractValue(xRequestDeadline, "([0-9]+([.][0-9]+)?(?!ms)[m])", xRequestId, format); + + return Optional.ofNullable(minutes).map(min -> min * 60000.0).map(Double::longValue).orElse(0L); + } + + public static Long extractSeconds(String xRequestDeadline, String xRequestId) { + String format = "seconds"; + + checkNegativeValues(xRequestDeadline, xRequestId, "([-][0-9]+([.][0-9]+)?[s])", format); + + Double seconds = extractValue(xRequestDeadline, "([0-9]+([.][0-9]+)?[s])", xRequestId, format); + + return Optional.ofNullable(seconds).map(s -> s * 1000.0).map(Double::longValue).orElse(0L); + } + + public static Long extractMilliseconds(String xRequestDeadline, String xRequestId) { + String format = "milliseconds"; + + checkNegativeValues(xRequestDeadline, xRequestId, "([-][0-9]+([.][0-9]+)?[m][s])", format); + + Double milliseconds = extractValue(xRequestDeadline, "([0-9]+([.][0-9]+)?[m][s])", xRequestId, format); + + if (milliseconds != null && Math.ceil(milliseconds % 1) > 0) { + throw new DeadlineException( + String.format("Deadline 'milliseconds' parameter can have only integer value, xRequestId=%s ", + xRequestId)); + } + + return Optional.ofNullable(milliseconds).map(Double::longValue).orElse(0L); + } + + private static void checkNegativeValues(String xRequestDeadline, String xRequestId, String regex, String format) { + if (!match(regex, xRequestDeadline).isEmpty()) { + throw new DeadlineException( + String.format("Deadline '%s' parameter has negative value, xRequestId=%s ", format, xRequestId)); + } + } + + private static Double extractValue(String xRequestDeadline, String formatRegex, String xRequestId, String format) { + String numberRegex = "([0-9]+([.][0-9]+)?)"; + + List doubles = new ArrayList<>(); + for (String string : match(formatRegex, xRequestDeadline)) { + doubles.addAll(match(numberRegex, string)); + } + if (doubles.size() > 1) { + throw new DeadlineException( + String.format("Deadline '%s' parameter has a few relative value, xRequestId=%s ", format, + xRequestId)); + } + if (doubles.isEmpty()) { + return null; + } + return Double.valueOf(doubles.get(0)); + } + + private static List match(String regex, String data) { + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(data); + List strings = new ArrayList<>(); + while (matcher.find()) { + strings.add(matcher.group()); + } + return strings; + } +} From 379175d2e6a10d20b352a981f9cb9e2c6b9bd7fd Mon Sep 17 00:00:00 2001 From: echerniak Date: Thu, 16 Sep 2021 22:05:29 +0300 Subject: [PATCH 15/37] WIP. Refactoring + error handling --- .../rbkmoney/anapi/v2/config/WebConfig.java | 2 +- .../v2/controller/ErrorControllerAdvice.java | 62 +++++ .../anapi/v2/controller/SearchController.java | 46 +--- ...aramsToChargebackSearchQueryConverter.java | 62 ++++- .../ParamsToInvoiceSearchQueryConverter.java | 8 +- .../ParamsToPaymentSearchQueryConverter.java | 35 ++- .../ParamsToPayoutSearchQueryConverter.java | 25 +- .../ParamsToRefundSearchQueryConverter.java | 23 +- .../StatChargebackToChargebackConverter.java | 26 +- .../StatInvoiceToInvoiceConverter.java | 25 +- ...PaymentToPaymentSearchResultConverter.java | 89 ++++++- .../response/StatPayoutToPayoutConverter.java | 102 ++++++- ...atRefundToRefundSearchResultConverter.java | 20 +- .../rbkmoney/anapi/v2/util/CommonUtil.java | 22 -- .../rbkmoney/anapi/v2/util/ConverterUtil.java | 56 ++++ .../rbkmoney/anapi/v2/util/DamselUtil.java | 140 ---------- .../{DeadlineUtils.java => DeadlineUtil.java} | 2 +- .../rbkmoney/anapi/v2/util/OpenApiUtil.java | 252 ------------------ 18 files changed, 496 insertions(+), 501 deletions(-) create mode 100644 src/main/java/com/rbkmoney/anapi/v2/controller/ErrorControllerAdvice.java delete mode 100644 src/main/java/com/rbkmoney/anapi/v2/util/CommonUtil.java create mode 100644 src/main/java/com/rbkmoney/anapi/v2/util/ConverterUtil.java delete mode 100644 src/main/java/com/rbkmoney/anapi/v2/util/DamselUtil.java rename src/main/java/com/rbkmoney/anapi/v2/util/{DeadlineUtils.java => DeadlineUtil.java} (99%) delete mode 100644 src/main/java/com/rbkmoney/anapi/v2/util/OpenApiUtil.java diff --git a/src/main/java/com/rbkmoney/anapi/v2/config/WebConfig.java b/src/main/java/com/rbkmoney/anapi/v2/config/WebConfig.java index 36b1c9b..cc51351 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/config/WebConfig.java +++ b/src/main/java/com/rbkmoney/anapi/v2/config/WebConfig.java @@ -23,7 +23,7 @@ import java.security.Principal; import java.time.Instant; import java.time.temporal.ChronoUnit; -import static com.rbkmoney.anapi.v2.util.DeadlineUtils.*; +import static com.rbkmoney.anapi.v2.util.DeadlineUtil.*; import static com.rbkmoney.woody.api.trace.ContextUtils.setCustomMetadataValue; import static com.rbkmoney.woody.api.trace.ContextUtils.setDeadline; diff --git a/src/main/java/com/rbkmoney/anapi/v2/controller/ErrorControllerAdvice.java b/src/main/java/com/rbkmoney/anapi/v2/controller/ErrorControllerAdvice.java new file mode 100644 index 0000000..60e3fc1 --- /dev/null +++ b/src/main/java/com/rbkmoney/anapi/v2/controller/ErrorControllerAdvice.java @@ -0,0 +1,62 @@ +package com.rbkmoney.anapi.v2.controller; + +import com.rbkmoney.anapi.v2.exception.BadRequestException; +import com.rbkmoney.anapi.v2.exception.DeadlineException; +import com.rbkmoney.openapi.anapi_v2.model.DefaultLogicError; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; +import java.util.Set; +import java.util.stream.Collectors; + +@Slf4j +@RestControllerAdvice +@RequiredArgsConstructor +public class ErrorControllerAdvice { + + @ExceptionHandler({ConstraintViolationException.class}) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public Object handleConstraintViolationException(ConstraintViolationException e) { + log.warn("<- Res [400]: Not valid", e); + Set> constraintViolations = + e.getConstraintViolations(); + String errorMessage = + constraintViolations.stream() + .map(violation -> violation.getPropertyPath() + ": " + violation.getMessage()) + .collect(Collectors.joining(", ")); + return new DefaultLogicError() + .code(DefaultLogicError.CodeEnum.INVALIDREQUEST) + .message(errorMessage); + } + + @ExceptionHandler({BadRequestException.class}) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public Object handleBadRequestException(BadRequestException e) { + log.warn("<- Res [400]: Not valid", e); + return new DefaultLogicError() + .code(DefaultLogicError.CodeEnum.INVALIDREQUEST) + .message(e.getMessage()); + } + + @ExceptionHandler({DeadlineException.class}) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public Object handleDeadlineException(DeadlineException e) { + log.warn("<- Res [400]: Not valid", e); + return new DefaultLogicError() + .code(DefaultLogicError.CodeEnum.INVALIDDEADLINE) + .message(e.getMessage()); + } + + @ExceptionHandler(Exception.class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public void handleException(Exception e) { + log.error("<- Res [500]: Unrecognized inner error", e); + } + +} diff --git a/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java b/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java index 9db6628..b1e6a2c 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java +++ b/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java @@ -1,7 +1,6 @@ package com.rbkmoney.anapi.v2.controller; import com.rbkmoney.anapi.v2.converter.search.request.*; -import com.rbkmoney.anapi.v2.exception.BadRequestException; import com.rbkmoney.anapi.v2.service.SearchService; import com.rbkmoney.magista.*; import com.rbkmoney.openapi.anapi_v2.api.*; @@ -9,26 +8,18 @@ import com.rbkmoney.openapi.anapi_v2.model.*; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; -import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.context.request.NativeWebRequest; -import javax.validation.ConstraintViolation; -import javax.validation.ConstraintViolationException; import javax.validation.Valid; import javax.validation.constraints.*; import java.time.OffsetDateTime; import java.util.List; import java.util.Optional; -import java.util.Set; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeoutException; -import java.util.stream.Collectors; -import static com.rbkmoney.anapi.v2.util.DeadlineUtils.checkDeadline; +import static com.rbkmoney.anapi.v2.util.DeadlineUtil.checkDeadline; @Slf4j @@ -288,39 +279,4 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp InlineResponse20012 response = searchService.findRefunds(query); return ResponseEntity.ok(response); } - - @ExceptionHandler({ConstraintViolationException.class, BadRequestException.class, IllegalArgumentException.class, - InterruptedException.class, ExecutionException.class, TimeoutException.class}) - public ResponseEntity handleConstraintViolation(Exception ex) { - DefaultLogicError error; - if (ex instanceof ConstraintViolationException) { - log.warn("Invalid request: ", ex); - Set> constraintViolations = - ((ConstraintViolationException) ex).getConstraintViolations(); - String errorMessage = - constraintViolations.stream() - .map(violation -> violation.getPropertyPath() + ": " + violation.getMessage()) - .collect(Collectors.joining(", ")); - error = new DefaultLogicError() - .code(DefaultLogicError.CodeEnum.INVALIDREQUEST) - .message(errorMessage); - } else if (ex instanceof BadRequestException) { - log.warn("Invalid request: ", ex); - error = new DefaultLogicError() - .code(((BadRequestException) ex).getErrorCode()) - .message(ex.getMessage()); - } else if (ex instanceof InterruptedException) { - log.error("Internal error: ", ex); - Thread.currentThread().interrupt(); - return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); - } else if (ex instanceof TimeoutException) { - log.error("Request timeout: ", ex); - return new ResponseEntity<>(HttpStatus.GATEWAY_TIMEOUT); - } else { - log.error("Internal error: ", ex); - return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); - } - - return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST); - } } diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToChargebackSearchQueryConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToChargebackSearchQueryConverter.java index 0980b52..55b7b94 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToChargebackSearchQueryConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToChargebackSearchQueryConverter.java @@ -1,15 +1,19 @@ package com.rbkmoney.anapi.v2.converter.search.request; -import com.rbkmoney.anapi.v2.util.DamselUtil; +import com.rbkmoney.anapi.v2.exception.BadRequestException; +import com.rbkmoney.damsel.domain.*; import com.rbkmoney.magista.ChargebackSearchQuery; +import com.rbkmoney.openapi.anapi_v2.model.ChargebackCategory; +import com.rbkmoney.openapi.anapi_v2.model.ChargebackStage; +import com.rbkmoney.openapi.anapi_v2.model.ChargebackStatus; import org.springframework.stereotype.Component; import java.time.OffsetDateTime; import java.util.List; import java.util.stream.Collectors; -import static com.rbkmoney.anapi.v2.util.CommonUtil.merge; -import static com.rbkmoney.anapi.v2.util.DamselUtil.fillCommonParams; +import static com.rbkmoney.anapi.v2.util.ConverterUtil.fillCommonParams; +import static com.rbkmoney.anapi.v2.util.ConverterUtil.merge; @Component public class ParamsToChargebackSearchQueryConverter { @@ -38,20 +42,66 @@ public class ParamsToChargebackSearchQueryConverter { .setChargebackId(chargebackID) .setChargebackStatuses(chargebackStatuses != null ? chargebackStatuses.stream() - .map(DamselUtil::mapToDamselStatus) + .map(this::mapToDamselStatus) .collect(Collectors.toList()) : null ) .setChargebackStages(chargebackStages != null ? chargebackStages.stream() - .map(DamselUtil::mapToDamselStage) + .map(this::mapToDamselStage) .collect(Collectors.toList()) : null ) .setChargebackCategories(chargebackCategories != null ? chargebackCategories.stream() - .map(DamselUtil::mapToDamselCategory) + .map(this::mapToDamselCategory) .collect(Collectors.toList()) : null); } + + private InvoicePaymentChargebackStage mapToDamselStage(String chargebackStage) { + var stage = Enum.valueOf(ChargebackStage.class, chargebackStage); + var damselStage = new InvoicePaymentChargebackStage(); + switch (stage) { + case CHARGEBACK -> damselStage.setChargeback(new InvoicePaymentChargebackStageChargeback()); + case PRE_ARBITRATION -> damselStage.setPreArbitration(new InvoicePaymentChargebackStagePreArbitration()); + case ARBITRATION -> damselStage.setArbitration(new InvoicePaymentChargebackStageArbitration()); + default -> throw new BadRequestException( + String.format("Chargeback stage %s cannot be processed", chargebackStage)); + } + + return damselStage; + } + + private InvoicePaymentChargebackStatus mapToDamselStatus(String chargebackStatus) { + var status = Enum.valueOf(ChargebackStatus.class, chargebackStatus); + var damselStatus = new InvoicePaymentChargebackStatus(); + switch (status) { + case PENDING -> damselStatus.setPending(new InvoicePaymentChargebackPending()); + case ACCEPTED -> damselStatus.setAccepted(new InvoicePaymentChargebackAccepted()); + case REJECTED -> damselStatus.setRejected(new InvoicePaymentChargebackRejected()); + case CANCELLED -> damselStatus.setCancelled(new InvoicePaymentChargebackCancelled()); + default -> throw new BadRequestException( + String.format("Chargeback status %s cannot be processed", chargebackStatus)); + } + + return damselStatus; + } + + private InvoicePaymentChargebackCategory mapToDamselCategory(String chargebackCategory) { + var category = Enum.valueOf(ChargebackCategory.class, chargebackCategory); + var damselCategory = new InvoicePaymentChargebackCategory(); + switch (category) { + case FRAUD -> damselCategory.setFraud(new InvoicePaymentChargebackCategoryFraud()); + case DISPUTE -> damselCategory.setDispute(new InvoicePaymentChargebackCategoryDispute()); + case AUTHORISATION -> damselCategory + .setAuthorisation(new InvoicePaymentChargebackCategoryAuthorisation()); + case PROCESSING_ERROR -> damselCategory + .setProcessingError(new InvoicePaymentChargebackCategoryProcessingError()); + default -> throw new BadRequestException( + String.format("Chargeback category %s cannot be processed", chargebackCategory)); + } + + return damselCategory; + } } diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToInvoiceSearchQueryConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToInvoiceSearchQueryConverter.java index 7b7fb58..eea7230 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToInvoiceSearchQueryConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToInvoiceSearchQueryConverter.java @@ -7,9 +7,9 @@ import org.springframework.stereotype.Component; import java.time.OffsetDateTime; import java.util.List; -import static com.rbkmoney.anapi.v2.util.CommonUtil.merge; -import static com.rbkmoney.anapi.v2.util.DamselUtil.fillCommonParams; -import static com.rbkmoney.anapi.v2.util.DamselUtil.getStatus; +import static com.rbkmoney.anapi.v2.util.ConverterUtil.merge; +import static com.rbkmoney.anapi.v2.util.ConverterUtil.fillCommonParams; +import static com.rbkmoney.anapi.v2.util.ConverterUtil.mapStatus; @Component public class ParamsToInvoiceSearchQueryConverter { @@ -37,7 +37,7 @@ public class ParamsToInvoiceSearchQueryConverter { new PaymentParams() .setPaymentAmountFrom(invoiceAmountFrom) .setPaymentAmountTo(invoiceAmountTo) - .setPaymentStatus(invoiceStatus != null ? getStatus(invoiceStatus) : null) + .setPaymentStatus(invoiceStatus != null ? mapStatus(invoiceStatus) : null) ) .setInvoiceIds(merge(invoiceID, invoiceIDs)) .setExternalId(externalID); diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPaymentSearchQueryConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPaymentSearchQueryConverter.java index 07791bc..79a4f9b 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPaymentSearchQueryConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPaymentSearchQueryConverter.java @@ -1,8 +1,7 @@ package com.rbkmoney.anapi.v2.converter.search.request; -import com.rbkmoney.damsel.domain.LegacyBankCardPaymentSystem; -import com.rbkmoney.damsel.domain.LegacyBankCardTokenProvider; -import com.rbkmoney.damsel.domain.LegacyTerminalPaymentProvider; +import com.rbkmoney.anapi.v2.exception.BadRequestException; +import com.rbkmoney.damsel.domain.*; import com.rbkmoney.magista.PaymentParams; import com.rbkmoney.magista.PaymentSearchQuery; import com.rbkmoney.openapi.anapi_v2.model.BankCardPaymentSystem; @@ -12,8 +11,9 @@ import org.springframework.stereotype.Component; import java.time.OffsetDateTime; import java.util.List; -import static com.rbkmoney.anapi.v2.util.CommonUtil.merge; -import static com.rbkmoney.anapi.v2.util.DamselUtil.*; +import static com.rbkmoney.anapi.v2.util.ConverterUtil.merge; +import static com.rbkmoney.anapi.v2.util.ConverterUtil.fillCommonParams; +import static com.rbkmoney.anapi.v2.util.ConverterUtil.mapStatus; @Component public class ParamsToPaymentSearchQueryConverter { @@ -77,7 +77,7 @@ public class ParamsToPaymentSearchQueryConverter { .setPaymentId(paymentID) .setPaymentIp(payerIP) .setPaymentRrn(rrn) - .setPaymentStatus(paymentStatus != null ? getStatus(paymentStatus) : null) + .setPaymentStatus(paymentStatus != null ? mapStatus(paymentStatus) : null) .setPaymentSystem(bankCardPaymentSystem != null ? LegacyBankCardPaymentSystem.valueOf(bankCardPaymentSystem.getValue()) : null); @@ -90,4 +90,27 @@ public class ParamsToPaymentSearchQueryConverter { query.setPaymentParams(paymentParams); return query; } + + private PaymentTool mapToPaymentTool(String paymentMethod) { + var paymentTool = new PaymentTool(); + switch (paymentMethod) { + case "bankCard" -> paymentTool.setBankCard(new BankCard()); + case "paymentTerminal" -> paymentTool.setPaymentTerminal(new PaymentTerminal()); + default -> throw new BadRequestException( + String.format("Payment method %s cannot be processed", paymentMethod)); + } + + return paymentTool; + } + + private InvoicePaymentFlow mapToInvoicePaymentFlow(String paymentFlow) { + var invoicePaymentFlow = new InvoicePaymentFlow(); + switch (paymentFlow) { + case "instant" -> invoicePaymentFlow.setInstant(new InvoicePaymentFlowInstant()); + case "hold" -> invoicePaymentFlow.setHold(new InvoicePaymentFlowHold()); + default -> throw new BadRequestException( + String.format("Payment flow %s cannot be processed", paymentFlow)); + } + return invoicePaymentFlow; + } } diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPayoutSearchQueryConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPayoutSearchQueryConverter.java index a731bb0..900b5f9 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPayoutSearchQueryConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPayoutSearchQueryConverter.java @@ -1,14 +1,18 @@ package com.rbkmoney.anapi.v2.converter.search.request; +import com.rbkmoney.anapi.v2.exception.BadRequestException; +import com.rbkmoney.damsel.domain.PaymentInstitutionAccount; +import com.rbkmoney.damsel.domain.PayoutToolInfo; +import com.rbkmoney.damsel.domain.RussianBankAccount; +import com.rbkmoney.damsel.domain.WalletInfo; import com.rbkmoney.magista.PayoutSearchQuery; import org.springframework.stereotype.Component; import java.time.OffsetDateTime; import java.util.List; -import static com.rbkmoney.anapi.v2.util.CommonUtil.merge; -import static com.rbkmoney.anapi.v2.util.DamselUtil.fillCommonParams; -import static com.rbkmoney.anapi.v2.util.DamselUtil.mapToDamselPayoutToolInfo; +import static com.rbkmoney.anapi.v2.util.ConverterUtil.fillCommonParams; +import static com.rbkmoney.anapi.v2.util.ConverterUtil.merge; @Component public class ParamsToPayoutSearchQueryConverter { @@ -33,4 +37,19 @@ public class ParamsToPayoutSearchQueryConverter { .setPayoutId(payoutID) .setPayoutType(payoutToolType != null ? mapToDamselPayoutToolInfo(payoutToolType) : null); } + + private PayoutToolInfo mapToDamselPayoutToolInfo(String payoutToolType) { + var payoutToolInfo = new PayoutToolInfo(); + switch (payoutToolType) { + case "PayoutAccount" -> payoutToolInfo + .setRussianBankAccount(new RussianBankAccount()); //TODO: Russian or international? + case "Wallet" -> payoutToolInfo.setWalletInfo(new WalletInfo()); + case "PaymentInstitutionAccount" -> payoutToolInfo + .setPaymentInstitutionAccount(new PaymentInstitutionAccount()); + default -> throw new BadRequestException( + String.format("PayoutToolType %s cannot be processed", payoutToolType)); + } + + return payoutToolInfo; + } } diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToRefundSearchQueryConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToRefundSearchQueryConverter.java index 2f85123..11daf5e 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToRefundSearchQueryConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToRefundSearchQueryConverter.java @@ -1,14 +1,19 @@ package com.rbkmoney.anapi.v2.converter.search.request; +import com.rbkmoney.anapi.v2.exception.BadRequestException; +import com.rbkmoney.damsel.domain.InvoicePaymentRefundFailed; +import com.rbkmoney.damsel.domain.InvoicePaymentRefundPending; +import com.rbkmoney.damsel.domain.InvoicePaymentRefundStatus; +import com.rbkmoney.damsel.domain.InvoicePaymentRefundSucceeded; import com.rbkmoney.magista.RefundSearchQuery; +import com.rbkmoney.openapi.anapi_v2.model.RefundStatus; import org.springframework.stereotype.Component; import java.time.OffsetDateTime; import java.util.List; -import static com.rbkmoney.anapi.v2.util.CommonUtil.merge; -import static com.rbkmoney.anapi.v2.util.DamselUtil.fillCommonParams; -import static com.rbkmoney.anapi.v2.util.DamselUtil.getRefundStatus; +import static com.rbkmoney.anapi.v2.util.ConverterUtil.fillCommonParams; +import static com.rbkmoney.anapi.v2.util.ConverterUtil.merge; @Component public class ParamsToRefundSearchQueryConverter { @@ -40,4 +45,16 @@ public class ParamsToRefundSearchQueryConverter { .setRefundId(refundID); } + private InvoicePaymentRefundStatus getRefundStatus(String refundStatus) { + var invoicePaymentRefundStatus = new InvoicePaymentRefundStatus(); + switch (Enum.valueOf(RefundStatus.StatusEnum.class, refundStatus)) { + case PENDING -> invoicePaymentRefundStatus.setPending(new InvoicePaymentRefundPending()); + case SUCCEEDED -> invoicePaymentRefundStatus.setSucceeded(new InvoicePaymentRefundSucceeded()); + case FAILED -> invoicePaymentRefundStatus.setFailed(new InvoicePaymentRefundFailed()); + default -> throw new BadRequestException( + String.format("Refund status %s cannot be processed", refundStatus)); + } + return invoicePaymentRefundStatus; + } + } diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatChargebackToChargebackConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatChargebackToChargebackConverter.java index b1377da..7008ef6 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatChargebackToChargebackConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatChargebackToChargebackConverter.java @@ -1,16 +1,16 @@ package com.rbkmoney.anapi.v2.converter.search.response; +import com.rbkmoney.damsel.domain.InvoicePaymentChargebackCategory; import com.rbkmoney.geck.common.util.TypeUtil; import com.rbkmoney.magista.StatChargeback; import com.rbkmoney.openapi.anapi_v2.model.Chargeback; +import com.rbkmoney.openapi.anapi_v2.model.ChargebackCategory; import com.rbkmoney.openapi.anapi_v2.model.ChargebackReason; import com.rbkmoney.openapi.anapi_v2.model.Content; import org.springframework.stereotype.Component; import java.time.ZoneOffset; -import static com.rbkmoney.anapi.v2.util.OpenApiUtil.mapToChargebackCategory; - @Component public class StatChargebackToChargebackConverter { @@ -29,4 +29,26 @@ public class StatChargebackToChargebackConverter { .type(chargeback.getContent().getType()) : null) .bodyCurrency(chargeback.getCurrencyCode().getSymbolicCode()); } + + private ChargebackCategory mapToChargebackCategory(InvoicePaymentChargebackCategory chargebackCategory) { + if (chargebackCategory.isSetAuthorisation()) { + //TODO: Is it a typo? Could be fixed? (authorization) + return ChargebackCategory.AUTHORISATION; + } + + if (chargebackCategory.isSetDispute()) { + return ChargebackCategory.DISPUTE; + } + + if (chargebackCategory.isSetFraud()) { + return ChargebackCategory.FRAUD; + } + + if (chargebackCategory.isSetProcessingError()) { + return ChargebackCategory.PROCESSING_ERROR; + } + + throw new IllegalArgumentException( + String.format("Chargeback category %s cannot be processed", chargebackCategory)); + } } diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatInvoiceToInvoiceConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatInvoiceToInvoiceConverter.java index 0d8d971..103b459 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatInvoiceToInvoiceConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatInvoiceToInvoiceConverter.java @@ -1,7 +1,7 @@ package com.rbkmoney.anapi.v2.converter.search.response; -import com.rbkmoney.anapi.v2.util.OpenApiUtil; import com.rbkmoney.geck.common.util.TypeUtil; +import com.rbkmoney.magista.InvoiceStatus; import com.rbkmoney.magista.StatInvoice; import com.rbkmoney.openapi.anapi_v2.model.Invoice; import com.rbkmoney.openapi.anapi_v2.model.InvoiceLine; @@ -32,7 +32,28 @@ public class StatInvoiceToInvoiceConverter { .product(invoice.getProduct()) //.reason() .shopID(invoice.getShopId()) - .status(OpenApiUtil.mapToInvoiceStatus(invoice.getStatus())); + .status(mapToInvoiceStatus(invoice.getStatus())); + } + + private Invoice.StatusEnum mapToInvoiceStatus(InvoiceStatus status) { + if (status.isSetFulfilled()) { + return Invoice.StatusEnum.FULFILLED; + } + + if (status.isSetPaid()) { + return Invoice.StatusEnum.PAID; + } + + if (status.isSetUnpaid()) { + return Invoice.StatusEnum.UNPAID; + } + + if (status.isSetCancelled()) { + return Invoice.StatusEnum.CANCELLED; + } + + throw new IllegalArgumentException( + String.format("Invoice status %s cannot be processed", status)); } } diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverter.java index 0fe19bb..d917d77 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverter.java @@ -2,17 +2,12 @@ package com.rbkmoney.anapi.v2.converter.search.response; import com.rbkmoney.geck.common.util.TypeUtil; import com.rbkmoney.magista.StatPayment; -import com.rbkmoney.openapi.anapi_v2.model.GeoLocationInfo; -import com.rbkmoney.openapi.anapi_v2.model.PaymentFlow; -import com.rbkmoney.openapi.anapi_v2.model.PaymentSearchResult; -import com.rbkmoney.openapi.anapi_v2.model.TransactionInfo; +import com.rbkmoney.openapi.anapi_v2.model.*; import org.springframework.stereotype.Component; +import java.time.OffsetDateTime; import java.time.ZoneOffset; -import static com.rbkmoney.anapi.v2.util.OpenApiUtil.fillPaymentStatusInfo; -import static com.rbkmoney.anapi.v2.util.OpenApiUtil.getPayer; - @Component public class StatPaymentToPaymentSearchResultConverter { @@ -44,4 +39,84 @@ public class StatPaymentToPaymentSearchResultConverter { fillPaymentStatusInfo(payment, result); return result; } + + private Payer getPayer(StatPayment payment) { + var statPayer = payment.getPayer(); + Payer payer = new Payer(); + + if (statPayer.isSetCustomer()) { + return payer.payerType(Payer.PayerTypeEnum.CUSTOMERPAYER); + } + + if (statPayer.isSetPaymentResource()) { + return payer.payerType(Payer.PayerTypeEnum.PAYMENTRESOURCEPAYER); + } + + if (statPayer.isSetRecurrent()) { + return payer.payerType(Payer.PayerTypeEnum.RECURRENTPAYER); + } + + throw new IllegalArgumentException( + String.format("Payer %s cannot be processed", statPayer)); + } + + private void fillPaymentStatusInfo(StatPayment payment, PaymentSearchResult result) { + var status = payment.getStatus(); + if (status.isSetCancelled()) { + OffsetDateTime createdAt = status.getCancelled().getAt() != null + ? TypeUtil.stringToInstant(status.getCancelled().getAt()).atOffset(ZoneOffset.UTC) + : null; + result.status(PaymentSearchResult.StatusEnum.CANCELLED) + .createdAt(createdAt); + return; + } + + if (status.isSetCaptured()) { + OffsetDateTime createdAt = status.getCaptured().getAt() != null + ? TypeUtil.stringToInstant(status.getCaptured().getAt()).atOffset(ZoneOffset.UTC) + : null; + result.status(PaymentSearchResult.StatusEnum.CAPTURED) + .createdAt(createdAt); + return; + } + + if (status.isSetChargedBack()) { + //TODO: Clearify + } + + if (status.isSetFailed()) { + OffsetDateTime createdAt = status.getFailed().getAt() != null + ? TypeUtil.stringToInstant(status.getFailed().getAt()).atOffset(ZoneOffset.UTC) + : null; + result.status(PaymentSearchResult.StatusEnum.FAILED) + .createdAt(createdAt); + return; + } + + if (status.isSetPending()) { + result.status(PaymentSearchResult.StatusEnum.PENDING); + return; + } + + if (status.isSetProcessed()) { + OffsetDateTime createdAt = status.getProcessed().getAt() != null + ? TypeUtil.stringToInstant(status.getProcessed().getAt()).atOffset(ZoneOffset.UTC) + : null; + result.status(PaymentSearchResult.StatusEnum.PROCESSED) + .createdAt(createdAt); + return; + } + + if (status.isSetRefunded()) { + OffsetDateTime createdAt = status.getRefunded().getAt() != null + ? TypeUtil.stringToInstant(status.getRefunded().getAt()).atOffset(ZoneOffset.UTC) + : null; + result.status(PaymentSearchResult.StatusEnum.REFUNDED) + .createdAt(createdAt); + return; + } + + throw new IllegalArgumentException( + String.format("Payment status %s cannot be processed", payment.getStatus())); + } } diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPayoutToPayoutConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPayoutToPayoutConverter.java index e2acf5d..fb426ab 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPayoutToPayoutConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPayoutToPayoutConverter.java @@ -1,15 +1,16 @@ package com.rbkmoney.anapi.v2.converter.search.response; -import com.rbkmoney.anapi.v2.util.OpenApiUtil; +import com.rbkmoney.damsel.domain.CountryCode; +import com.rbkmoney.damsel.domain.PayoutToolInfo; import com.rbkmoney.geck.common.util.TypeUtil; +import com.rbkmoney.magista.PayoutStatus; import com.rbkmoney.magista.StatPayout; -import com.rbkmoney.openapi.anapi_v2.model.Payout; +import com.rbkmoney.openapi.anapi_v2.model.*; import org.springframework.stereotype.Component; +import javax.annotation.Nullable; import java.time.ZoneOffset; -import static com.rbkmoney.anapi.v2.util.OpenApiUtil.mapToPayoutToolDetails; - @Component public class StatPayoutToPayoutConverter { @@ -22,10 +23,101 @@ public class StatPayoutToPayoutConverter { .id(payout.getId()) .payoutToolDetails(mapToPayoutToolDetails(payout.getPayoutToolInfo())) .shopID(payout.getShopId()) - .status(OpenApiUtil.mapToPayoutStatus(payout.getStatus())) + .status(mapToPayoutStatus(payout.getStatus())) .cancellationDetails( payout.getStatus().isSetCancelled() ? payout.getStatus().getCancelled().getDetails() : null); } + + private String mapToPayoutStatus(PayoutStatus status) { + if (status.isSetCancelled()) { + return "Cancelled"; + } + + if (status.isSetPaid()) { + return "Paid"; + } + + if (status.isSetConfirmed()) { + return "Confirmed"; + } + + if (status.isSetUnpaid()) { + return "Unpaid"; + } + + throw new IllegalArgumentException( + String.format("Payout status %s cannot be processed", status)); + } + + private PayoutToolDetails mapToPayoutToolDetails(PayoutToolInfo payoutToolInfo) { + if (payoutToolInfo.isSetRussianBankAccount()) { + var account = payoutToolInfo.getRussianBankAccount(); + return new PayoutToolDetailsBankAccount() + .account(account.getAccount()) + .bankBik(account.getBankBik()) + .bankName(account.getBankName()) + .bankPostAccount(account.getBankPostAccount()) + .detailsType("PayoutToolDetailsBankAccount"); + } + + if (payoutToolInfo.isSetInternationalBankAccount()) { + var account = payoutToolInfo.getInternationalBankAccount(); + return new PayoutToolDetailsInternationalBankAccount() + .iban(account.getIban()) + .number(account.getNumber()) + .bankDetails(account.getBank() != null + ? new InternationalBankDetails() + .name(account.getBank().getName()) + .bic(account.getBank().getBic()) + .countryCode(getCountryCode(account.getBank().getCountry())) + .address(account.getBank().getAddress()) + .abartn(account.getBank().getAbaRtn()) + : null) + .correspondentBankAccount(mapToInternationalCorrespondentBankAccount(account)) + .detailsType("PayoutToolDetailsInternationalBankAccount"); + } + + if (payoutToolInfo.isSetPaymentInstitutionAccount()) { + return new PayoutToolDetailsPaymentInstitutionAccount() + .detailsType("PayoutToolDetailsPaymentInstitutionAccount"); + } + + if (payoutToolInfo.isSetWalletInfo()) { + return new PayoutToolDetailsWalletInfo() + .walletID(payoutToolInfo.getWalletInfo().getWalletId()) + .detailsType("PayoutToolDetailsWalletInfo"); + } + + throw new IllegalArgumentException( + String.format("PayoutToolInfo %s cannot be processed", payoutToolInfo)); + + } + + private String getCountryCode(@Nullable CountryCode countryCode) { + if (countryCode == null) { + return null; + } + return countryCode.name(); + } + + private InternationalCorrespondentBankAccount mapToInternationalCorrespondentBankAccount( + com.rbkmoney.damsel.domain.InternationalBankAccount account) { + var details = account.getBank(); + return new InternationalCorrespondentBankAccount() + .bankDetails(details != null + ? new InternationalBankDetails() + .name(details.getName()) + .bic(details.getBic()) + .countryCode(details.getCountry().name()) + .address(details.getAddress()) + .abartn(details.getAbaRtn()) + : null) + .iban(account.getIban()) + .number(account.getNumber()) + .correspondentBankAccount(account.getCorrespondentAccount() != null + ? mapToInternationalCorrespondentBankAccount(account.getCorrespondentAccount()) + : null); + } } diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatRefundToRefundSearchResultConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatRefundToRefundSearchResultConverter.java index 8a24524..2a4c504 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatRefundToRefundSearchResultConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatRefundToRefundSearchResultConverter.java @@ -1,6 +1,7 @@ package com.rbkmoney.anapi.v2.converter.search.response; import com.rbkmoney.geck.common.util.TypeUtil; +import com.rbkmoney.magista.InvoicePaymentRefundStatus; import com.rbkmoney.magista.StatRefund; import com.rbkmoney.openapi.anapi_v2.model.RefundSearchResult; import com.rbkmoney.openapi.anapi_v2.model.RefundStatusError; @@ -8,8 +9,6 @@ import org.springframework.stereotype.Component; import java.time.ZoneOffset; -import static com.rbkmoney.anapi.v2.util.OpenApiUtil.mapToRefundStatus; - @Component public class StatRefundToRefundSearchResultConverter { @@ -32,4 +31,21 @@ public class StatRefundToRefundSearchResultConverter { .paymentID(refund.getPaymentId()) .reason(refund.getReason()); } + + private RefundSearchResult.StatusEnum mapToRefundStatus(InvoicePaymentRefundStatus status) { + if (status.isSetPending()) { + return RefundSearchResult.StatusEnum.PENDING; + } + + if (status.isSetFailed()) { + return RefundSearchResult.StatusEnum.FAILED; + } + + if (status.isSetSucceeded()) { + return RefundSearchResult.StatusEnum.SUCCEEDED; + } + + throw new IllegalArgumentException( + String.format("Refund status %s cannot be processed", status)); + } } diff --git a/src/main/java/com/rbkmoney/anapi/v2/util/CommonUtil.java b/src/main/java/com/rbkmoney/anapi/v2/util/CommonUtil.java deleted file mode 100644 index a4a9c3d..0000000 --- a/src/main/java/com/rbkmoney/anapi/v2/util/CommonUtil.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.rbkmoney.anapi.v2.util; - -import lombok.experimental.UtilityClass; - -import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.List; - -@UtilityClass -public class CommonUtil { - - public static List merge(@Nullable String id, @Nullable List ids) { - if (id != null) { - if (ids == null) { - ids = new ArrayList<>(); - } - ids.add(id); - } - return ids; - } - -} diff --git a/src/main/java/com/rbkmoney/anapi/v2/util/ConverterUtil.java b/src/main/java/com/rbkmoney/anapi/v2/util/ConverterUtil.java new file mode 100644 index 0000000..7cb67ab --- /dev/null +++ b/src/main/java/com/rbkmoney/anapi/v2/util/ConverterUtil.java @@ -0,0 +1,56 @@ +package com.rbkmoney.anapi.v2.util; + +import com.rbkmoney.anapi.v2.exception.BadRequestException; +import com.rbkmoney.damsel.domain.*; +import com.rbkmoney.geck.common.util.TypeUtil; +import com.rbkmoney.magista.CommonSearchQueryParams; +import com.rbkmoney.openapi.anapi_v2.model.PaymentStatus; +import lombok.experimental.UtilityClass; + +import javax.annotation.Nullable; +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.List; + +@UtilityClass +public class ConverterUtil { + + public static List merge(@Nullable String id, @Nullable List ids) { + if (id != null) { + if (ids == null) { + ids = new ArrayList<>(); + } + ids.add(id); + } + return ids; + } + + public static CommonSearchQueryParams fillCommonParams(OffsetDateTime fromTime, OffsetDateTime toTime, + Integer limit, + String partyId, List shopIDs, + String continuationToken) { + return new CommonSearchQueryParams() + .setContinuationToken(continuationToken) + .setFromTime(TypeUtil.temporalToString(fromTime.toLocalDateTime())) + .setToTime(TypeUtil.temporalToString(toTime.toLocalDateTime())) + .setLimit(limit) + .setPartyId(partyId) + .setShopIds(shopIDs); + } + + public static InvoicePaymentStatus mapStatus(String paymentStatus) { + var status = Enum.valueOf(PaymentStatus.StatusEnum.class, paymentStatus); + var invoicePaymentStatus = new com.rbkmoney.damsel.domain.InvoicePaymentStatus(); + switch (status) { + case PENDING -> invoicePaymentStatus.setPending(new InvoicePaymentPending()); + case PROCESSED -> invoicePaymentStatus.setProcessed(new InvoicePaymentProcessed()); + case CAPTURED -> invoicePaymentStatus.setCaptured(new InvoicePaymentCaptured()); + case CANCELLED -> invoicePaymentStatus.setCancelled(new InvoicePaymentCancelled()); + case REFUNDED -> invoicePaymentStatus.setRefunded(new InvoicePaymentRefunded()); + case FAILED -> invoicePaymentStatus.setFailed(new InvoicePaymentFailed()); + default -> throw new BadRequestException( + String.format("Payment status %s cannot be processed", paymentStatus)); + } + return invoicePaymentStatus; + } +} diff --git a/src/main/java/com/rbkmoney/anapi/v2/util/DamselUtil.java b/src/main/java/com/rbkmoney/anapi/v2/util/DamselUtil.java deleted file mode 100644 index 126e358..0000000 --- a/src/main/java/com/rbkmoney/anapi/v2/util/DamselUtil.java +++ /dev/null @@ -1,140 +0,0 @@ -package com.rbkmoney.anapi.v2.util; - -import com.rbkmoney.anapi.v2.exception.BadRequestException; -import com.rbkmoney.damsel.domain.*; -import com.rbkmoney.geck.common.util.TypeUtil; -import com.rbkmoney.magista.CommonSearchQueryParams; -import com.rbkmoney.openapi.anapi_v2.model.*; -import lombok.experimental.UtilityClass; - -import java.time.OffsetDateTime; -import java.util.List; - -@UtilityClass -public class DamselUtil { - - public static PaymentTool mapToPaymentTool(String paymentMethod) { - var paymentTool = new PaymentTool(); - switch (paymentMethod) { - case "bankCard" -> paymentTool.setBankCard(new BankCard()); - case "paymentTerminal" -> paymentTool.setPaymentTerminal(new PaymentTerminal()); - default -> throw new BadRequestException( - String.format("Payment method %s cannot be processed", paymentMethod)); - } - - return paymentTool; - } - - public static InvoicePaymentFlow mapToInvoicePaymentFlow(String paymentFlow) { - var invoicePaymentFlow = new InvoicePaymentFlow(); - switch (paymentFlow) { - case "instant" -> invoicePaymentFlow.setInstant(new InvoicePaymentFlowInstant()); - case "hold" -> invoicePaymentFlow.setHold(new InvoicePaymentFlowHold()); - default -> throw new BadRequestException( - String.format("Payment flow %s cannot be processed", paymentFlow)); - } - return invoicePaymentFlow; - } - - public static CommonSearchQueryParams fillCommonParams(OffsetDateTime fromTime, OffsetDateTime toTime, - Integer limit, - String partyId, List shopIDs, - String continuationToken) { - return new CommonSearchQueryParams() - .setContinuationToken(continuationToken) - .setFromTime(TypeUtil.temporalToString(fromTime.toLocalDateTime())) - .setToTime(TypeUtil.temporalToString(toTime.toLocalDateTime())) - .setLimit(limit) - .setPartyId(partyId) - .setShopIds(shopIDs); - } - - public static com.rbkmoney.damsel.domain.InvoicePaymentStatus getStatus(String paymentStatus) { - var status = Enum.valueOf(PaymentStatus.StatusEnum.class, paymentStatus); - var invoicePaymentStatus = new com.rbkmoney.damsel.domain.InvoicePaymentStatus(); - switch (status) { - case PENDING -> invoicePaymentStatus.setPending(new InvoicePaymentPending()); - case PROCESSED -> invoicePaymentStatus.setProcessed(new InvoicePaymentProcessed()); - case CAPTURED -> invoicePaymentStatus.setCaptured(new InvoicePaymentCaptured()); - case CANCELLED -> invoicePaymentStatus.setCancelled(new InvoicePaymentCancelled()); - case REFUNDED -> invoicePaymentStatus.setRefunded(new InvoicePaymentRefunded()); - case FAILED -> invoicePaymentStatus.setFailed(new InvoicePaymentFailed()); - default -> throw new BadRequestException( - String.format("Payment status %s cannot be processed", paymentStatus)); - } - return invoicePaymentStatus; - } - - public static InvoicePaymentChargebackStage mapToDamselStage(String chargebackStage) { - var stage = Enum.valueOf(ChargebackStage.class, chargebackStage); - var damselStage = new InvoicePaymentChargebackStage(); - switch (stage) { - case CHARGEBACK -> damselStage.setChargeback(new InvoicePaymentChargebackStageChargeback()); - case PRE_ARBITRATION -> damselStage.setPreArbitration(new InvoicePaymentChargebackStagePreArbitration()); - case ARBITRATION -> damselStage.setArbitration(new InvoicePaymentChargebackStageArbitration()); - default -> throw new BadRequestException( - String.format("Chargeback stage %s cannot be processed", chargebackStage)); - } - - return damselStage; - } - - public static InvoicePaymentChargebackStatus mapToDamselStatus(String chargebackStatus) { - var status = Enum.valueOf(ChargebackStatus.class, chargebackStatus); - var damselStatus = new InvoicePaymentChargebackStatus(); - switch (status) { - case PENDING -> damselStatus.setPending(new InvoicePaymentChargebackPending()); - case ACCEPTED -> damselStatus.setAccepted(new InvoicePaymentChargebackAccepted()); - case REJECTED -> damselStatus.setRejected(new InvoicePaymentChargebackRejected()); - case CANCELLED -> damselStatus.setCancelled(new InvoicePaymentChargebackCancelled()); - default -> throw new BadRequestException( - String.format("Chargeback status %s cannot be processed", chargebackStatus)); - } - - return damselStatus; - } - - public static InvoicePaymentChargebackCategory mapToDamselCategory(String chargebackCategory) { - var category = Enum.valueOf(ChargebackCategory.class, chargebackCategory); - var damselCategory = new InvoicePaymentChargebackCategory(); - switch (category) { - case FRAUD -> damselCategory.setFraud(new InvoicePaymentChargebackCategoryFraud()); - case DISPUTE -> damselCategory.setDispute(new InvoicePaymentChargebackCategoryDispute()); - case AUTHORISATION -> damselCategory - .setAuthorisation(new InvoicePaymentChargebackCategoryAuthorisation()); - case PROCESSING_ERROR -> damselCategory - .setProcessingError(new InvoicePaymentChargebackCategoryProcessingError()); - default -> throw new BadRequestException( - String.format("Chargeback category %s cannot be processed", chargebackCategory)); - } - - return damselCategory; - } - - public static PayoutToolInfo mapToDamselPayoutToolInfo(String payoutToolType) { - var payoutToolInfo = new PayoutToolInfo(); - switch (payoutToolType) { - case "PayoutAccount" -> payoutToolInfo - .setRussianBankAccount(new RussianBankAccount()); //TODO: Russian or international? - case "Wallet" -> payoutToolInfo.setWalletInfo(new WalletInfo()); - case "PaymentInstitutionAccount" -> payoutToolInfo - .setPaymentInstitutionAccount(new PaymentInstitutionAccount()); - default -> throw new BadRequestException( - String.format("PayoutToolType %s cannot be processed", payoutToolType)); - } - - return payoutToolInfo; - } - - public static InvoicePaymentRefundStatus getRefundStatus(String refundStatus) { - var invoicePaymentRefundStatus = new InvoicePaymentRefundStatus(); - switch (Enum.valueOf(RefundStatus.StatusEnum.class, refundStatus)) { - case PENDING -> invoicePaymentRefundStatus.setPending(new InvoicePaymentRefundPending()); - case SUCCEEDED -> invoicePaymentRefundStatus.setSucceeded(new InvoicePaymentRefundSucceeded()); - case FAILED -> invoicePaymentRefundStatus.setFailed(new InvoicePaymentRefundFailed()); - default -> throw new BadRequestException( - String.format("Refund status %s cannot be processed", refundStatus)); - } - return invoicePaymentRefundStatus; - } -} diff --git a/src/main/java/com/rbkmoney/anapi/v2/util/DeadlineUtils.java b/src/main/java/com/rbkmoney/anapi/v2/util/DeadlineUtil.java similarity index 99% rename from src/main/java/com/rbkmoney/anapi/v2/util/DeadlineUtils.java rename to src/main/java/com/rbkmoney/anapi/v2/util/DeadlineUtil.java index 86b0245..e2ee821 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/util/DeadlineUtils.java +++ b/src/main/java/com/rbkmoney/anapi/v2/util/DeadlineUtil.java @@ -12,7 +12,7 @@ import java.util.regex.Pattern; @UtilityClass @SuppressWarnings("ParameterName") -public class DeadlineUtils { +public class DeadlineUtil { public static void checkDeadline(String xRequestDeadline, String xRequestId) { if (xRequestDeadline == null) { diff --git a/src/main/java/com/rbkmoney/anapi/v2/util/OpenApiUtil.java b/src/main/java/com/rbkmoney/anapi/v2/util/OpenApiUtil.java deleted file mode 100644 index 246b5e0..0000000 --- a/src/main/java/com/rbkmoney/anapi/v2/util/OpenApiUtil.java +++ /dev/null @@ -1,252 +0,0 @@ -package com.rbkmoney.anapi.v2.util; - -import com.rbkmoney.damsel.domain.CountryCode; -import com.rbkmoney.damsel.domain.InvoicePaymentChargebackCategory; -import com.rbkmoney.damsel.domain.PayoutToolInfo; -import com.rbkmoney.geck.common.util.TypeUtil; -import com.rbkmoney.magista.InvoicePaymentRefundStatus; -import com.rbkmoney.magista.InvoiceStatus; -import com.rbkmoney.magista.PayoutStatus; -import com.rbkmoney.magista.StatPayment; -import com.rbkmoney.openapi.anapi_v2.model.*; -import lombok.experimental.UtilityClass; - -import javax.annotation.Nullable; -import java.time.OffsetDateTime; -import java.time.ZoneOffset; - -@UtilityClass -public class OpenApiUtil { - - public static ChargebackCategory mapToChargebackCategory(InvoicePaymentChargebackCategory chargebackCategory) { - if (chargebackCategory.isSetAuthorisation()) { - //TODO: Is it a typo? Could be fixed? (authorization) - return ChargebackCategory.AUTHORISATION; - } - - if (chargebackCategory.isSetDispute()) { - return ChargebackCategory.DISPUTE; - } - - if (chargebackCategory.isSetFraud()) { - return ChargebackCategory.FRAUD; - } - - if (chargebackCategory.isSetProcessingError()) { - return ChargebackCategory.PROCESSING_ERROR; - } - - throw new IllegalArgumentException( - String.format("Chargeback category %s cannot be processed", chargebackCategory)); - } - - public static Invoice.StatusEnum mapToInvoiceStatus(InvoiceStatus status) { - if (status.isSetFulfilled()) { - return Invoice.StatusEnum.FULFILLED; - } - - if (status.isSetPaid()) { - return Invoice.StatusEnum.PAID; - } - - if (status.isSetUnpaid()) { - return Invoice.StatusEnum.UNPAID; - } - - if (status.isSetCancelled()) { - return Invoice.StatusEnum.CANCELLED; - } - - throw new IllegalArgumentException( - String.format("Invoice status %s cannot be processed", status)); - } - - public static String mapToPayoutStatus(PayoutStatus status) { - if (status.isSetCancelled()) { - return "Cancelled"; - } - - if (status.isSetPaid()) { - return "Paid"; - } - - if (status.isSetConfirmed()) { - return "Confirmed"; - } - - if (status.isSetUnpaid()) { - return "Unpaid"; - } - - throw new IllegalArgumentException( - String.format("Payout status %s cannot be processed", status)); - } - - public static PayoutToolDetails mapToPayoutToolDetails(PayoutToolInfo payoutToolInfo) { - if (payoutToolInfo.isSetRussianBankAccount()) { - var account = payoutToolInfo.getRussianBankAccount(); - return new PayoutToolDetailsBankAccount() - .account(account.getAccount()) - .bankBik(account.getBankBik()) - .bankName(account.getBankName()) - .bankPostAccount(account.getBankPostAccount()) - .detailsType("PayoutToolDetailsBankAccount"); - } - - if (payoutToolInfo.isSetInternationalBankAccount()) { - var account = payoutToolInfo.getInternationalBankAccount(); - return new PayoutToolDetailsInternationalBankAccount() - .iban(account.getIban()) - .number(account.getNumber()) - .bankDetails(account.getBank() != null - ? new InternationalBankDetails() - .name(account.getBank().getName()) - .bic(account.getBank().getBic()) - .countryCode(getCountryCode(account.getBank().getCountry())) - .address(account.getBank().getAddress()) - .abartn(account.getBank().getAbaRtn()) - : null) - .correspondentBankAccount(mapToInternationalCorrespondentBankAccount(account)) - .detailsType("PayoutToolDetailsInternationalBankAccount"); - } - - if (payoutToolInfo.isSetPaymentInstitutionAccount()) { - return new PayoutToolDetailsPaymentInstitutionAccount() - .detailsType("PayoutToolDetailsPaymentInstitutionAccount"); - } - - if (payoutToolInfo.isSetWalletInfo()) { - return new PayoutToolDetailsWalletInfo() - .walletID(payoutToolInfo.getWalletInfo().getWalletId()) - .detailsType("PayoutToolDetailsWalletInfo"); - } - - throw new IllegalArgumentException( - String.format("PayoutToolInfo %s cannot be processed", payoutToolInfo)); - - } - - private static String getCountryCode(@Nullable CountryCode countryCode) { - if (countryCode == null) { - return null; - } - - return countryCode.name(); - } - - public static InternationalCorrespondentBankAccount mapToInternationalCorrespondentBankAccount( - com.rbkmoney.damsel.domain.InternationalBankAccount account) { - var details = account.getBank(); - return new InternationalCorrespondentBankAccount() - .bankDetails(details != null - ? new InternationalBankDetails() - .name(details.getName()) - .bic(details.getBic()) - .countryCode(details.getCountry().name()) - .address(details.getAddress()) - .abartn(details.getAbaRtn()) - : null) - .iban(account.getIban()) - .number(account.getNumber()) - .correspondentBankAccount(account.getCorrespondentAccount() != null - ? mapToInternationalCorrespondentBankAccount(account.getCorrespondentAccount()) - : null); - } - - public static RefundSearchResult.StatusEnum mapToRefundStatus(InvoicePaymentRefundStatus status) { - if (status.isSetPending()) { - return RefundSearchResult.StatusEnum.PENDING; - } - - if (status.isSetFailed()) { - return RefundSearchResult.StatusEnum.FAILED; - } - - if (status.isSetSucceeded()) { - return RefundSearchResult.StatusEnum.SUCCEEDED; - } - - throw new IllegalArgumentException( - String.format("Refund status %s cannot be processed", status)); - } - - public static Payer getPayer(StatPayment payment) { - var statPayer = payment.getPayer(); - Payer payer = new Payer(); - - if (statPayer.isSetCustomer()) { - return payer.payerType(Payer.PayerTypeEnum.CUSTOMERPAYER); - } - - if (statPayer.isSetPaymentResource()) { - return payer.payerType(Payer.PayerTypeEnum.PAYMENTRESOURCEPAYER); - } - - if (statPayer.isSetRecurrent()) { - return payer.payerType(Payer.PayerTypeEnum.RECURRENTPAYER); - } - - throw new IllegalArgumentException( - String.format("Payer %s cannot be processed", statPayer)); - } - - public static void fillPaymentStatusInfo(StatPayment payment, PaymentSearchResult result) { - var status = payment.getStatus(); - if (status.isSetCancelled()) { - OffsetDateTime createdAt = status.getCancelled().getAt() != null - ? TypeUtil.stringToInstant(status.getCancelled().getAt()).atOffset(ZoneOffset.UTC) - : null; - result.status(PaymentSearchResult.StatusEnum.CANCELLED) - .createdAt(createdAt); - return; - } - - if (status.isSetCaptured()) { - OffsetDateTime createdAt = status.getCaptured().getAt() != null - ? TypeUtil.stringToInstant(status.getCaptured().getAt()).atOffset(ZoneOffset.UTC) - : null; - result.status(PaymentSearchResult.StatusEnum.CAPTURED) - .createdAt(createdAt); - return; - } - - if (status.isSetChargedBack()) { - //TODO: Clearify - } - - if (status.isSetFailed()) { - OffsetDateTime createdAt = status.getFailed().getAt() != null - ? TypeUtil.stringToInstant(status.getFailed().getAt()).atOffset(ZoneOffset.UTC) - : null; - result.status(PaymentSearchResult.StatusEnum.FAILED) - .createdAt(createdAt); - return; - } - - if (status.isSetPending()) { - result.status(PaymentSearchResult.StatusEnum.PENDING); - return; - } - - if (status.isSetProcessed()) { - OffsetDateTime createdAt = status.getProcessed().getAt() != null - ? TypeUtil.stringToInstant(status.getProcessed().getAt()).atOffset(ZoneOffset.UTC) - : null; - result.status(PaymentSearchResult.StatusEnum.PROCESSED) - .createdAt(createdAt); - return; - } - - if (status.isSetRefunded()) { - OffsetDateTime createdAt = status.getRefunded().getAt() != null - ? TypeUtil.stringToInstant(status.getRefunded().getAt()).atOffset(ZoneOffset.UTC) - : null; - result.status(PaymentSearchResult.StatusEnum.REFUNDED) - .createdAt(createdAt); - return; - } - - throw new IllegalArgumentException( - String.format("Payment status %s cannot be processed", payment.getStatus())); - } -} From 9e81d7e8f731666b22ca5e3d583464b40e8ab16e Mon Sep 17 00:00:00 2001 From: echerniak Date: Thu, 16 Sep 2021 22:37:48 +0300 Subject: [PATCH 16/37] Mapping fixes, todos --- .../anapi/v2/controller/SearchController.java | 5 ---- ...aramsToChargebackSearchQueryConverter.java | 1 + .../ParamsToInvoiceSearchQueryConverter.java | 1 + .../ParamsToPaymentSearchQueryConverter.java | 6 ++--- .../ParamsToPayoutSearchQueryConverter.java | 4 +-- .../ParamsToRefundSearchQueryConverter.java | 1 + .../StatInvoiceToInvoiceConverter.java | 27 ++++++++++++------- ...PaymentToPaymentSearchResultConverter.java | 2 +- src/main/resources/application.yml | 10 +++++++ 9 files changed, 35 insertions(+), 22 deletions(-) diff --git a/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java b/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java index b1e6a2c..de094df 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java +++ b/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java @@ -128,7 +128,6 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Valid List chargebackStages, @Valid List chargebackCategories, @Valid String continuationToken) { - //TODO: clarify mapping for paymentInstitutionRealm, xrequestID, xrequestDeadline, offset checkDeadline(xRequestDeadline, xRequestID); ChargebackSearchQuery query = chargebackSearchConverter.convert(partyID, fromTime, @@ -173,7 +172,6 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Min(1L) @Valid Long invoiceAmountTo, @Valid List excludedShops, @Valid String continuationToken) { - //TODO: clarify mapping for paymentInstitutionRealm, xrequestID, xrequestDeadline, excludedShops checkDeadline(xRequestDeadline, xRequestID); InvoiceSearchQuery query = invoiceSearchConverter.convert(partyID, fromTime, @@ -214,8 +212,6 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Valid String payoutToolType, @Valid List excludedShops, @Valid String continuationToken) { - //TODO: clarify mapping for paymentInstitutionRealm, xrequestID, xrequestDeadline, excludedShops, - //offset + setStatuses checkDeadline(xRequestDeadline, xRequestID); PayoutSearchQuery query = payoutSearchConverter.convert(partyID, fromTime, @@ -257,7 +253,6 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Valid String refundStatus, @Valid List excludedShops, @Valid String continuationToken) { - //TODO: clarify mapping for paymentInstitutionRealm, xrequestID, xrequestDeadline, excludedShops, offset checkDeadline(xRequestDeadline, xRequestID); RefundSearchQuery query = refundSearchConverter.convert(partyID, fromTime, diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToChargebackSearchQueryConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToChargebackSearchQueryConverter.java index 55b7b94..40f183b 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToChargebackSearchQueryConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToChargebackSearchQueryConverter.java @@ -33,6 +33,7 @@ public class ParamsToChargebackSearchQueryConverter { List chargebackStages, List chargebackCategories, String continuationToken) { + //TODO: Mapping for paymentInstitutionRealm, offset return new ChargebackSearchQuery() .setCommonSearchQueryParams( fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToInvoiceSearchQueryConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToInvoiceSearchQueryConverter.java index eea7230..134d218 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToInvoiceSearchQueryConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToInvoiceSearchQueryConverter.java @@ -29,6 +29,7 @@ public class ParamsToInvoiceSearchQueryConverter { Long invoiceAmountTo, List excludedShops, String continuationToken) { + //TODO: Mapping for paymentInstitutionRealm, excludedShops return new InvoiceSearchQuery() .setCommonSearchQueryParams( fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPaymentSearchQueryConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPaymentSearchQueryConverter.java index 79a4f9b..5fc1549 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPaymentSearchQueryConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPaymentSearchQueryConverter.java @@ -11,9 +11,7 @@ import org.springframework.stereotype.Component; import java.time.OffsetDateTime; import java.util.List; -import static com.rbkmoney.anapi.v2.util.ConverterUtil.merge; -import static com.rbkmoney.anapi.v2.util.ConverterUtil.fillCommonParams; -import static com.rbkmoney.anapi.v2.util.ConverterUtil.mapStatus; +import static com.rbkmoney.anapi.v2.util.ConverterUtil.*; @Component public class ParamsToPaymentSearchQueryConverter { @@ -46,7 +44,7 @@ public class ParamsToPaymentSearchQueryConverter { Long paymentAmountTo, List excludedShops, String continuationToken) { -//TODO: clarify mapping for paymentInstitutionRealm + //TODO: Mapping for paymentInstitutionRealm PaymentSearchQuery query = new PaymentSearchQuery() .setCommonSearchQueryParams( fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPayoutSearchQueryConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPayoutSearchQueryConverter.java index 900b5f9..7c31c4b 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPayoutSearchQueryConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPayoutSearchQueryConverter.java @@ -29,7 +29,7 @@ public class ParamsToPayoutSearchQueryConverter { String payoutToolType, List excludedShops, String continuationToken) { - + //TODO: Mapping for paymentInstitutionRealm, offset, excludedShops return new PayoutSearchQuery() .setCommonSearchQueryParams( fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), @@ -42,7 +42,7 @@ public class ParamsToPayoutSearchQueryConverter { var payoutToolInfo = new PayoutToolInfo(); switch (payoutToolType) { case "PayoutAccount" -> payoutToolInfo - .setRussianBankAccount(new RussianBankAccount()); //TODO: Russian or international? + .setRussianBankAccount(new RussianBankAccount());//TODO: Russian or International? case "Wallet" -> payoutToolInfo.setWalletInfo(new WalletInfo()); case "PaymentInstitutionAccount" -> payoutToolInfo .setPaymentInstitutionAccount(new PaymentInstitutionAccount()); diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToRefundSearchQueryConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToRefundSearchQueryConverter.java index 11daf5e..2b24d63 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToRefundSearchQueryConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToRefundSearchQueryConverter.java @@ -34,6 +34,7 @@ public class ParamsToRefundSearchQueryConverter { String refundStatus, List excludedShops, String continuationToken) { + //TODO: Mapping for paymentInstitutionRealm, offset, excludedShops return new RefundSearchQuery() .setCommonSearchQueryParams( fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatInvoiceToInvoiceConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatInvoiceToInvoiceConverter.java index 103b459..cac2f58 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatInvoiceToInvoiceConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatInvoiceToInvoiceConverter.java @@ -14,7 +14,7 @@ import java.util.stream.Collectors; public class StatInvoiceToInvoiceConverter { public Invoice convert(StatInvoice invoice) { - return new Invoice() + Invoice result = new Invoice() .amount(invoice.getAmount()) .createdAt(TypeUtil.stringToInstant(invoice.getCreatedAt()).atOffset(ZoneOffset.UTC)) .currency(invoice.getCurrencySymbolicCode()) @@ -24,32 +24,39 @@ public class StatInvoiceToInvoiceConverter { .cost(invoiceLine.getQuantity() * invoiceLine.getPrice().getAmount()) .price(invoiceLine.getPrice().getAmount()) .product(invoiceLine.getProduct()) - //.getTaxMode() + //.taxMode() //TODO: Where is the value? ).collect(Collectors.toList()) : null) .description(invoice.getDescription()) .dueDate(TypeUtil.stringToInstant(invoice.getDue()).atOffset(ZoneOffset.UTC)) .id(invoice.getId()) .product(invoice.getProduct()) - //.reason() - .shopID(invoice.getShopId()) - .status(mapToInvoiceStatus(invoice.getStatus())); + .shopID(invoice.getShopId()); + + fillStatusInfo(result, invoice.getStatus()); + return result; } - private Invoice.StatusEnum mapToInvoiceStatus(InvoiceStatus status) { + private void fillStatusInfo(Invoice invoice, InvoiceStatus status) { if (status.isSetFulfilled()) { - return Invoice.StatusEnum.FULFILLED; + invoice.setStatus(Invoice.StatusEnum.FULFILLED); + invoice.setReason(status.getFulfilled().getDetails()); + return; } if (status.isSetPaid()) { - return Invoice.StatusEnum.PAID; + invoice.setStatus(Invoice.StatusEnum.PAID); + return; } if (status.isSetUnpaid()) { - return Invoice.StatusEnum.UNPAID; + invoice.setStatus(Invoice.StatusEnum.UNPAID); + return; } if (status.isSetCancelled()) { - return Invoice.StatusEnum.CANCELLED; + invoice.setStatus(Invoice.StatusEnum.CANCELLED); + invoice.setReason(status.getCancelled().getDetails()); + return; } throw new IllegalArgumentException( diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverter.java index d917d77..dba566f 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverter.java @@ -81,7 +81,7 @@ public class StatPaymentToPaymentSearchResultConverter { } if (status.isSetChargedBack()) { - //TODO: Clearify + //TODO: OpenAPI missing status, should be added? } if (status.isSetFailed()) { diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index ed3e8da..0b75ee6 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -39,3 +39,13 @@ spring: info: version: '@project.version@' stage: dev + +keycloak: + realm: internal + auth-server-url: http://keycloak:8080/auth/ + resource: common-api + not-before: 0 + ssl-required: none + realm-public-key: + +auth.enabled: true From e9889116e862fd682d3fe6a8331303e7cdada092 Mon Sep 17 00:00:00 2001 From: echerniak Date: Thu, 16 Sep 2021 22:53:44 +0300 Subject: [PATCH 17/37] Dependecy updated --- pom.xml | 4 +-- .../anapi/v2/controller/SearchController.java | 26 ++----------------- 2 files changed, 3 insertions(+), 27 deletions(-) diff --git a/pom.xml b/pom.xml index 6186cb1..651b209 100644 --- a/pom.xml +++ b/pom.xml @@ -56,12 +56,10 @@ payout-manager-proto 1.17-8aa3eb8 - com.rbkmoney swag-anapi-v2 - - 1.0.0-SNAPSHOT + 1.29-27f5575-server com.rbkmoney diff --git a/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java b/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java index de094df..059f6be 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java +++ b/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java @@ -6,11 +6,10 @@ import com.rbkmoney.magista.*; import com.rbkmoney.openapi.anapi_v2.api.*; import com.rbkmoney.openapi.anapi_v2.model.*; import lombok.RequiredArgsConstructor; -import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.context.request.NativeWebRequest; import javax.validation.Valid; @@ -23,6 +22,7 @@ import static com.rbkmoney.anapi.v2.util.DeadlineUtil.checkDeadline; @Slf4j +@PreAuthorize("hasAuthority('invoices:read')") @Controller @RequiredArgsConstructor @SuppressWarnings("ParameterName") @@ -40,7 +40,6 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp return PaymentsApi.super.getRequest(); } - @SneakyThrows @Override public ResponseEntity searchPayments(String xRequestID, @NotNull @Size(min = 1, max = 40) @Valid String partyID, @@ -105,11 +104,6 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp return ResponseEntity.ok(response); } - @SneakyThrows - @GetMapping( - value = "/chargebacks", - produces = {"application/json; charset=utf-8"} - ) @Override public ResponseEntity searchChargebacks(String xRequestID, @NotNull @Size(min = 1, max = 40) @Valid String partyID, @@ -149,11 +143,6 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp return ResponseEntity.ok(response); } - @SneakyThrows - @GetMapping( - value = "/invoices", - produces = {"application/json; charset=utf-8"} - ) @Override public ResponseEntity searchInvoices(String xRequestID, @NotNull @Size(min = 1, max = 40) @Valid String partyID, @@ -192,11 +181,6 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp return ResponseEntity.ok(response); } - @SneakyThrows - @GetMapping( - value = "/payouts", - produces = {"application/json; charset=utf-8"} - ) @Override public ResponseEntity searchPayouts(String xRequestID, @NotNull @Size(min = 1, max = 40) @Valid String partyID, @@ -229,11 +213,6 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp return ResponseEntity.ok(response); } - @SneakyThrows - @GetMapping( - value = "/refunds", - produces = {"application/json; charset=utf-8"} - ) @Override public ResponseEntity searchRefunds(String xRequestID, @NotNull @Size(min = 1, max = 40) @Valid String partyID, @@ -270,7 +249,6 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp refundStatus, excludedShops, continuationToken); - InlineResponse20012 response = searchService.findRefunds(query); return ResponseEntity.ok(response); } From 5eaeae032957e09d7c0ef723aa705f454b310001 Mon Sep 17 00:00:00 2001 From: echerniak Date: Fri, 17 Sep 2021 16:10:46 +0300 Subject: [PATCH 18/37] Payments test added --- pom.xml | 17 +++ .../v2/controller/ErrorControllerAdvice.java | 10 ++ .../rbkmoney/anapi/v2/SearchPaymentsTest.java | 123 ++++++++++++++++++ .../v2/auth/JwtTokenTestConfiguration.java | 45 +++++++ .../auth/KeycloakOpenIdTestConfiguration.java | 18 +++ .../anapi/v2/auth/utils/JwtTokenBuilder.java | 88 +++++++++++++ .../v2/auth/utils/KeycloakOpenIdStub.java | 55 ++++++++ ...bstractKeycloakOpenIdAsWiremockConfig.java | 39 ++++++ .../anapi/v2/testutil/MagistaUtil.java | 70 ++++++++++ .../anapi/v2/testutil/OpenApiUtil.java | 57 ++++++++ .../anapi/v2/testutil/RandomUtil.java | 16 +++ 11 files changed, 538 insertions(+) create mode 100644 src/test/java/com/rbkmoney/anapi/v2/SearchPaymentsTest.java create mode 100644 src/test/java/com/rbkmoney/anapi/v2/auth/JwtTokenTestConfiguration.java create mode 100644 src/test/java/com/rbkmoney/anapi/v2/auth/KeycloakOpenIdTestConfiguration.java create mode 100644 src/test/java/com/rbkmoney/anapi/v2/auth/utils/JwtTokenBuilder.java create mode 100644 src/test/java/com/rbkmoney/anapi/v2/auth/utils/KeycloakOpenIdStub.java create mode 100644 src/test/java/com/rbkmoney/anapi/v2/config/AbstractKeycloakOpenIdAsWiremockConfig.java create mode 100644 src/test/java/com/rbkmoney/anapi/v2/testutil/MagistaUtil.java create mode 100644 src/test/java/com/rbkmoney/anapi/v2/testutil/OpenApiUtil.java create mode 100644 src/test/java/com/rbkmoney/anapi/v2/testutil/RandomUtil.java diff --git a/pom.xml b/pom.xml index 651b209..aa8f06f 100644 --- a/pom.xml +++ b/pom.xml @@ -212,6 +212,11 @@ 0.2.1 provided + + org.bouncycastle + bcprov-jdk15on + 1.69 + @@ -219,6 +224,18 @@ spring-boot-starter-test test + + io.jsonwebtoken + jjwt + 0.9.1 + test + + + org.springframework.cloud + spring-cloud-contract-wiremock + 3.0.3 + test + diff --git a/src/main/java/com/rbkmoney/anapi/v2/controller/ErrorControllerAdvice.java b/src/main/java/com/rbkmoney/anapi/v2/controller/ErrorControllerAdvice.java index 60e3fc1..9e47e7f 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/controller/ErrorControllerAdvice.java +++ b/src/main/java/com/rbkmoney/anapi/v2/controller/ErrorControllerAdvice.java @@ -6,6 +6,7 @@ import com.rbkmoney.openapi.anapi_v2.model.DefaultLogicError; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; +import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; @@ -44,6 +45,15 @@ public class ErrorControllerAdvice { .message(e.getMessage()); } + @ExceptionHandler({MissingServletRequestParameterException.class}) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public Object handleMissingServletRequestParameterException(MissingServletRequestParameterException e) { + log.warn("<- Res [400]: Missing ServletRequestParameter", e); + return new DefaultLogicError() + .code(DefaultLogicError.CodeEnum.INVALIDREQUEST) + .message(e.getMessage()); + } + @ExceptionHandler({DeadlineException.class}) @ResponseStatus(HttpStatus.BAD_REQUEST) public Object handleDeadlineException(DeadlineException e) { diff --git a/src/test/java/com/rbkmoney/anapi/v2/SearchPaymentsTest.java b/src/test/java/com/rbkmoney/anapi/v2/SearchPaymentsTest.java new file mode 100644 index 0000000..9c37ae6 --- /dev/null +++ b/src/test/java/com/rbkmoney/anapi/v2/SearchPaymentsTest.java @@ -0,0 +1,123 @@ +package com.rbkmoney.anapi.v2; + +import com.rbkmoney.anapi.v2.config.AbstractKeycloakOpenIdAsWiremockConfig; +import com.rbkmoney.anapi.v2.testutil.MagistaUtil; +import com.rbkmoney.anapi.v2.testutil.OpenApiUtil; +import com.rbkmoney.magista.MerchantStatisticsServiceSrv; +import com.rbkmoney.openapi.anapi_v2.model.DefaultLogicError; +import lombok.SneakyThrows; +import org.apache.thrift.TException; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockitoAnnotations; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.util.MultiValueMap; + +import java.time.Instant; +import java.time.temporal.ChronoUnit; + +import static java.util.UUID.randomUUID; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + + +class SearchPaymentsTest extends AbstractKeycloakOpenIdAsWiremockConfig { + + @MockBean + public MerchantStatisticsServiceSrv.Iface magistaClient; + + @Autowired + private MockMvc mvc; + + private AutoCloseable mocks; + + private Object[] preparedMocks; + + @BeforeEach + public void init() { + mocks = MockitoAnnotations.openMocks(this); + preparedMocks = new Object[] {magistaClient}; + } + + @AfterEach + public void clean() throws Exception { + verifyNoMoreInteractions(preparedMocks); + mocks.close(); + } + + @Test + @SneakyThrows + void searchPaymentsRequiredParamsRequestSuccess() { + when(magistaClient.searchPayments(any())).thenReturn(MagistaUtil.createSearchPaymentRequiredResponse()); + mvc.perform(get("/payments") + .header("Authorization", "Bearer " + generateInvoicesReadJwt()) + .header("X-Request-ID", randomUUID()) + .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) + .params(OpenApiUtil.getSearchPaymentRequiredParams()) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content("")) + .andDo(print()) + .andExpect(status().is2xxSuccessful()) + .andExpect(jsonPath("$").exists()); + verify(magistaClient, times(1)).searchPayments(any()); + } + + @Test + @SneakyThrows + void searchPaymentsAllParamsRequestSuccess() { + when(magistaClient.searchPayments(any())).thenReturn(MagistaUtil.createSearchPaymentAllResponse()); + mvc.perform(get("/payments") + .header("Authorization", "Bearer " + generateInvoicesReadJwt()) + .header("X-Request-ID", randomUUID()) + .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) + .params(OpenApiUtil.getSearchPaymentAllParams()) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content("")) + .andDo(print()) + .andExpect(status().is2xxSuccessful()) + .andExpect(jsonPath("$").exists()); + verify(magistaClient, times(1)).searchPayments(any()); + } + + @Test + @SneakyThrows + void searchPaymentsRequestInvalid() { + MultiValueMap params = OpenApiUtil.getSearchPaymentRequiredParams(); + params.remove("partyID"); + mvc.perform(get("/payments") + .header("Authorization", "Bearer " + generateInvoicesReadJwt()) + .header("X-Request-ID", randomUUID()) + .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) + .params(params) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content("")) + .andDo(print()) + .andExpect(status().is4xxClientError()) + .andExpect(jsonPath("$.code").value(DefaultLogicError.CodeEnum.INVALIDREQUEST.getValue())) + .andExpect(jsonPath("$.message").isNotEmpty()); + } + + @Test + @SneakyThrows + void searchPaymentsRequestMagistaUnavailable() { + when(magistaClient.searchPayments(any())).thenThrow(TException.class); + mvc.perform(get("/payments") + .header("Authorization", "Bearer " + generateInvoicesReadJwt()) + .header("X-Request-ID", randomUUID()) + .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) + .params(OpenApiUtil.getSearchPaymentRequiredParams()) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content("")) + .andDo(print()) + .andExpect(status().is5xxServerError()); + verify(magistaClient, times(1)).searchPayments(any()); + } +} diff --git a/src/test/java/com/rbkmoney/anapi/v2/auth/JwtTokenTestConfiguration.java b/src/test/java/com/rbkmoney/anapi/v2/auth/JwtTokenTestConfiguration.java new file mode 100644 index 0000000..dcf2aad --- /dev/null +++ b/src/test/java/com/rbkmoney/anapi/v2/auth/JwtTokenTestConfiguration.java @@ -0,0 +1,45 @@ +package com.rbkmoney.anapi.v2.auth; + +import com.rbkmoney.anapi.v2.auth.utils.JwtTokenBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; +import org.springframework.core.io.ClassPathResource; + +import java.io.IOException; +import java.security.*; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; +import java.util.Properties; + +@Configuration +public class JwtTokenTestConfiguration { + + @Bean + public static PropertySourcesPlaceholderConfigurer properties(KeyPair keyPair) + throws NoSuchAlgorithmException, InvalidKeySpecException, IOException { + KeyFactory fact = KeyFactory.getInstance("RSA"); + X509EncodedKeySpec spec = fact.getKeySpec(keyPair.getPublic(), X509EncodedKeySpec.class); + String publicKey = Base64.getEncoder().encodeToString(spec.getEncoded()); + PropertySourcesPlaceholderConfigurer pspc = new PropertySourcesPlaceholderConfigurer(); + Properties properties = new Properties(); + properties.load(new ClassPathResource("application.yml").getInputStream()); + properties.setProperty("keycloak.realm-public-key", publicKey); + pspc.setProperties(properties); + pspc.setLocalOverride(true); + return pspc; + } + + @Bean + public JwtTokenBuilder jwtTokenBuilder(KeyPair keyPair) { + return new JwtTokenBuilder(keyPair.getPrivate()); + } + + @Bean + public KeyPair keyPair() throws GeneralSecurityException { + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); + keyGen.initialize(2048); + return keyGen.generateKeyPair(); + } +} diff --git a/src/test/java/com/rbkmoney/anapi/v2/auth/KeycloakOpenIdTestConfiguration.java b/src/test/java/com/rbkmoney/anapi/v2/auth/KeycloakOpenIdTestConfiguration.java new file mode 100644 index 0000000..4424109 --- /dev/null +++ b/src/test/java/com/rbkmoney/anapi/v2/auth/KeycloakOpenIdTestConfiguration.java @@ -0,0 +1,18 @@ +package com.rbkmoney.anapi.v2.auth; + +import com.rbkmoney.anapi.v2.auth.utils.JwtTokenBuilder; +import com.rbkmoney.anapi.v2.auth.utils.KeycloakOpenIdStub; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class KeycloakOpenIdTestConfiguration { + + @Bean + public KeycloakOpenIdStub keycloakOpenIdStub(@Value("${keycloak.auth-server-url}") String keycloakAuthServerUrl, + @Value("${keycloak.realm}") String keycloakRealm, + JwtTokenBuilder jwtTokenBuilder) { + return new KeycloakOpenIdStub(keycloakAuthServerUrl, keycloakRealm, jwtTokenBuilder); + } +} diff --git a/src/test/java/com/rbkmoney/anapi/v2/auth/utils/JwtTokenBuilder.java b/src/test/java/com/rbkmoney/anapi/v2/auth/utils/JwtTokenBuilder.java new file mode 100644 index 0000000..7564d7e --- /dev/null +++ b/src/test/java/com/rbkmoney/anapi/v2/auth/utils/JwtTokenBuilder.java @@ -0,0 +1,88 @@ +package com.rbkmoney.anapi.v2.auth.utils; + +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.security.PrivateKey; +import java.time.Instant; +import java.util.UUID; + +/** + * @since 04.07.17 + **/ +public class JwtTokenBuilder { + + public static final String DEFAULT_USERNAME = "Darth Vader"; + + public static final String DEFAULT_EMAIL = "darkside-the-best@mail.com"; + + private final String userId; + + private final String username; + + private final String email; + + private final PrivateKey privateKey; + + public JwtTokenBuilder(PrivateKey privateKey) { + this(UUID.randomUUID().toString(), DEFAULT_USERNAME, DEFAULT_EMAIL, privateKey); + } + + public JwtTokenBuilder(String userId, String username, String email, PrivateKey privateKey) { + this.userId = userId; + this.username = username; + this.email = email; + this.privateKey = privateKey; + } + + public String getUserId() { + return userId; + } + + public String getUsername() { + return username; + } + + public String getEmail() { + return email; + } + + public String generateJwtWithRoles(String issuer, String... roles) { + long iat = Instant.now().getEpochSecond(); + long exp = iat + 60 * 10; + return generateJwtWithRoles(iat, exp, issuer, roles); + } + + public String generateJwtWithRoles(long iat, long exp, String issuer, String... roles) { + String payload; + try { + payload = new JSONObject() + .put("jti", UUID.randomUUID().toString()) + .put("exp", exp) + .put("nbf", "0") + .put("iat", iat) + .put("iss", issuer) + .put("aud", "private-api") + .put("sub", userId) + .put("typ", "Bearer") + .put("azp", "private-api") + .put("resource_access", new JSONObject() + .put("common-api", new JSONObject() + .put("roles", new JSONArray(roles)))) + .put("preferred_username", username) + .put("email", email).toString(); + } catch (JSONException e) { + throw new RuntimeException(e); + } + + String jwt = Jwts.builder() + .setPayload(payload) + .signWith(SignatureAlgorithm.RS256, privateKey) + .compact(); + return jwt; + } + +} diff --git a/src/test/java/com/rbkmoney/anapi/v2/auth/utils/KeycloakOpenIdStub.java b/src/test/java/com/rbkmoney/anapi/v2/auth/utils/KeycloakOpenIdStub.java new file mode 100644 index 0000000..6e0c652 --- /dev/null +++ b/src/test/java/com/rbkmoney/anapi/v2/auth/utils/KeycloakOpenIdStub.java @@ -0,0 +1,55 @@ +package com.rbkmoney.anapi.v2.auth.utils; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; + +public class KeycloakOpenIdStub { + + private final String keycloakRealm; + private final String issuer; + private final String openidConfig; + private final JwtTokenBuilder jwtTokenBuilder; + + public KeycloakOpenIdStub(String keycloakAuthServerUrl, String keycloakRealm, JwtTokenBuilder jwtTokenBuilder) { + this.keycloakRealm = keycloakRealm; + this.jwtTokenBuilder = jwtTokenBuilder; + this.issuer = keycloakAuthServerUrl + "/realms/" + keycloakRealm; + this.openidConfig = "{\n" + + " \"issuer\": \"" + issuer + "\",\n" + + " \"authorization_endpoint\": \"" + keycloakAuthServerUrl + "/realms/" + keycloakRealm + + "/protocol/openid-connect/auth\",\n" + + " \"token_endpoint\": \"" + keycloakAuthServerUrl + "/realms/" + keycloakRealm + + "/protocol/openid-connect/token\",\n" + + " \"token_introspection_endpoint\": \"" + keycloakAuthServerUrl + "/realms/" + keycloakRealm + + "/protocol/openid-connect/token/introspect\",\n" + + " \"userinfo_endpoint\": \"" + keycloakAuthServerUrl + "/realms/" + keycloakRealm + + "/protocol/openid-connect/userinfo\",\n" + + " \"end_session_endpoint\": \"" + keycloakAuthServerUrl + "/realms/" + keycloakRealm + + "/protocol/openid-connect/logout\",\n" + + " \"jwks_uri\": \"" + keycloakAuthServerUrl + "/realms/" + keycloakRealm + + "/protocol/openid-connect/certs\",\n" + + " \"check_session_iframe\": \"" + keycloakAuthServerUrl + "/realms/" + keycloakRealm + + "/protocol/openid-connect/login-status-iframe.html\",\n" + + " \"registration_endpoint\": \"" + keycloakAuthServerUrl + "/realms/" + keycloakRealm + + "/clients-registrations/openid-connect\",\n" + + " \"introspection_endpoint\": \"" + keycloakAuthServerUrl + "/realms/" + keycloakRealm + + "/protocol/openid-connect/token/introspect\"\n" + + "}"; + } + + public void givenStub() { + stubFor(get(urlEqualTo(String.format("/auth/realms/%s/.well-known/openid-configuration", keycloakRealm))) + .willReturn(aResponse() + .withHeader("Content-Type", "application/json") + .withBody(openidConfig) + ) + ); + } + + public String generateJwt(String... roles) { + return jwtTokenBuilder.generateJwtWithRoles(issuer, roles); + } + + public String generateJwt(long iat, long exp, String... roles) { + return jwtTokenBuilder.generateJwtWithRoles(iat, exp, issuer, roles); + } +} diff --git a/src/test/java/com/rbkmoney/anapi/v2/config/AbstractKeycloakOpenIdAsWiremockConfig.java b/src/test/java/com/rbkmoney/anapi/v2/config/AbstractKeycloakOpenIdAsWiremockConfig.java new file mode 100644 index 0000000..f5a0953 --- /dev/null +++ b/src/test/java/com/rbkmoney/anapi/v2/config/AbstractKeycloakOpenIdAsWiremockConfig.java @@ -0,0 +1,39 @@ +package com.rbkmoney.anapi.v2.config; + +import com.rbkmoney.anapi.v2.AnapiV2Application; +import com.rbkmoney.anapi.v2.auth.utils.KeycloakOpenIdStub; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +@SpringBootTest( + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + classes = {AnapiV2Application.class}, + properties = { + "wiremock.server.baseUrl=http://localhost:${wiremock.server.port}", + "keycloak.auth-server-url=${wiremock.server.baseUrl}/auth"}) +@AutoConfigureMockMvc +@AutoConfigureWireMock(port = 0) +@ExtendWith(SpringExtension.class) +public abstract class AbstractKeycloakOpenIdAsWiremockConfig { + + @Autowired + private KeycloakOpenIdStub keycloakOpenIdStub; + + @BeforeAll + public static void setUp(@Autowired KeycloakOpenIdStub keycloakOpenIdStub) throws Exception { + keycloakOpenIdStub.givenStub(); + } + + protected String generateJwt(long iat, long exp, String... roles) { + return keycloakOpenIdStub.generateJwt(iat, exp, roles); + } + + protected String generateInvoicesReadJwt() { + return keycloakOpenIdStub.generateJwt("invoices:read"); + } +} diff --git a/src/test/java/com/rbkmoney/anapi/v2/testutil/MagistaUtil.java b/src/test/java/com/rbkmoney/anapi/v2/testutil/MagistaUtil.java new file mode 100644 index 0000000..a057cc7 --- /dev/null +++ b/src/test/java/com/rbkmoney/anapi/v2/testutil/MagistaUtil.java @@ -0,0 +1,70 @@ +package com.rbkmoney.anapi.v2.testutil; + +import com.rbkmoney.damsel.domain.InvoiceCart; +import com.rbkmoney.damsel.domain.InvoiceLine; +import com.rbkmoney.damsel.geo_ip.LocationInfo; +import com.rbkmoney.geck.serializer.kit.mock.FieldHandler; +import com.rbkmoney.geck.serializer.kit.mock.MockMode; +import com.rbkmoney.geck.serializer.kit.mock.MockTBaseProcessor; +import com.rbkmoney.geck.serializer.kit.tbase.TBaseHandler; +import com.rbkmoney.magista.*; +import lombok.SneakyThrows; +import lombok.experimental.UtilityClass; +import org.apache.thrift.TBase; + +import java.time.Instant; +import java.util.List; +import java.util.Map; + +@UtilityClass +public class MagistaUtil { + + private static final MockTBaseProcessor mockRequiredTBaseProcessor; + private static final MockTBaseProcessor mockFullTBaseProcessor; + + static { + mockRequiredTBaseProcessor = new MockTBaseProcessor(MockMode.REQUIRED_ONLY, 15, 1); + Map.Entry timeFields = Map.entry( + structHandler -> structHandler.value(Instant.now().toString()), + new String[] {"created_at", "at", "due"} + ); + mockRequiredTBaseProcessor.addFieldHandler(timeFields.getKey(), timeFields.getValue()); + + mockFullTBaseProcessor = new MockTBaseProcessor(MockMode.ALL, 15, 1); + mockFullTBaseProcessor.addFieldHandler(timeFields.getKey(), timeFields.getValue()); + } + + public static StatPaymentResponse createSearchPaymentRequiredResponse() { + return fillRequiredTBaseObject(new StatPaymentResponse(), StatPaymentResponse.class); + } + + public static StatPaymentResponse createSearchPaymentAllResponse() { + var payment = fillAllTBaseObject(new StatPayment(), StatPayment.class); + var cart = fillAllTBaseObject(new InvoiceCart(), InvoiceCart.class); + var line = fillAllTBaseObject(new InvoiceLine(), InvoiceLine.class); + var instant = fillAllTBaseObject(new InvoicePaymentFlowInstant(), InvoicePaymentFlowInstant.class); + var locationInfo = fillAllTBaseObject(new LocationInfo(), LocationInfo.class); + var response = fillAllTBaseObject(new StatPaymentResponse(), StatPaymentResponse.class); + + response.setPayments( + List.of(payment + .setStatus(InvoicePaymentStatus + .pending(new InvoicePaymentPending())) + .setCart(cart.setLines(List.of(line))) + .setFlow(InvoicePaymentFlow + .instant(instant)) + .setLocationInfo(locationInfo))); + + return response; + } + + @SneakyThrows + public static T fillRequiredTBaseObject(T tbase, Class type) { + return mockRequiredTBaseProcessor.process(tbase, new TBaseHandler<>(type)); + } + + @SneakyThrows + public static T fillAllTBaseObject(T tbase, Class type) { + return mockFullTBaseProcessor.process(tbase, new TBaseHandler<>(type)); + } +} diff --git a/src/test/java/com/rbkmoney/anapi/v2/testutil/OpenApiUtil.java b/src/test/java/com/rbkmoney/anapi/v2/testutil/OpenApiUtil.java new file mode 100644 index 0000000..e5de1d7 --- /dev/null +++ b/src/test/java/com/rbkmoney/anapi/v2/testutil/OpenApiUtil.java @@ -0,0 +1,57 @@ +package com.rbkmoney.anapi.v2.testutil; + +import com.rbkmoney.damsel.domain.PaymentInstitutionRealm; +import com.rbkmoney.magista.TerminalPaymentProvider; +import com.rbkmoney.openapi.anapi_v2.model.BankCardPaymentSystem; +import com.rbkmoney.openapi.anapi_v2.model.BankCardTokenProvider; +import com.rbkmoney.openapi.anapi_v2.model.PaymentStatus; +import lombok.experimental.UtilityClass; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; + +import static com.rbkmoney.anapi.v2.testutil.RandomUtil.randomInteger; + +@UtilityClass +public class OpenApiUtil { + + public static MultiValueMap getSearchPaymentRequiredParams() { + MultiValueMap params = new LinkedMultiValueMap<>(); + params.add("partyID", randomInteger(1, 1000)); + params.add("fromTime", "2007-12-03T10:15:30+01:00"); + params.add("toTime", "2020-12-03T10:15:30+01:00"); + params.add("limit", randomInteger(1, 40)); + return params; + } + + public static MultiValueMap getSearchPaymentAllParams() { + MultiValueMap params = getSearchPaymentRequiredParams(); + params.add("shopID", randomInteger(1, 10)); + params.add("shopIDs", randomInteger(11, 20)); + params.add("shopIDs", randomInteger(21, 30)); + params.add("paymentInstitutionRealm", PaymentInstitutionRealm.live.name()); + params.add("invoiceIDs", randomInteger(1, 10)); + params.add("invoiceIDs", randomInteger(11, 20)); + params.add("paymentStatus", PaymentStatus.StatusEnum.PENDING.name()); + params.add("paymentFlow", "instant"); + params.add("paymentMethod", "paymentTerminal"); + params.add("paymentTerminalProvider", TerminalPaymentProvider.alipay.name()); + params.add("invoiceID", randomInteger(1, 1000)); + params.add("paymentID", randomInteger(1, 1000)); + params.add("externalID", randomInteger(1, 1000)); + params.add("payerEmail", "payer@mail.com"); + params.add("payerIP", "0.0.0.0"); + params.add("payerFingerprint", "iamveryunique"); + params.add("first6", randomInteger(100000, 999999)); + params.add("last4", randomInteger(1000, 9999)); + params.add("rrn", "123456789010"); + params.add("approvalCode", "QWERTY"); + params.add("bankCardTokenProvider", BankCardTokenProvider.APPLEPAY.name()); + params.add("bankCardPaymentSystem", BankCardPaymentSystem.MASTERCARD.name()); + params.add("paymentAmountFrom", randomInteger(1, 9999)); + params.add("paymentAmountTo", randomInteger(9999, 999999)); + params.add("excludedShops", randomInteger(1, 10)); + params.add("excludedShops", randomInteger(11, 20)); + params.add("continuationToken", "test"); + return params; + } +} diff --git a/src/test/java/com/rbkmoney/anapi/v2/testutil/RandomUtil.java b/src/test/java/com/rbkmoney/anapi/v2/testutil/RandomUtil.java new file mode 100644 index 0000000..a8f98a2 --- /dev/null +++ b/src/test/java/com/rbkmoney/anapi/v2/testutil/RandomUtil.java @@ -0,0 +1,16 @@ +package com.rbkmoney.anapi.v2.testutil; + +import lombok.experimental.UtilityClass; + +import java.util.Random; + +@UtilityClass +public class RandomUtil { + + private static final Random random = new Random(); + + public static String randomInteger(int from, int to) { + return String.valueOf(random.nextInt(to - from) + from); + } + +} From 7519936339c528b947959bad51cf3c0fc4b421d5 Mon Sep 17 00:00:00 2001 From: echerniak Date: Mon, 20 Sep 2021 11:54:42 +0300 Subject: [PATCH 19/37] WIP. Tests --- .../anapi/v2/SearchChargebacksTest.java | 123 ++++++++++++++++++ .../rbkmoney/anapi/v2/SearchPaymentsTest.java | 6 +- .../anapi/v2/testutil/MagistaUtil.java | 28 +++- .../anapi/v2/testutil/OpenApiUtil.java | 24 +++- 4 files changed, 169 insertions(+), 12 deletions(-) create mode 100644 src/test/java/com/rbkmoney/anapi/v2/SearchChargebacksTest.java diff --git a/src/test/java/com/rbkmoney/anapi/v2/SearchChargebacksTest.java b/src/test/java/com/rbkmoney/anapi/v2/SearchChargebacksTest.java new file mode 100644 index 0000000..1665ecf --- /dev/null +++ b/src/test/java/com/rbkmoney/anapi/v2/SearchChargebacksTest.java @@ -0,0 +1,123 @@ +package com.rbkmoney.anapi.v2; + +import com.rbkmoney.anapi.v2.config.AbstractKeycloakOpenIdAsWiremockConfig; +import com.rbkmoney.anapi.v2.testutil.MagistaUtil; +import com.rbkmoney.anapi.v2.testutil.OpenApiUtil; +import com.rbkmoney.magista.MerchantStatisticsServiceSrv; +import com.rbkmoney.openapi.anapi_v2.model.DefaultLogicError; +import lombok.SneakyThrows; +import org.apache.thrift.TException; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockitoAnnotations; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.util.MultiValueMap; + +import java.time.Instant; +import java.time.temporal.ChronoUnit; + +import static java.util.UUID.randomUUID; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + + +class SearchChargebacksTest extends AbstractKeycloakOpenIdAsWiremockConfig { + + @MockBean + public MerchantStatisticsServiceSrv.Iface magistaClient; + + @Autowired + private MockMvc mvc; + + private AutoCloseable mocks; + + private Object[] preparedMocks; + + @BeforeEach + public void init() { + mocks = MockitoAnnotations.openMocks(this); + preparedMocks = new Object[] {magistaClient}; + } + + @AfterEach + public void clean() throws Exception { + verifyNoMoreInteractions(preparedMocks); + mocks.close(); + } + + @Test + @SneakyThrows + void searchChargebacksRequiredParamsRequestSuccess() { + when(magistaClient.searchChargebacks(any())).thenReturn(MagistaUtil.createSearchChargebackRequiredResponse()); + mvc.perform(get("/chargebacks") + .header("Authorization", "Bearer " + generateInvoicesReadJwt()) + .header("X-Request-ID", randomUUID()) + .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) + .params(OpenApiUtil.getSearchRequiredParams()) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content("")) + .andDo(print()) + .andExpect(status().is2xxSuccessful()) + .andExpect(jsonPath("$").exists()); + verify(magistaClient, times(1)).searchChargebacks(any()); + } + + @Test + @SneakyThrows + void searchChargebacksAllParamsRequestSuccess() { + when(magistaClient.searchChargebacks(any())).thenReturn(MagistaUtil.createSearchChargebackAllResponse()); + mvc.perform(get("/chargebacks") + .header("Authorization", "Bearer " + generateInvoicesReadJwt()) + .header("X-Request-ID", randomUUID()) + .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) + .params(OpenApiUtil.getSearchChargebackAllParams()) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content("")) + .andDo(print()) + .andExpect(status().is2xxSuccessful()) + .andExpect(jsonPath("$").exists()); + verify(magistaClient, times(1)).searchChargebacks(any()); + } + + @Test + @SneakyThrows + void searchChargebacksRequestInvalid() { + MultiValueMap params = OpenApiUtil.getSearchRequiredParams(); + params.remove("partyID"); + mvc.perform(get("/chargebacks") + .header("Authorization", "Bearer " + generateInvoicesReadJwt()) + .header("X-Request-ID", randomUUID()) + .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) + .params(params) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content("")) + .andDo(print()) + .andExpect(status().is4xxClientError()) + .andExpect(jsonPath("$.code").value(DefaultLogicError.CodeEnum.INVALIDREQUEST.getValue())) + .andExpect(jsonPath("$.message").isNotEmpty()); + } + + @Test + @SneakyThrows + void searchChargebacksRequestMagistaUnavailable() { + when(magistaClient.searchPayments(any())).thenThrow(TException.class); + mvc.perform(get("/chargebacks") + .header("Authorization", "Bearer " + generateInvoicesReadJwt()) + .header("X-Request-ID", randomUUID()) + .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) + .params(OpenApiUtil.getSearchRequiredParams()) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content("")) + .andDo(print()) + .andExpect(status().is5xxServerError()); + verify(magistaClient, times(1)).searchChargebacks(any()); + } +} diff --git a/src/test/java/com/rbkmoney/anapi/v2/SearchPaymentsTest.java b/src/test/java/com/rbkmoney/anapi/v2/SearchPaymentsTest.java index 9c37ae6..df76a09 100644 --- a/src/test/java/com/rbkmoney/anapi/v2/SearchPaymentsTest.java +++ b/src/test/java/com/rbkmoney/anapi/v2/SearchPaymentsTest.java @@ -61,7 +61,7 @@ class SearchPaymentsTest extends AbstractKeycloakOpenIdAsWiremockConfig { .header("Authorization", "Bearer " + generateInvoicesReadJwt()) .header("X-Request-ID", randomUUID()) .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) - .params(OpenApiUtil.getSearchPaymentRequiredParams()) + .params(OpenApiUtil.getSearchRequiredParams()) .contentType(MediaType.APPLICATION_JSON_UTF8) .content("")) .andDo(print()) @@ -90,7 +90,7 @@ class SearchPaymentsTest extends AbstractKeycloakOpenIdAsWiremockConfig { @Test @SneakyThrows void searchPaymentsRequestInvalid() { - MultiValueMap params = OpenApiUtil.getSearchPaymentRequiredParams(); + MultiValueMap params = OpenApiUtil.getSearchRequiredParams(); params.remove("partyID"); mvc.perform(get("/payments") .header("Authorization", "Bearer " + generateInvoicesReadJwt()) @@ -113,7 +113,7 @@ class SearchPaymentsTest extends AbstractKeycloakOpenIdAsWiremockConfig { .header("Authorization", "Bearer " + generateInvoicesReadJwt()) .header("X-Request-ID", randomUUID()) .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) - .params(OpenApiUtil.getSearchPaymentRequiredParams()) + .params(OpenApiUtil.getSearchRequiredParams()) .contentType(MediaType.APPLICATION_JSON_UTF8) .content("")) .andDo(print()) diff --git a/src/test/java/com/rbkmoney/anapi/v2/testutil/MagistaUtil.java b/src/test/java/com/rbkmoney/anapi/v2/testutil/MagistaUtil.java index a057cc7..5990795 100644 --- a/src/test/java/com/rbkmoney/anapi/v2/testutil/MagistaUtil.java +++ b/src/test/java/com/rbkmoney/anapi/v2/testutil/MagistaUtil.java @@ -1,12 +1,15 @@ package com.rbkmoney.anapi.v2.testutil; -import com.rbkmoney.damsel.domain.InvoiceCart; -import com.rbkmoney.damsel.domain.InvoiceLine; +import com.rbkmoney.damsel.domain.*; import com.rbkmoney.damsel.geo_ip.LocationInfo; import com.rbkmoney.geck.serializer.kit.mock.FieldHandler; import com.rbkmoney.geck.serializer.kit.mock.MockMode; import com.rbkmoney.geck.serializer.kit.mock.MockTBaseProcessor; import com.rbkmoney.geck.serializer.kit.tbase.TBaseHandler; +import com.rbkmoney.magista.InvoicePaymentFlow; +import com.rbkmoney.magista.InvoicePaymentFlowInstant; +import com.rbkmoney.magista.InvoicePaymentPending; +import com.rbkmoney.magista.InvoicePaymentStatus; import com.rbkmoney.magista.*; import lombok.SneakyThrows; import lombok.experimental.UtilityClass; @@ -38,6 +41,10 @@ public class MagistaUtil { return fillRequiredTBaseObject(new StatPaymentResponse(), StatPaymentResponse.class); } + public static StatChargebackResponse createSearchChargebackRequiredResponse() { + return fillRequiredTBaseObject(new StatChargebackResponse(), StatChargebackResponse.class); + } + public static StatPaymentResponse createSearchPaymentAllResponse() { var payment = fillAllTBaseObject(new StatPayment(), StatPayment.class); var cart = fillAllTBaseObject(new InvoiceCart(), InvoiceCart.class); @@ -46,7 +53,7 @@ public class MagistaUtil { var locationInfo = fillAllTBaseObject(new LocationInfo(), LocationInfo.class); var response = fillAllTBaseObject(new StatPaymentResponse(), StatPaymentResponse.class); - response.setPayments( + return response.setPayments( List.of(payment .setStatus(InvoicePaymentStatus .pending(new InvoicePaymentPending())) @@ -54,8 +61,21 @@ public class MagistaUtil { .setFlow(InvoicePaymentFlow .instant(instant)) .setLocationInfo(locationInfo))); + } - return response; + public static StatChargebackResponse createSearchChargebackAllResponse() { + var chargeback = fillAllTBaseObject(new StatChargeback(), StatChargeback.class); + var stage = fillAllTBaseObject(new InvoicePaymentChargebackStage(), InvoicePaymentChargebackStage.class); + var reason = fillAllTBaseObject(new InvoicePaymentChargebackReason(), InvoicePaymentChargebackReason.class); + var status = fillAllTBaseObject(new InvoicePaymentChargebackStatus(), InvoicePaymentChargebackStatus.class); + var response = fillAllTBaseObject(new StatChargebackResponse(), StatChargebackResponse.class); + + return response.setChargebacks( + List.of(chargeback + .setStage(stage) + .setChargebackReason(reason) + .setChargebackStatus(status)) + ); } @SneakyThrows diff --git a/src/test/java/com/rbkmoney/anapi/v2/testutil/OpenApiUtil.java b/src/test/java/com/rbkmoney/anapi/v2/testutil/OpenApiUtil.java index e5de1d7..f634901 100644 --- a/src/test/java/com/rbkmoney/anapi/v2/testutil/OpenApiUtil.java +++ b/src/test/java/com/rbkmoney/anapi/v2/testutil/OpenApiUtil.java @@ -2,9 +2,7 @@ package com.rbkmoney.anapi.v2.testutil; import com.rbkmoney.damsel.domain.PaymentInstitutionRealm; import com.rbkmoney.magista.TerminalPaymentProvider; -import com.rbkmoney.openapi.anapi_v2.model.BankCardPaymentSystem; -import com.rbkmoney.openapi.anapi_v2.model.BankCardTokenProvider; -import com.rbkmoney.openapi.anapi_v2.model.PaymentStatus; +import com.rbkmoney.openapi.anapi_v2.model.*; import lombok.experimental.UtilityClass; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; @@ -14,7 +12,7 @@ import static com.rbkmoney.anapi.v2.testutil.RandomUtil.randomInteger; @UtilityClass public class OpenApiUtil { - public static MultiValueMap getSearchPaymentRequiredParams() { + public static MultiValueMap getSearchRequiredParams() { MultiValueMap params = new LinkedMultiValueMap<>(); params.add("partyID", randomInteger(1, 1000)); params.add("fromTime", "2007-12-03T10:15:30+01:00"); @@ -24,7 +22,7 @@ public class OpenApiUtil { } public static MultiValueMap getSearchPaymentAllParams() { - MultiValueMap params = getSearchPaymentRequiredParams(); + MultiValueMap params = getSearchRequiredParams(); params.add("shopID", randomInteger(1, 10)); params.add("shopIDs", randomInteger(11, 20)); params.add("shopIDs", randomInteger(21, 30)); @@ -54,4 +52,20 @@ public class OpenApiUtil { params.add("continuationToken", "test"); return params; } + + public static MultiValueMap getSearchChargebackAllParams() { + MultiValueMap params = getSearchRequiredParams(); + params.add("shopID", randomInteger(1, 10)); + params.add("shopIDs", randomInteger(11, 20)); + params.add("shopIDs", randomInteger(21, 30)); + params.add("paymentInstitutionRealm", PaymentInstitutionRealm.live.name()); + params.add("invoiceID", randomInteger(1, 1000)); + params.add("paymentID", randomInteger(1, 1000)); + params.add("chargebackID", randomInteger(1, 1000)); + params.add("chargebackStatuses", ChargebackStatus.PENDING.name()); + params.add("chargebackStages", ChargebackStage.CHARGEBACK.name()); + params.add("chargebackCategories", ChargebackCategory.AUTHORISATION.name()); + params.add("continuationToken", "test"); + return params; + } } From e9b89be09cafe4cc62283f2d32e9a345692658a0 Mon Sep 17 00:00:00 2001 From: echerniak Date: Mon, 20 Sep 2021 14:37:59 +0300 Subject: [PATCH 20/37] WIP. More tests + mapping fixes --- .../ParamsToInvoiceSearchQueryConverter.java | 24 +++- .../ParamsToPaymentSearchQueryConverter.java | 20 ++- .../rbkmoney/anapi/v2/util/ConverterUtil.java | 16 --- .../anapi/v2/AnapiV2ApplicationTest.java | 16 --- .../rbkmoney/anapi/v2/SearchInvoicesTest.java | 122 ++++++++++++++++++ .../rbkmoney/anapi/v2/SearchRefundsTest.java | 122 ++++++++++++++++++ .../anapi/v2/testutil/MagistaUtil.java | 42 ++++++ .../anapi/v2/testutil/OpenApiUtil.java | 41 ++++++ 8 files changed, 365 insertions(+), 38 deletions(-) delete mode 100644 src/test/java/com/rbkmoney/anapi/v2/AnapiV2ApplicationTest.java create mode 100644 src/test/java/com/rbkmoney/anapi/v2/SearchInvoicesTest.java create mode 100644 src/test/java/com/rbkmoney/anapi/v2/SearchRefundsTest.java diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToInvoiceSearchQueryConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToInvoiceSearchQueryConverter.java index 134d218..09840d4 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToInvoiceSearchQueryConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToInvoiceSearchQueryConverter.java @@ -1,5 +1,6 @@ package com.rbkmoney.anapi.v2.converter.search.request; +import com.rbkmoney.anapi.v2.exception.BadRequestException; import com.rbkmoney.magista.InvoiceSearchQuery; import com.rbkmoney.magista.PaymentParams; import org.springframework.stereotype.Component; @@ -7,9 +8,8 @@ import org.springframework.stereotype.Component; import java.time.OffsetDateTime; import java.util.List; -import static com.rbkmoney.anapi.v2.util.ConverterUtil.merge; import static com.rbkmoney.anapi.v2.util.ConverterUtil.fillCommonParams; -import static com.rbkmoney.anapi.v2.util.ConverterUtil.mapStatus; +import static com.rbkmoney.anapi.v2.util.ConverterUtil.merge; @Component public class ParamsToInvoiceSearchQueryConverter { @@ -36,11 +36,25 @@ public class ParamsToInvoiceSearchQueryConverter { continuationToken)) .setPaymentParams( new PaymentParams() - .setPaymentAmountFrom(invoiceAmountFrom) - .setPaymentAmountTo(invoiceAmountTo) - .setPaymentStatus(invoiceStatus != null ? mapStatus(invoiceStatus) : null) + .setPaymentAmountFrom(invoiceAmountFrom != null ? invoiceAmountFrom : 0L) + .setPaymentAmountTo(invoiceAmountTo != null ? invoiceAmountTo : 0L) ) + .setInvoiceStatus(invoiceStatus != null ? mapStatus(invoiceStatus) : null) .setInvoiceIds(merge(invoiceID, invoiceIDs)) .setExternalId(externalID); } + + private com.rbkmoney.damsel.domain.InvoiceStatus mapStatus(String statusParam) { + var status = Enum.valueOf(com.rbkmoney.openapi.anapi_v2.model.InvoiceStatus.StatusEnum.class, statusParam); + var invoiceStatus = new com.rbkmoney.damsel.domain.InvoiceStatus(); + switch (status) { + case CANCELLED -> invoiceStatus.setCancelled(new com.rbkmoney.damsel.domain.InvoiceCancelled()); + case FULFILLED -> invoiceStatus.setFulfilled(new com.rbkmoney.damsel.domain.InvoiceFulfilled()); + case PAID -> invoiceStatus.setPaid(new com.rbkmoney.damsel.domain.InvoicePaid()); + case UNPAID -> invoiceStatus.setUnpaid(new com.rbkmoney.damsel.domain.InvoiceUnpaid()); + default -> throw new BadRequestException( + String.format("Invoice status %s cannot be processed", status)); + } + return invoiceStatus; + } } diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPaymentSearchQueryConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPaymentSearchQueryConverter.java index 5fc1549..6ddb8b2 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPaymentSearchQueryConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPaymentSearchQueryConverter.java @@ -6,12 +6,14 @@ import com.rbkmoney.magista.PaymentParams; import com.rbkmoney.magista.PaymentSearchQuery; import com.rbkmoney.openapi.anapi_v2.model.BankCardPaymentSystem; import com.rbkmoney.openapi.anapi_v2.model.BankCardTokenProvider; +import com.rbkmoney.openapi.anapi_v2.model.PaymentStatus; import org.springframework.stereotype.Component; import java.time.OffsetDateTime; import java.util.List; -import static com.rbkmoney.anapi.v2.util.ConverterUtil.*; +import static com.rbkmoney.anapi.v2.util.ConverterUtil.fillCommonParams; +import static com.rbkmoney.anapi.v2.util.ConverterUtil.merge; @Component public class ParamsToPaymentSearchQueryConverter { @@ -111,4 +113,20 @@ public class ParamsToPaymentSearchQueryConverter { } return invoicePaymentFlow; } + + private InvoicePaymentStatus mapStatus(String paymentStatus) { + var status = Enum.valueOf(PaymentStatus.StatusEnum.class, paymentStatus); + var invoicePaymentStatus = new com.rbkmoney.damsel.domain.InvoicePaymentStatus(); + switch (status) { + case PENDING -> invoicePaymentStatus.setPending(new InvoicePaymentPending()); + case PROCESSED -> invoicePaymentStatus.setProcessed(new InvoicePaymentProcessed()); + case CAPTURED -> invoicePaymentStatus.setCaptured(new InvoicePaymentCaptured()); + case CANCELLED -> invoicePaymentStatus.setCancelled(new InvoicePaymentCancelled()); + case REFUNDED -> invoicePaymentStatus.setRefunded(new InvoicePaymentRefunded()); + case FAILED -> invoicePaymentStatus.setFailed(new InvoicePaymentFailed()); + default -> throw new BadRequestException( + String.format("Payment status %s cannot be processed", paymentStatus)); + } + return invoicePaymentStatus; + } } diff --git a/src/main/java/com/rbkmoney/anapi/v2/util/ConverterUtil.java b/src/main/java/com/rbkmoney/anapi/v2/util/ConverterUtil.java index 7cb67ab..dfe00c6 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/util/ConverterUtil.java +++ b/src/main/java/com/rbkmoney/anapi/v2/util/ConverterUtil.java @@ -37,20 +37,4 @@ public class ConverterUtil { .setPartyId(partyId) .setShopIds(shopIDs); } - - public static InvoicePaymentStatus mapStatus(String paymentStatus) { - var status = Enum.valueOf(PaymentStatus.StatusEnum.class, paymentStatus); - var invoicePaymentStatus = new com.rbkmoney.damsel.domain.InvoicePaymentStatus(); - switch (status) { - case PENDING -> invoicePaymentStatus.setPending(new InvoicePaymentPending()); - case PROCESSED -> invoicePaymentStatus.setProcessed(new InvoicePaymentProcessed()); - case CAPTURED -> invoicePaymentStatus.setCaptured(new InvoicePaymentCaptured()); - case CANCELLED -> invoicePaymentStatus.setCancelled(new InvoicePaymentCancelled()); - case REFUNDED -> invoicePaymentStatus.setRefunded(new InvoicePaymentRefunded()); - case FAILED -> invoicePaymentStatus.setFailed(new InvoicePaymentFailed()); - default -> throw new BadRequestException( - String.format("Payment status %s cannot be processed", paymentStatus)); - } - return invoicePaymentStatus; - } } diff --git a/src/test/java/com/rbkmoney/anapi/v2/AnapiV2ApplicationTest.java b/src/test/java/com/rbkmoney/anapi/v2/AnapiV2ApplicationTest.java deleted file mode 100644 index 959553b..0000000 --- a/src/test/java/com/rbkmoney/anapi/v2/AnapiV2ApplicationTest.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.rbkmoney.anapi.v2; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; - -@RunWith(SpringRunner.class) -@SpringBootTest(classes = AnapiV2Application.class) -public class AnapiV2ApplicationTest { - - @Test - public void contextLoads() { - - } -} diff --git a/src/test/java/com/rbkmoney/anapi/v2/SearchInvoicesTest.java b/src/test/java/com/rbkmoney/anapi/v2/SearchInvoicesTest.java new file mode 100644 index 0000000..25a143d --- /dev/null +++ b/src/test/java/com/rbkmoney/anapi/v2/SearchInvoicesTest.java @@ -0,0 +1,122 @@ +package com.rbkmoney.anapi.v2; + +import com.rbkmoney.anapi.v2.config.AbstractKeycloakOpenIdAsWiremockConfig; +import com.rbkmoney.anapi.v2.testutil.MagistaUtil; +import com.rbkmoney.anapi.v2.testutil.OpenApiUtil; +import com.rbkmoney.magista.MerchantStatisticsServiceSrv; +import com.rbkmoney.openapi.anapi_v2.model.DefaultLogicError; +import lombok.SneakyThrows; +import org.apache.thrift.TException; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockitoAnnotations; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.util.MultiValueMap; + +import java.time.Instant; +import java.time.temporal.ChronoUnit; + +import static java.util.UUID.randomUUID; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class SearchInvoicesTest extends AbstractKeycloakOpenIdAsWiremockConfig { + + @MockBean + public MerchantStatisticsServiceSrv.Iface magistaClient; + + @Autowired + private MockMvc mvc; + + private AutoCloseable mocks; + + private Object[] preparedMocks; + + @BeforeEach + public void init() { + mocks = MockitoAnnotations.openMocks(this); + preparedMocks = new Object[] {magistaClient}; + } + + @AfterEach + public void clean() throws Exception { + verifyNoMoreInteractions(preparedMocks); + mocks.close(); + } + + @Test + @SneakyThrows + void searchInvoicesRequiredParamsRequestSuccess() { + when(magistaClient.searchInvoices(any())).thenReturn(MagistaUtil.createSearchInvoiceRequiredResponse()); + mvc.perform(get("/invoices") + .header("Authorization", "Bearer " + generateInvoicesReadJwt()) + .header("X-Request-ID", randomUUID()) + .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) + .params(OpenApiUtil.getSearchRequiredParams()) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content("")) + .andDo(print()) + .andExpect(status().is2xxSuccessful()) + .andExpect(jsonPath("$").exists()); + verify(magistaClient, times(1)).searchInvoices(any()); + } + + @Test + @SneakyThrows + void searchInvoicesAllParamsRequestSuccess() { + when(magistaClient.searchInvoices(any())).thenReturn(MagistaUtil.createSearchInvoiceAllResponse()); + mvc.perform(get("/invoices") + .header("Authorization", "Bearer " + generateInvoicesReadJwt()) + .header("X-Request-ID", randomUUID()) + .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) + .params(OpenApiUtil.getSearchInvoiceAllParams()) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content("")) + .andDo(print()) + .andExpect(status().is2xxSuccessful()) + .andExpect(jsonPath("$").exists()); + verify(magistaClient, times(1)).searchInvoices(any()); + } + + @Test + @SneakyThrows + void searchInvoicesRequestInvalid() { + MultiValueMap params = OpenApiUtil.getSearchRequiredParams(); + params.remove("partyID"); + mvc.perform(get("/invoices") + .header("Authorization", "Bearer " + generateInvoicesReadJwt()) + .header("X-Request-ID", randomUUID()) + .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) + .params(params) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content("")) + .andDo(print()) + .andExpect(status().is4xxClientError()) + .andExpect(jsonPath("$.code").value(DefaultLogicError.CodeEnum.INVALIDREQUEST.getValue())) + .andExpect(jsonPath("$.message").isNotEmpty()); + } + + @Test + @SneakyThrows + void searchInvoicesRequestMagistaUnavailable() { + when(magistaClient.searchInvoices(any())).thenThrow(TException.class); + mvc.perform(get("/invoices") + .header("Authorization", "Bearer " + generateInvoicesReadJwt()) + .header("X-Request-ID", randomUUID()) + .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) + .params(OpenApiUtil.getSearchRequiredParams()) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content("")) + .andDo(print()) + .andExpect(status().is5xxServerError()); + verify(magistaClient, times(1)).searchInvoices(any()); + } +} diff --git a/src/test/java/com/rbkmoney/anapi/v2/SearchRefundsTest.java b/src/test/java/com/rbkmoney/anapi/v2/SearchRefundsTest.java new file mode 100644 index 0000000..6356935 --- /dev/null +++ b/src/test/java/com/rbkmoney/anapi/v2/SearchRefundsTest.java @@ -0,0 +1,122 @@ +package com.rbkmoney.anapi.v2; + +import com.rbkmoney.anapi.v2.config.AbstractKeycloakOpenIdAsWiremockConfig; +import com.rbkmoney.anapi.v2.testutil.MagistaUtil; +import com.rbkmoney.anapi.v2.testutil.OpenApiUtil; +import com.rbkmoney.magista.MerchantStatisticsServiceSrv; +import com.rbkmoney.openapi.anapi_v2.model.DefaultLogicError; +import lombok.SneakyThrows; +import org.apache.thrift.TException; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockitoAnnotations; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.util.MultiValueMap; + +import java.time.Instant; +import java.time.temporal.ChronoUnit; + +import static java.util.UUID.randomUUID; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class SearchRefundsTest extends AbstractKeycloakOpenIdAsWiremockConfig { + + @MockBean + public MerchantStatisticsServiceSrv.Iface magistaClient; + + @Autowired + private MockMvc mvc; + + private AutoCloseable mocks; + + private Object[] preparedMocks; + + @BeforeEach + public void init() { + mocks = MockitoAnnotations.openMocks(this); + preparedMocks = new Object[] {magistaClient}; + } + + @AfterEach + public void clean() throws Exception { + verifyNoMoreInteractions(preparedMocks); + mocks.close(); + } + + @Test + @SneakyThrows + void searchRefundsRequiredParamsRequestSuccess() { + when(magistaClient.searchRefunds(any())).thenReturn(MagistaUtil.createSearchRefundRequiredResponse()); + mvc.perform(get("/refunds") + .header("Authorization", "Bearer " + generateInvoicesReadJwt()) + .header("X-Request-ID", randomUUID()) + .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) + .params(OpenApiUtil.getSearchRequiredParams()) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content("")) + .andDo(print()) + .andExpect(status().is2xxSuccessful()) + .andExpect(jsonPath("$").exists()); + verify(magistaClient, times(1)).searchRefunds(any()); + } + + @Test + @SneakyThrows + void searchRefundsAllParamsRequestSuccess() { + when(magistaClient.searchRefunds(any())).thenReturn(MagistaUtil.createsearchRefundAllResponse()); + mvc.perform(get("/refunds") + .header("Authorization", "Bearer " + generateInvoicesReadJwt()) + .header("X-Request-ID", randomUUID()) + .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) + .params(OpenApiUtil.getSearchRefundAllParams()) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content("")) + .andDo(print()) + .andExpect(status().is2xxSuccessful()) + .andExpect(jsonPath("$").exists()); + verify(magistaClient, times(1)).searchRefunds(any()); + } + + @Test + @SneakyThrows + void searchRefundsRequestInvalid() { + MultiValueMap params = OpenApiUtil.getSearchRequiredParams(); + params.remove("partyID"); + mvc.perform(get("/refunds") + .header("Authorization", "Bearer " + generateInvoicesReadJwt()) + .header("X-Request-ID", randomUUID()) + .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) + .params(params) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content("")) + .andDo(print()) + .andExpect(status().is4xxClientError()) + .andExpect(jsonPath("$.code").value(DefaultLogicError.CodeEnum.INVALIDREQUEST.getValue())) + .andExpect(jsonPath("$.message").isNotEmpty()); + } + + @Test + @SneakyThrows + void searchRefundsRequestMagistaUnavailable() { + when(magistaClient.searchRefunds(any())).thenThrow(TException.class); + mvc.perform(get("/refunds") + .header("Authorization", "Bearer " + generateInvoicesReadJwt()) + .header("X-Request-ID", randomUUID()) + .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) + .params(OpenApiUtil.getSearchRequiredParams()) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content("")) + .andDo(print()) + .andExpect(status().is5xxServerError()); + verify(magistaClient, times(1)).searchRefunds(any()); + } +} diff --git a/src/test/java/com/rbkmoney/anapi/v2/testutil/MagistaUtil.java b/src/test/java/com/rbkmoney/anapi/v2/testutil/MagistaUtil.java index 5990795..396d624 100644 --- a/src/test/java/com/rbkmoney/anapi/v2/testutil/MagistaUtil.java +++ b/src/test/java/com/rbkmoney/anapi/v2/testutil/MagistaUtil.java @@ -9,7 +9,9 @@ import com.rbkmoney.geck.serializer.kit.tbase.TBaseHandler; import com.rbkmoney.magista.InvoicePaymentFlow; import com.rbkmoney.magista.InvoicePaymentFlowInstant; import com.rbkmoney.magista.InvoicePaymentPending; +import com.rbkmoney.magista.InvoicePaymentRefundStatus; import com.rbkmoney.magista.InvoicePaymentStatus; +import com.rbkmoney.magista.InvoiceStatus; import com.rbkmoney.magista.*; import lombok.SneakyThrows; import lombok.experimental.UtilityClass; @@ -78,6 +80,46 @@ public class MagistaUtil { ); } + public static StatRefundResponse createSearchRefundRequiredResponse() { + return fillRequiredTBaseObject(new StatRefundResponse(), StatRefundResponse.class); + } + + public static StatRefundResponse createsearchRefundAllResponse() { + var refund = fillAllTBaseObject(new StatRefund(), StatRefund.class); + var cart = fillAllTBaseObject(new InvoiceCart(), InvoiceCart.class); + var line = fillAllTBaseObject(new InvoiceLine(), InvoiceLine.class); + var cash = fillAllTBaseObject(new Cash(), Cash.class); + var status = fillAllTBaseObject(new InvoicePaymentRefundStatus(), InvoicePaymentRefundStatus.class); + var response = fillAllTBaseObject(new StatRefundResponse(), StatRefundResponse.class); + + return response.setRefunds( + List.of(refund + .setCart(cart + .setLines(List.of(line.setPrice(cash)))) + .setStatus(status)) + ); + } + + public static StatInvoiceResponse createSearchInvoiceRequiredResponse() { + return fillRequiredTBaseObject(new StatInvoiceResponse(), StatInvoiceResponse.class); + } + + public static StatInvoiceResponse createSearchInvoiceAllResponse() { + var invoice = fillAllTBaseObject(new StatInvoice(), StatInvoice.class); + var cart = fillAllTBaseObject(new InvoiceCart(), InvoiceCart.class); + var line = fillAllTBaseObject(new InvoiceLine(), InvoiceLine.class); + var cash = fillAllTBaseObject(new Cash(), Cash.class); + var status = fillAllTBaseObject(new InvoiceStatus(), InvoiceStatus.class); + var response = fillAllTBaseObject(new StatInvoiceResponse(), StatInvoiceResponse.class); + + return response.setInvoices( + List.of(invoice + .setCart(cart + .setLines(List.of(line.setPrice(cash)))) + .setStatus(status)) + ); + } + @SneakyThrows public static T fillRequiredTBaseObject(T tbase, Class type) { return mockRequiredTBaseProcessor.process(tbase, new TBaseHandler<>(type)); diff --git a/src/test/java/com/rbkmoney/anapi/v2/testutil/OpenApiUtil.java b/src/test/java/com/rbkmoney/anapi/v2/testutil/OpenApiUtil.java index f634901..0f8fff2 100644 --- a/src/test/java/com/rbkmoney/anapi/v2/testutil/OpenApiUtil.java +++ b/src/test/java/com/rbkmoney/anapi/v2/testutil/OpenApiUtil.java @@ -59,6 +59,7 @@ public class OpenApiUtil { params.add("shopIDs", randomInteger(11, 20)); params.add("shopIDs", randomInteger(21, 30)); params.add("paymentInstitutionRealm", PaymentInstitutionRealm.live.name()); + params.add("offset", randomInteger(1, 10)); params.add("invoiceID", randomInteger(1, 1000)); params.add("paymentID", randomInteger(1, 1000)); params.add("chargebackID", randomInteger(1, 1000)); @@ -68,4 +69,44 @@ public class OpenApiUtil { params.add("continuationToken", "test"); return params; } + + public static MultiValueMap getSearchRefundAllParams() { + MultiValueMap params = getSearchRequiredParams(); + params.add("shopID", randomInteger(1, 10)); + params.add("shopIDs", randomInteger(11, 20)); + params.add("shopIDs", randomInteger(21, 30)); + params.add("paymentInstitutionRealm", PaymentInstitutionRealm.live.name()); + params.add("offset", randomInteger(1, 10)); + params.add("invoiceIDs", randomInteger(1, 10)); + params.add("invoiceIDs", randomInteger(11, 20)); + params.add("invoiceID", randomInteger(1, 1000)); + params.add("paymentID", randomInteger(1, 1000)); + params.add("refundID", randomInteger(1, 1000)); + params.add("externalID", randomInteger(1, 1000)); + params.add("refundStatus", RefundStatus.StatusEnum.PENDING.name()); + params.add("excludedShops", randomInteger(1, 10)); + params.add("excludedShops", randomInteger(11, 20)); + params.add("continuationToken", "test"); + return params; + } + + public static MultiValueMap getSearchInvoiceAllParams() { + MultiValueMap params = getSearchRequiredParams(); + params.add("shopID", randomInteger(1, 10)); + params.add("shopIDs", randomInteger(11, 20)); + params.add("shopIDs", randomInteger(21, 30)); + params.add("paymentInstitutionRealm", PaymentInstitutionRealm.live.name()); + params.add("invoiceIDs", randomInteger(1, 10)); + params.add("invoiceIDs", randomInteger(11, 20)); + params.add("invoiceStatus", Invoice.StatusEnum.PAID.name()); + params.add("invoiceID", randomInteger(1, 1000)); + params.add("externalID", randomInteger(1, 1000)); + params.add("invoiceAmountFrom", randomInteger(1, 1000)); + params.add("invoiceAmountTo", randomInteger(1, 1000)); + params.add("excludedShops", randomInteger(1, 10)); + params.add("excludedShops", randomInteger(11, 20)); + params.add("continuationToken", "test"); + + return params; + } } From 8475513ced85acae149b8eccd2c9a66b6354266e Mon Sep 17 00:00:00 2001 From: echerniak Date: Mon, 20 Sep 2021 15:04:12 +0300 Subject: [PATCH 21/37] WIP. Payout test --- .../rbkmoney/anapi/v2/SearchPayoutsTest.java | 122 ++++++++++++++++++ .../anapi/v2/testutil/MagistaUtil.java | 18 +++ .../anapi/v2/testutil/OpenApiUtil.java | 15 +++ 3 files changed, 155 insertions(+) create mode 100644 src/test/java/com/rbkmoney/anapi/v2/SearchPayoutsTest.java diff --git a/src/test/java/com/rbkmoney/anapi/v2/SearchPayoutsTest.java b/src/test/java/com/rbkmoney/anapi/v2/SearchPayoutsTest.java new file mode 100644 index 0000000..12d0f07 --- /dev/null +++ b/src/test/java/com/rbkmoney/anapi/v2/SearchPayoutsTest.java @@ -0,0 +1,122 @@ +package com.rbkmoney.anapi.v2; + +import com.rbkmoney.anapi.v2.config.AbstractKeycloakOpenIdAsWiremockConfig; +import com.rbkmoney.anapi.v2.testutil.MagistaUtil; +import com.rbkmoney.anapi.v2.testutil.OpenApiUtil; +import com.rbkmoney.magista.MerchantStatisticsServiceSrv; +import com.rbkmoney.openapi.anapi_v2.model.DefaultLogicError; +import lombok.SneakyThrows; +import org.apache.thrift.TException; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockitoAnnotations; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.util.MultiValueMap; + +import java.time.Instant; +import java.time.temporal.ChronoUnit; + +import static java.util.UUID.randomUUID; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class SearchPayoutsTest extends AbstractKeycloakOpenIdAsWiremockConfig { + + @MockBean + public MerchantStatisticsServiceSrv.Iface magistaClient; + + @Autowired + private MockMvc mvc; + + private AutoCloseable mocks; + + private Object[] preparedMocks; + + @BeforeEach + public void init() { + mocks = MockitoAnnotations.openMocks(this); + preparedMocks = new Object[] {magistaClient}; + } + + @AfterEach + public void clean() throws Exception { + verifyNoMoreInteractions(preparedMocks); + mocks.close(); + } + + @Test + @SneakyThrows + void searchPayoutsRequiredParamsRequestSuccess() { + when(magistaClient.searchPayouts(any())).thenReturn(MagistaUtil.createSearchPayoutRequiredResponse()); + mvc.perform(get("/payouts") + .header("Authorization", "Bearer " + generateInvoicesReadJwt()) + .header("X-Request-ID", randomUUID()) + .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) + .params(OpenApiUtil.getSearchRequiredParams()) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content("")) + .andDo(print()) + .andExpect(status().is2xxSuccessful()) + .andExpect(jsonPath("$").exists()); + verify(magistaClient, times(1)).searchPayouts(any()); + } + + @Test + @SneakyThrows + void searchPayoutsAllParamsRequestSuccess() { + when(magistaClient.searchPayouts(any())).thenReturn(MagistaUtil.createSearchPayoutAllResponse()); + mvc.perform(get("/payouts") + .header("Authorization", "Bearer " + generateInvoicesReadJwt()) + .header("X-Request-ID", randomUUID()) + .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) + .params(OpenApiUtil.getSearchPayoutAllParams()) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content("")) + .andDo(print()) + .andExpect(status().is2xxSuccessful()) + .andExpect(jsonPath("$").exists()); + verify(magistaClient, times(1)).searchPayouts(any()); + } + + @Test + @SneakyThrows + void searchPayoutsRequestInvalid() { + MultiValueMap params = OpenApiUtil.getSearchRequiredParams(); + params.remove("partyID"); + mvc.perform(get("/payouts") + .header("Authorization", "Bearer " + generateInvoicesReadJwt()) + .header("X-Request-ID", randomUUID()) + .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) + .params(params) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content("")) + .andDo(print()) + .andExpect(status().is4xxClientError()) + .andExpect(jsonPath("$.code").value(DefaultLogicError.CodeEnum.INVALIDREQUEST.getValue())) + .andExpect(jsonPath("$.message").isNotEmpty()); + } + + @Test + @SneakyThrows + void searchPayoutsRequestMagistaUnavailable() { + when(magistaClient.searchPayouts(any())).thenThrow(TException.class); + mvc.perform(get("/payouts") + .header("Authorization", "Bearer " + generateInvoicesReadJwt()) + .header("X-Request-ID", randomUUID()) + .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) + .params(OpenApiUtil.getSearchRequiredParams()) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content("")) + .andDo(print()) + .andExpect(status().is5xxServerError()); + verify(magistaClient, times(1)).searchPayouts(any()); + } +} diff --git a/src/test/java/com/rbkmoney/anapi/v2/testutil/MagistaUtil.java b/src/test/java/com/rbkmoney/anapi/v2/testutil/MagistaUtil.java index 396d624..a956bd2 100644 --- a/src/test/java/com/rbkmoney/anapi/v2/testutil/MagistaUtil.java +++ b/src/test/java/com/rbkmoney/anapi/v2/testutil/MagistaUtil.java @@ -120,6 +120,24 @@ public class MagistaUtil { ); } + public static StatPayoutResponse createSearchPayoutRequiredResponse() { + return fillRequiredTBaseObject(new StatPayoutResponse(), StatPayoutResponse.class); + } + + public static StatPayoutResponse createSearchPayoutAllResponse() { + var payout = fillAllTBaseObject(new StatPayout(), StatPayout.class); + var toolInfo = fillAllTBaseObject(new PayoutToolInfo(), PayoutToolInfo.class); + var bank = fillAllTBaseObject(new RussianBankAccount(), RussianBankAccount.class); + var status = fillAllTBaseObject(new PayoutStatus(), PayoutStatus.class); + var response = fillAllTBaseObject(new StatPayoutResponse(), StatPayoutResponse.class); + toolInfo.setRussianBankAccount(bank); + return response.setPayouts( + List.of(payout + .setPayoutToolInfo(toolInfo) + .setStatus(status)) + ); + } + @SneakyThrows public static T fillRequiredTBaseObject(T tbase, Class type) { return mockRequiredTBaseProcessor.process(tbase, new TBaseHandler<>(type)); diff --git a/src/test/java/com/rbkmoney/anapi/v2/testutil/OpenApiUtil.java b/src/test/java/com/rbkmoney/anapi/v2/testutil/OpenApiUtil.java index 0f8fff2..0f62d70 100644 --- a/src/test/java/com/rbkmoney/anapi/v2/testutil/OpenApiUtil.java +++ b/src/test/java/com/rbkmoney/anapi/v2/testutil/OpenApiUtil.java @@ -109,4 +109,19 @@ public class OpenApiUtil { return params; } + + public static MultiValueMap getSearchPayoutAllParams() { + MultiValueMap params = getSearchRequiredParams(); + params.add("shopID", randomInteger(1, 10)); + params.add("shopIDs", randomInteger(11, 20)); + params.add("shopIDs", randomInteger(21, 30)); + params.add("paymentInstitutionRealm", PaymentInstitutionRealm.live.name()); + params.add("offset", randomInteger(1, 10)); + params.add("payoutID", randomInteger(1, 1000)); + params.add("payoutToolType", "PayoutAccount"); + params.add("excludedShops", randomInteger(1, 10)); + params.add("excludedShops", randomInteger(11, 20)); + params.add("continuationToken", "test"); + return params; + } } From ac6cfa05df809567b2900358585868ac143e9974 Mon Sep 17 00:00:00 2001 From: echerniak Date: Thu, 23 Sep 2021 12:34:02 +0300 Subject: [PATCH 22/37] Feedback edits + additional tests + mapping updates --- pom.xml | 6 +- .../ParamsToInvoiceSearchQueryConverter.java | 19 +- .../ParamsToPaymentSearchQueryConverter.java | 53 +++--- .../ParamsToRefundSearchQueryConverter.java | 19 +- .../StatInvoiceToInvoiceConverter.java | 24 ++- ...PaymentToPaymentSearchResultConverter.java | 80 ++------ ...atRefundToRefundSearchResultConverter.java | 22 ++- .../rbkmoney/anapi/v2/util/ConverterUtil.java | 15 +- .../v2/controller/ErrorControllerTest.java | 173 ++++++++++++++++++ .../anapi/v2/testutil/MagistaUtil.java | 12 +- .../anapi/v2/testutil/OpenApiUtil.java | 4 +- .../anapi/v2/util/ConverterUtilTest.java | 28 +++ .../anapi/v2/util/DeadlineUtilTest.java | 87 +++++++++ 13 files changed, 400 insertions(+), 142 deletions(-) create mode 100644 src/test/java/com/rbkmoney/anapi/v2/controller/ErrorControllerTest.java create mode 100644 src/test/java/com/rbkmoney/anapi/v2/util/ConverterUtilTest.java create mode 100644 src/test/java/com/rbkmoney/anapi/v2/util/DeadlineUtilTest.java diff --git a/pom.xml b/pom.xml index aa8f06f..6c33a2f 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ anapi-v2 - 0.0.1-SNAPSHOT + 1.0.0 jar anapi-v2 @@ -59,12 +59,12 @@ com.rbkmoney swag-anapi-v2 - 1.29-27f5575-server + 1.42-6975859-server com.rbkmoney magista-proto - 1.9-1fc32d2 + 1.12-7be076b com.rbkmoney.geck diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToInvoiceSearchQueryConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToInvoiceSearchQueryConverter.java index 09840d4..fc6c0bf 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToInvoiceSearchQueryConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToInvoiceSearchQueryConverter.java @@ -1,8 +1,7 @@ package com.rbkmoney.anapi.v2.converter.search.request; import com.rbkmoney.anapi.v2.exception.BadRequestException; -import com.rbkmoney.magista.InvoiceSearchQuery; -import com.rbkmoney.magista.PaymentParams; +import com.rbkmoney.magista.*; import org.springframework.stereotype.Component; import java.time.OffsetDateTime; @@ -44,17 +43,15 @@ public class ParamsToInvoiceSearchQueryConverter { .setExternalId(externalID); } - private com.rbkmoney.damsel.domain.InvoiceStatus mapStatus(String statusParam) { + private InvoiceStatus mapStatus(String statusParam) { var status = Enum.valueOf(com.rbkmoney.openapi.anapi_v2.model.InvoiceStatus.StatusEnum.class, statusParam); - var invoiceStatus = new com.rbkmoney.damsel.domain.InvoiceStatus(); - switch (status) { - case CANCELLED -> invoiceStatus.setCancelled(new com.rbkmoney.damsel.domain.InvoiceCancelled()); - case FULFILLED -> invoiceStatus.setFulfilled(new com.rbkmoney.damsel.domain.InvoiceFulfilled()); - case PAID -> invoiceStatus.setPaid(new com.rbkmoney.damsel.domain.InvoicePaid()); - case UNPAID -> invoiceStatus.setUnpaid(new com.rbkmoney.damsel.domain.InvoiceUnpaid()); + return switch (status) { + case CANCELLED -> InvoiceStatus.cancelled; + case FULFILLED -> InvoiceStatus.fulfilled; + case PAID -> InvoiceStatus.paid; + case UNPAID -> InvoiceStatus.unpaid; default -> throw new BadRequestException( String.format("Invoice status %s cannot be processed", status)); - } - return invoiceStatus; + }; } } diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPaymentSearchQueryConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPaymentSearchQueryConverter.java index 6ddb8b2..200d9b9 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPaymentSearchQueryConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPaymentSearchQueryConverter.java @@ -1,7 +1,10 @@ package com.rbkmoney.anapi.v2.converter.search.request; import com.rbkmoney.anapi.v2.exception.BadRequestException; -import com.rbkmoney.damsel.domain.*; +import com.rbkmoney.damsel.domain.LegacyBankCardPaymentSystem; +import com.rbkmoney.damsel.domain.LegacyBankCardTokenProvider; +import com.rbkmoney.damsel.domain.LegacyTerminalPaymentProvider; +import com.rbkmoney.magista.InvoicePaymentStatus; import com.rbkmoney.magista.PaymentParams; import com.rbkmoney.magista.PaymentSearchQuery; import com.rbkmoney.openapi.anapi_v2.model.BankCardPaymentSystem; @@ -14,6 +17,11 @@ import java.util.List; import static com.rbkmoney.anapi.v2.util.ConverterUtil.fillCommonParams; import static com.rbkmoney.anapi.v2.util.ConverterUtil.merge; +import static com.rbkmoney.magista.InvoicePaymentFlowType.hold; +import static com.rbkmoney.magista.InvoicePaymentFlowType.instant; +import static com.rbkmoney.magista.InvoicePaymentStatus.*; +import static com.rbkmoney.magista.PaymentTool.bank_card; +import static com.rbkmoney.magista.PaymentTool.payment_terminal; @Component public class ParamsToPaymentSearchQueryConverter { @@ -91,42 +99,35 @@ public class ParamsToPaymentSearchQueryConverter { return query; } - private PaymentTool mapToPaymentTool(String paymentMethod) { - var paymentTool = new PaymentTool(); - switch (paymentMethod) { - case "bankCard" -> paymentTool.setBankCard(new BankCard()); - case "paymentTerminal" -> paymentTool.setPaymentTerminal(new PaymentTerminal()); + private com.rbkmoney.magista.PaymentTool mapToPaymentTool(String paymentMethod) { + return switch (paymentMethod) { + case "bankCard" -> bank_card; + case "paymentTerminal" -> payment_terminal; default -> throw new BadRequestException( String.format("Payment method %s cannot be processed", paymentMethod)); - } - - return paymentTool; + }; } - private InvoicePaymentFlow mapToInvoicePaymentFlow(String paymentFlow) { - var invoicePaymentFlow = new InvoicePaymentFlow(); - switch (paymentFlow) { - case "instant" -> invoicePaymentFlow.setInstant(new InvoicePaymentFlowInstant()); - case "hold" -> invoicePaymentFlow.setHold(new InvoicePaymentFlowHold()); + private com.rbkmoney.magista.InvoicePaymentFlowType mapToInvoicePaymentFlow(String paymentFlow) { + return switch (paymentFlow) { + case "instant" -> instant; + case "hold" -> hold; default -> throw new BadRequestException( String.format("Payment flow %s cannot be processed", paymentFlow)); - } - return invoicePaymentFlow; + }; } private InvoicePaymentStatus mapStatus(String paymentStatus) { var status = Enum.valueOf(PaymentStatus.StatusEnum.class, paymentStatus); - var invoicePaymentStatus = new com.rbkmoney.damsel.domain.InvoicePaymentStatus(); - switch (status) { - case PENDING -> invoicePaymentStatus.setPending(new InvoicePaymentPending()); - case PROCESSED -> invoicePaymentStatus.setProcessed(new InvoicePaymentProcessed()); - case CAPTURED -> invoicePaymentStatus.setCaptured(new InvoicePaymentCaptured()); - case CANCELLED -> invoicePaymentStatus.setCancelled(new InvoicePaymentCancelled()); - case REFUNDED -> invoicePaymentStatus.setRefunded(new InvoicePaymentRefunded()); - case FAILED -> invoicePaymentStatus.setFailed(new InvoicePaymentFailed()); + return switch (status) { + case PENDING -> pending; + case PROCESSED -> processed; + case CAPTURED -> captured; + case CANCELLED -> cancelled; + case REFUNDED -> refunded; + case FAILED -> failed; default -> throw new BadRequestException( String.format("Payment status %s cannot be processed", paymentStatus)); - } - return invoicePaymentStatus; + }; } } diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToRefundSearchQueryConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToRefundSearchQueryConverter.java index 2b24d63..13cf8c1 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToRefundSearchQueryConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToRefundSearchQueryConverter.java @@ -1,10 +1,6 @@ package com.rbkmoney.anapi.v2.converter.search.request; import com.rbkmoney.anapi.v2.exception.BadRequestException; -import com.rbkmoney.damsel.domain.InvoicePaymentRefundFailed; -import com.rbkmoney.damsel.domain.InvoicePaymentRefundPending; -import com.rbkmoney.damsel.domain.InvoicePaymentRefundStatus; -import com.rbkmoney.damsel.domain.InvoicePaymentRefundSucceeded; import com.rbkmoney.magista.RefundSearchQuery; import com.rbkmoney.openapi.anapi_v2.model.RefundStatus; import org.springframework.stereotype.Component; @@ -14,6 +10,7 @@ import java.util.List; import static com.rbkmoney.anapi.v2.util.ConverterUtil.fillCommonParams; import static com.rbkmoney.anapi.v2.util.ConverterUtil.merge; +import static com.rbkmoney.magista.InvoicePaymentRefundStatus.*; @Component public class ParamsToRefundSearchQueryConverter { @@ -46,16 +43,14 @@ public class ParamsToRefundSearchQueryConverter { .setRefundId(refundID); } - private InvoicePaymentRefundStatus getRefundStatus(String refundStatus) { - var invoicePaymentRefundStatus = new InvoicePaymentRefundStatus(); - switch (Enum.valueOf(RefundStatus.StatusEnum.class, refundStatus)) { - case PENDING -> invoicePaymentRefundStatus.setPending(new InvoicePaymentRefundPending()); - case SUCCEEDED -> invoicePaymentRefundStatus.setSucceeded(new InvoicePaymentRefundSucceeded()); - case FAILED -> invoicePaymentRefundStatus.setFailed(new InvoicePaymentRefundFailed()); + private com.rbkmoney.magista.InvoicePaymentRefundStatus getRefundStatus(String refundStatus) { + return switch (Enum.valueOf(RefundStatus.StatusEnum.class, refundStatus)) { + case PENDING -> pending; + case SUCCEEDED -> succeeded; + case FAILED -> failed; default -> throw new BadRequestException( String.format("Refund status %s cannot be processed", refundStatus)); - } - return invoicePaymentRefundStatus; + }; } } diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatInvoiceToInvoiceConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatInvoiceToInvoiceConverter.java index cac2f58..3697b53 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatInvoiceToInvoiceConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatInvoiceToInvoiceConverter.java @@ -1,13 +1,17 @@ package com.rbkmoney.anapi.v2.converter.search.response; +import com.rbkmoney.damsel.domain.InvoiceStatus; +import com.rbkmoney.damsel.msgpack.Value; import com.rbkmoney.geck.common.util.TypeUtil; -import com.rbkmoney.magista.InvoiceStatus; import com.rbkmoney.magista.StatInvoice; import com.rbkmoney.openapi.anapi_v2.model.Invoice; import com.rbkmoney.openapi.anapi_v2.model.InvoiceLine; +import com.rbkmoney.openapi.anapi_v2.model.InvoiceLineTaxMode; +import com.rbkmoney.openapi.anapi_v2.model.InvoiceLineTaxVAT; import org.springframework.stereotype.Component; import java.time.ZoneOffset; +import java.util.Map; import java.util.stream.Collectors; @Component @@ -21,10 +25,10 @@ public class StatInvoiceToInvoiceConverter { .externalID(invoice.getExternalId()) .cart(invoice.getCart() != null ? invoice.getCart().getLines().stream().map(invoiceLine -> new InvoiceLine() - .cost(invoiceLine.getQuantity() * invoiceLine.getPrice().getAmount()) - .price(invoiceLine.getPrice().getAmount()) - .product(invoiceLine.getProduct()) - //.taxMode() //TODO: Where is the value? + .cost(invoiceLine.getQuantity() * invoiceLine.getPrice().getAmount()) + .price(invoiceLine.getPrice().getAmount()) + .product(invoiceLine.getProduct()) + .taxMode(getTaxMode(invoiceLine.getMetadata())) ).collect(Collectors.toList()) : null) .description(invoice.getDescription()) .dueDate(TypeUtil.stringToInstant(invoice.getDue()).atOffset(ZoneOffset.UTC)) @@ -63,4 +67,14 @@ public class StatInvoiceToInvoiceConverter { String.format("Invoice status %s cannot be processed", status)); } + private InvoiceLineTaxMode getTaxMode(Map metadata) { + Value taxMode = metadata.get("TaxMode"); + if (taxMode != null) { + return new InvoiceLineTaxVAT() + .rate(InvoiceLineTaxVAT.RateEnum.fromValue( + taxMode.getStr())); + } + return null; + } + } diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverter.java index dba566f..f287964 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverter.java @@ -1,18 +1,20 @@ package com.rbkmoney.anapi.v2.converter.search.response; import com.rbkmoney.geck.common.util.TypeUtil; +import com.rbkmoney.magista.InvoicePaymentStatus; import com.rbkmoney.magista.StatPayment; import com.rbkmoney.openapi.anapi_v2.model.*; import org.springframework.stereotype.Component; -import java.time.OffsetDateTime; import java.time.ZoneOffset; +import static com.rbkmoney.openapi.anapi_v2.model.PaymentSearchResult.StatusEnum.*; + @Component public class StatPaymentToPaymentSearchResultConverter { public PaymentSearchResult convert(StatPayment payment) { - PaymentSearchResult result = new PaymentSearchResult() + return new PaymentSearchResult() .amount(payment.getAmount()) .createdAt(TypeUtil.stringToInstant(payment.getCreatedAt()).atOffset(ZoneOffset.UTC)) .currency(payment.getCurrencySymbolicCode()) @@ -25,6 +27,9 @@ public class StatPaymentToPaymentSearchResultConverter { .cityGeoID(payment.getLocationInfo().getCityGeoId()) .countryGeoID(payment.getLocationInfo().getCountryGeoId()) : null) + .status(mapStatus(payment.getStatus())) + .statusChangedAt(payment.getStatusChangedAt() != null + ? TypeUtil.stringToInstant(payment.getStatusChangedAt()).atOffset(ZoneOffset.UTC) : null) .id(payment.getId()) .invoiceID(payment.getInvoiceId()) .makeRecurrent(payment.isMakeRecurrent()) @@ -36,8 +41,6 @@ public class StatPaymentToPaymentSearchResultConverter { .approvalCode(payment.getAdditionalTransactionInfo().getApprovalCode()) .rrn(payment.getAdditionalTransactionInfo().getRrn()) : null); - fillPaymentStatusInfo(payment, result); - return result; } private Payer getPayer(StatPayment payment) { @@ -60,63 +63,18 @@ public class StatPaymentToPaymentSearchResultConverter { String.format("Payer %s cannot be processed", statPayer)); } - private void fillPaymentStatusInfo(StatPayment payment, PaymentSearchResult result) { - var status = payment.getStatus(); - if (status.isSetCancelled()) { - OffsetDateTime createdAt = status.getCancelled().getAt() != null - ? TypeUtil.stringToInstant(status.getCancelled().getAt()).atOffset(ZoneOffset.UTC) - : null; - result.status(PaymentSearchResult.StatusEnum.CANCELLED) - .createdAt(createdAt); - return; - } + private PaymentSearchResult.StatusEnum mapStatus(InvoicePaymentStatus status) { + return switch (status) { + case pending -> PENDING; + case processed -> PROCESSED; + case captured -> CAPTURED; + case cancelled -> CANCELLED; + case refunded -> REFUNDED; + case failed -> FAILED; + //case charged_back ->; TODO: OpenAPI missing status, should be added? + default -> throw new IllegalArgumentException( + String.format("Payment status %s cannot be processed", status)); - if (status.isSetCaptured()) { - OffsetDateTime createdAt = status.getCaptured().getAt() != null - ? TypeUtil.stringToInstant(status.getCaptured().getAt()).atOffset(ZoneOffset.UTC) - : null; - result.status(PaymentSearchResult.StatusEnum.CAPTURED) - .createdAt(createdAt); - return; - } - - if (status.isSetChargedBack()) { - //TODO: OpenAPI missing status, should be added? - } - - if (status.isSetFailed()) { - OffsetDateTime createdAt = status.getFailed().getAt() != null - ? TypeUtil.stringToInstant(status.getFailed().getAt()).atOffset(ZoneOffset.UTC) - : null; - result.status(PaymentSearchResult.StatusEnum.FAILED) - .createdAt(createdAt); - return; - } - - if (status.isSetPending()) { - result.status(PaymentSearchResult.StatusEnum.PENDING); - return; - } - - if (status.isSetProcessed()) { - OffsetDateTime createdAt = status.getProcessed().getAt() != null - ? TypeUtil.stringToInstant(status.getProcessed().getAt()).atOffset(ZoneOffset.UTC) - : null; - result.status(PaymentSearchResult.StatusEnum.PROCESSED) - .createdAt(createdAt); - return; - } - - if (status.isSetRefunded()) { - OffsetDateTime createdAt = status.getRefunded().getAt() != null - ? TypeUtil.stringToInstant(status.getRefunded().getAt()).atOffset(ZoneOffset.UTC) - : null; - result.status(PaymentSearchResult.StatusEnum.REFUNDED) - .createdAt(createdAt); - return; - } - - throw new IllegalArgumentException( - String.format("Payment status %s cannot be processed", payment.getStatus())); + }; } } diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatRefundToRefundSearchResultConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatRefundToRefundSearchResultConverter.java index 2a4c504..49db712 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatRefundToRefundSearchResultConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatRefundToRefundSearchResultConverter.java @@ -1,7 +1,7 @@ package com.rbkmoney.anapi.v2.converter.search.response; +import com.rbkmoney.damsel.domain.InvoicePaymentRefundStatus; import com.rbkmoney.geck.common.util.TypeUtil; -import com.rbkmoney.magista.InvoicePaymentRefundStatus; import com.rbkmoney.magista.StatRefund; import com.rbkmoney.openapi.anapi_v2.model.RefundSearchResult; import com.rbkmoney.openapi.anapi_v2.model.RefundStatusError; @@ -19,19 +19,25 @@ public class StatRefundToRefundSearchResultConverter { .currency(refund.getCurrencySymbolicCode()) .id(refund.getId()) .shopID(refund.getShopId()) - .status(refund.getStatus() != null ? mapToRefundStatus(refund.getStatus()) : null) + .status(mapToRefundStatus(refund.getStatus())) .externalID(refund.getExternalId()) - .error(refund.getStatus().isSetFailed() - && refund.getStatus().getFailed().getFailure().isSetFailure() - ? new RefundStatusError() - .code(refund.getStatus().getFailed().getFailure().getFailure().getCode()) - .message(refund.getStatus().getFailed().getFailure().getFailure().getReason()) - : null) + .error(mapToRefundStatusError(refund.getStatus())) .invoiceID(refund.getInvoiceId()) .paymentID(refund.getPaymentId()) .reason(refund.getReason()); } + private RefundStatusError mapToRefundStatusError(InvoicePaymentRefundStatus status) { + if (status.isSetFailed() && status.getFailed().getFailure().isSetFailure()) { + var failure = status.getFailed().getFailure().getFailure(); + return new RefundStatusError() + .code(failure.getCode()) + .message(failure.getReason()); + } + + return null; + } + private RefundSearchResult.StatusEnum mapToRefundStatus(InvoicePaymentRefundStatus status) { if (status.isSetPending()) { return RefundSearchResult.StatusEnum.PENDING; diff --git a/src/main/java/com/rbkmoney/anapi/v2/util/ConverterUtil.java b/src/main/java/com/rbkmoney/anapi/v2/util/ConverterUtil.java index dfe00c6..65ff496 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/util/ConverterUtil.java +++ b/src/main/java/com/rbkmoney/anapi/v2/util/ConverterUtil.java @@ -1,10 +1,7 @@ package com.rbkmoney.anapi.v2.util; -import com.rbkmoney.anapi.v2.exception.BadRequestException; -import com.rbkmoney.damsel.domain.*; import com.rbkmoney.geck.common.util.TypeUtil; import com.rbkmoney.magista.CommonSearchQueryParams; -import com.rbkmoney.openapi.anapi_v2.model.PaymentStatus; import lombok.experimental.UtilityClass; import javax.annotation.Nullable; @@ -16,13 +13,15 @@ import java.util.List; public class ConverterUtil { public static List merge(@Nullable String id, @Nullable List ids) { + List identifiers = new ArrayList<>(); if (id != null) { - if (ids == null) { - ids = new ArrayList<>(); - } - ids.add(id); + identifiers.add(id); } - return ids; + + if (ids != null && !ids.isEmpty()) { + identifiers.addAll(ids); + } + return identifiers; } public static CommonSearchQueryParams fillCommonParams(OffsetDateTime fromTime, OffsetDateTime toTime, diff --git a/src/test/java/com/rbkmoney/anapi/v2/controller/ErrorControllerTest.java b/src/test/java/com/rbkmoney/anapi/v2/controller/ErrorControllerTest.java new file mode 100644 index 0000000..fbc30a1 --- /dev/null +++ b/src/test/java/com/rbkmoney/anapi/v2/controller/ErrorControllerTest.java @@ -0,0 +1,173 @@ +package com.rbkmoney.anapi.v2.controller; + +import com.rbkmoney.anapi.v2.config.AbstractKeycloakOpenIdAsWiremockConfig; +import com.rbkmoney.anapi.v2.converter.search.request.*; +import com.rbkmoney.anapi.v2.exception.BadRequestException; +import com.rbkmoney.anapi.v2.service.SearchService; +import com.rbkmoney.anapi.v2.testutil.OpenApiUtil; +import com.rbkmoney.openapi.anapi_v2.model.DefaultLogicError; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.util.MultiValueMap; + +import java.time.Instant; +import java.time.temporal.ChronoUnit; + +import static java.util.UUID.randomUUID; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class ErrorControllerTest extends AbstractKeycloakOpenIdAsWiremockConfig { + + @Autowired + private MockMvc mockMvc; + + @MockBean + private SearchService searchService; + @MockBean + private ParamsToPaymentSearchQueryConverter paymentSearchConverter; + @MockBean + private ParamsToChargebackSearchQueryConverter chargebackSearchConverter; + @MockBean + private ParamsToInvoiceSearchQueryConverter invoiceSearchConverter; + @MockBean + private ParamsToPayoutSearchQueryConverter payoutSearchConverter; + @MockBean + private ParamsToRefundSearchQueryConverter refundSearchConverter; + + @Test + void testConstraintViolationException() throws Exception { + MultiValueMap params = OpenApiUtil.getSearchRequiredParams(); + params.set("limit", "1001"); + + mockMvc.perform( + get("/payments") + .header("Authorization", "Bearer " + generateInvoicesReadJwt()) + .header("X-Request-ID", randomUUID()) + .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) + .params(params) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content("")) + .andDo(print()) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code").value(DefaultLogicError.CodeEnum.INVALIDREQUEST.getValue())) + .andExpect(jsonPath("$.message").isNotEmpty()); + } + + @Test + void testBadRequestException() throws Exception { + String message = "Error!"; + doThrow(new BadRequestException(message)).when(refundSearchConverter) + .convert(any(), any(), any(), any(), + any(), any(), any(), any(), + any(), any(), any(), any(), + any(), any(), any(), any()); + + MultiValueMap params = OpenApiUtil.getSearchRequiredParams(); + + mockMvc.perform( + get("/refunds") + .header("Authorization", "Bearer " + generateInvoicesReadJwt()) + .header("X-Request-ID", randomUUID()) + .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) + .params(params) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content("")) + .andDo(print()) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code").value(DefaultLogicError.CodeEnum.INVALIDREQUEST.getValue())) + .andExpect(jsonPath("$.message").value(message)); + + verify(refundSearchConverter, times(1)) + .convert(any(), any(), any(), any(), + any(), any(), any(), any(), + any(), any(), any(), any(), + any(), any(), any(), any()); + } + + @Test + void testMissingServletRequestParameterException() throws Exception { + + MultiValueMap params = OpenApiUtil.getSearchRequiredParams(); + params.remove("limit"); + + mockMvc.perform( + get("/refunds") + .header("Authorization", "Bearer " + generateInvoicesReadJwt()) + .header("X-Request-ID", randomUUID()) + .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) + .params(params) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content("")) + .andDo(print()) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code").value(DefaultLogicError.CodeEnum.INVALIDREQUEST.getValue())) + .andExpect(jsonPath("$.message").isNotEmpty()); + } + + @Test + void testDeadlineException() throws Exception { + mockMvc.perform( + get("/refunds") + .header("Authorization", "Bearer " + generateInvoicesReadJwt()) + .header("X-Request-ID", randomUUID()) + .header("X-Request-Deadline", "fail") + .params(OpenApiUtil.getSearchRequiredParams()) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content("")) + .andDo(print()) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code").value(DefaultLogicError.CodeEnum.INVALIDDEADLINE.getValue())) + .andExpect(jsonPath("$.message").isNotEmpty()); + } + + @Test + void testInternalException() throws Exception { + doThrow(new RuntimeException()).when(refundSearchConverter) + .convert(any(), any(), any(), any(), + any(), any(), any(), any(), + any(), any(), any(), any(), + any(), any(), any(), any()); + + MultiValueMap params = OpenApiUtil.getSearchRequiredParams(); + + mockMvc.perform( + get("/refunds") + .header("Authorization", "Bearer " + generateInvoicesReadJwt()) + .header("X-Request-ID", randomUUID()) + .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) + .params(params) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content("")) + .andDo(print()) + .andExpect(status().isInternalServerError()) + .andExpect(jsonPath("$").doesNotExist()); + verify(refundSearchConverter, times(1)) + .convert(any(), any(), any(), any(), + any(), any(), any(), any(), + any(), any(), any(), any(), + any(), any(), any(), any()); + } + + @Test + void testUnauthorizedException() throws Exception { + mockMvc.perform( + get("/refunds") + .header("X-Request-ID", randomUUID()) + .header("X-Request-Deadline", "fail") + .params(OpenApiUtil.getSearchRequiredParams()) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content("")) + .andDo(print()) + .andExpect(status().isUnauthorized()) + .andExpect(jsonPath("$").doesNotExist()); + } + +} diff --git a/src/test/java/com/rbkmoney/anapi/v2/testutil/MagistaUtil.java b/src/test/java/com/rbkmoney/anapi/v2/testutil/MagistaUtil.java index a956bd2..519a173 100644 --- a/src/test/java/com/rbkmoney/anapi/v2/testutil/MagistaUtil.java +++ b/src/test/java/com/rbkmoney/anapi/v2/testutil/MagistaUtil.java @@ -1,5 +1,7 @@ package com.rbkmoney.anapi.v2.testutil; +import com.rbkmoney.damsel.domain.InvoicePaymentRefundStatus; +import com.rbkmoney.damsel.domain.InvoiceStatus; import com.rbkmoney.damsel.domain.*; import com.rbkmoney.damsel.geo_ip.LocationInfo; import com.rbkmoney.geck.serializer.kit.mock.FieldHandler; @@ -8,10 +10,7 @@ import com.rbkmoney.geck.serializer.kit.mock.MockTBaseProcessor; import com.rbkmoney.geck.serializer.kit.tbase.TBaseHandler; import com.rbkmoney.magista.InvoicePaymentFlow; import com.rbkmoney.magista.InvoicePaymentFlowInstant; -import com.rbkmoney.magista.InvoicePaymentPending; -import com.rbkmoney.magista.InvoicePaymentRefundStatus; import com.rbkmoney.magista.InvoicePaymentStatus; -import com.rbkmoney.magista.InvoiceStatus; import com.rbkmoney.magista.*; import lombok.SneakyThrows; import lombok.experimental.UtilityClass; @@ -31,7 +30,7 @@ public class MagistaUtil { mockRequiredTBaseProcessor = new MockTBaseProcessor(MockMode.REQUIRED_ONLY, 15, 1); Map.Entry timeFields = Map.entry( structHandler -> structHandler.value(Instant.now().toString()), - new String[] {"created_at", "at", "due"} + new String[] {"created_at", "at", "due", "status_changed_at"} ); mockRequiredTBaseProcessor.addFieldHandler(timeFields.getKey(), timeFields.getValue()); @@ -58,7 +57,7 @@ public class MagistaUtil { return response.setPayments( List.of(payment .setStatus(InvoicePaymentStatus - .pending(new InvoicePaymentPending())) + .pending) .setCart(cart.setLines(List.of(line))) .setFlow(InvoicePaymentFlow .instant(instant)) @@ -109,7 +108,8 @@ public class MagistaUtil { var cart = fillAllTBaseObject(new InvoiceCart(), InvoiceCart.class); var line = fillAllTBaseObject(new InvoiceLine(), InvoiceLine.class); var cash = fillAllTBaseObject(new Cash(), Cash.class); - var status = fillAllTBaseObject(new InvoiceStatus(), InvoiceStatus.class); + var status = fillAllTBaseObject(new InvoiceStatus(), + InvoiceStatus.class); var response = fillAllTBaseObject(new StatInvoiceResponse(), StatInvoiceResponse.class); return response.setInvoices( diff --git a/src/test/java/com/rbkmoney/anapi/v2/testutil/OpenApiUtil.java b/src/test/java/com/rbkmoney/anapi/v2/testutil/OpenApiUtil.java index 0f62d70..780b06f 100644 --- a/src/test/java/com/rbkmoney/anapi/v2/testutil/OpenApiUtil.java +++ b/src/test/java/com/rbkmoney/anapi/v2/testutil/OpenApiUtil.java @@ -1,7 +1,7 @@ package com.rbkmoney.anapi.v2.testutil; import com.rbkmoney.damsel.domain.PaymentInstitutionRealm; -import com.rbkmoney.magista.TerminalPaymentProvider; +import com.rbkmoney.damsel.merch_stat.TerminalPaymentProvider; import com.rbkmoney.openapi.anapi_v2.model.*; import lombok.experimental.UtilityClass; import org.springframework.util.LinkedMultiValueMap; @@ -66,7 +66,7 @@ public class OpenApiUtil { params.add("chargebackStatuses", ChargebackStatus.PENDING.name()); params.add("chargebackStages", ChargebackStage.CHARGEBACK.name()); params.add("chargebackCategories", ChargebackCategory.AUTHORISATION.name()); - params.add("continuationToken", "test"); + //params.add("continuationToken", "test"); return params; } diff --git a/src/test/java/com/rbkmoney/anapi/v2/util/ConverterUtilTest.java b/src/test/java/com/rbkmoney/anapi/v2/util/ConverterUtilTest.java new file mode 100644 index 0000000..82b5109 --- /dev/null +++ b/src/test/java/com/rbkmoney/anapi/v2/util/ConverterUtilTest.java @@ -0,0 +1,28 @@ +package com.rbkmoney.anapi.v2.util; + +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +class ConverterUtilTest { + + @Test + void testMerge() { + String id = "1"; + List ids = List.of("2", "3"); + List result = ConverterUtil.merge(id, ids); + assertEquals(List.of("1", "2", "3"), result); + + result = ConverterUtil.merge(null, ids); + assertEquals(List.of("2", "3"), result); + + result = ConverterUtil.merge(id, null); + assertEquals(List.of("1"), result); + + result = ConverterUtil.merge(null, null); + assertNull(result); + } +} diff --git a/src/test/java/com/rbkmoney/anapi/v2/util/DeadlineUtilTest.java b/src/test/java/com/rbkmoney/anapi/v2/util/DeadlineUtilTest.java new file mode 100644 index 0000000..172b60b --- /dev/null +++ b/src/test/java/com/rbkmoney/anapi/v2/util/DeadlineUtilTest.java @@ -0,0 +1,87 @@ +package com.rbkmoney.anapi.v2.util; + +import com.rbkmoney.anapi.v2.exception.DeadlineException; +import org.junit.jupiter.api.Test; + +import java.time.Instant; +import java.time.temporal.ChronoUnit; + +import static com.rbkmoney.anapi.v2.util.DeadlineUtil.*; +import static org.junit.jupiter.api.Assertions.*; + +class DeadlineUtilTest { + + @Test + void checkDeadlineTest() { + checkDeadline(null, null); + + checkDeadline("12m", null); + checkDeadline("1.2m", null); + assertThrows(DeadlineException.class, () -> checkDeadline("-1.2m", null)); + + checkDeadline("12s", null); + checkDeadline("1.2s", null); + assertThrows(DeadlineException.class, () -> checkDeadline("-1.2s", null)); + + checkDeadline("12ms", null); + assertThrows(DeadlineException.class, () -> checkDeadline("1.2ms", null)); + assertThrows(DeadlineException.class, () -> checkDeadline("-12ms", null)); + + checkDeadline("12m12s12ms", null); + checkDeadline("1.2m1.2s12ms", null); + assertThrows(DeadlineException.class, () -> checkDeadline("1.2m1.2s1.2ms", null)); + assertThrows(DeadlineException.class, () -> checkDeadline("12s12s", null)); + assertThrows(DeadlineException.class, () -> checkDeadline("12m12m", null)); + assertThrows(DeadlineException.class, () -> checkDeadline("12ms12ms", null)); + assertThrows(DeadlineException.class, () -> checkDeadline("12s12ms12ms", null)); + assertThrows(DeadlineException.class, () -> checkDeadline("12s12s12ms", null)); + + checkDeadline(Instant.now().plus(1, ChronoUnit.DAYS).toString(), null); + assertThrows(DeadlineException.class, + () -> checkDeadline(Instant.now().minus(1, ChronoUnit.DAYS).toString(), null)); + + assertThrows(DeadlineException.class, () -> checkDeadline("undefined", null)); + } + + @Test + void extractMillisecondsTest() { + assertEquals(12, (long) extractMilliseconds("12ms", null)); + assertEquals(12, (long) extractMilliseconds("1.2m1.2s12ms", null)); + assertThrows(DeadlineException.class, () -> extractMilliseconds("1.2ms", null)); + assertThrows(DeadlineException.class, () -> extractMilliseconds("-12ms", null)); + assertThrows(DeadlineException.class, () -> extractMilliseconds("12ms12ms", null)); + } + + @Test + void extractSecondsTest() { + assertEquals(12000, (long) extractSeconds("12s", null)); + assertEquals(1200, (long) extractSeconds("1.2s", null)); + assertEquals(1200, (long) extractSeconds("1.2m1.2s12ms", null)); + assertThrows(DeadlineException.class, () -> extractSeconds("-1.2s", null)); + assertThrows(DeadlineException.class, () -> extractSeconds("12s12s", null)); + + } + + @Test + void extractMinutesTest() { + assertEquals(720000, (long) extractMinutes("12m", null)); + assertEquals(72000, (long) extractMinutes("1.2m", null)); + assertEquals(72000, (long) extractMinutes("1.2m1.2s12ms", null)); + assertThrows(DeadlineException.class, () -> extractMinutes("-1.2m", null)); + assertThrows(DeadlineException.class, () -> extractMinutes("12m12m", null)); + } + + @Test + void containsRelativeValuesTest() { + assertTrue(containsRelativeValues("12m", null)); + assertTrue(containsRelativeValues("12s", null)); + assertTrue(containsRelativeValues("12ms", null)); + assertTrue(containsRelativeValues("1.2m1.2s12ms", null)); + + assertThrows(DeadlineException.class, () -> containsRelativeValues("-1.2s", null)); + assertThrows(DeadlineException.class, () -> containsRelativeValues("12s12s", null)); + + assertFalse(containsRelativeValues(Instant.now().toString(), null)); + assertFalse(containsRelativeValues("asd", null)); + } +} From 16bc0d043862fd65559fd990c51b1f62bad49516 Mon Sep 17 00:00:00 2001 From: echerniak Date: Fri, 24 Sep 2021 12:55:50 +0300 Subject: [PATCH 23/37] Feedback edits --- .../ParamsToInvoiceSearchQueryConverter.java | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToInvoiceSearchQueryConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToInvoiceSearchQueryConverter.java index fc6c0bf..ad24ed4 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToInvoiceSearchQueryConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToInvoiceSearchQueryConverter.java @@ -1,7 +1,9 @@ package com.rbkmoney.anapi.v2.converter.search.request; import com.rbkmoney.anapi.v2.exception.BadRequestException; -import com.rbkmoney.magista.*; +import com.rbkmoney.magista.InvoiceSearchQuery; +import com.rbkmoney.magista.InvoiceStatus; +import com.rbkmoney.magista.PaymentParams; import org.springframework.stereotype.Component; import java.time.OffsetDateTime; @@ -34,15 +36,24 @@ public class ParamsToInvoiceSearchQueryConverter { fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), continuationToken)) .setPaymentParams( - new PaymentParams() - .setPaymentAmountFrom(invoiceAmountFrom != null ? invoiceAmountFrom : 0L) - .setPaymentAmountTo(invoiceAmountTo != null ? invoiceAmountTo : 0L) + getPaymentParams(invoiceAmountFrom, invoiceAmountTo) ) .setInvoiceStatus(invoiceStatus != null ? mapStatus(invoiceStatus) : null) .setInvoiceIds(merge(invoiceID, invoiceIDs)) .setExternalId(externalID); } + private PaymentParams getPaymentParams(Long invoiceAmountFrom, Long invoiceAmountTo) { + var params = new PaymentParams(); + if (invoiceAmountFrom != null) { + params.setPaymentAmountFrom(invoiceAmountFrom); + } + if (invoiceAmountTo != null) { + params.setPaymentAmountTo(invoiceAmountTo); + } + return params; + } + private InvoiceStatus mapStatus(String statusParam) { var status = Enum.valueOf(com.rbkmoney.openapi.anapi_v2.model.InvoiceStatus.StatusEnum.class, statusParam); return switch (status) { From d30730c506b9c0037f3a73aa03bc2cae00675701 Mon Sep 17 00:00:00 2001 From: echerniak Date: Thu, 30 Sep 2021 12:19:14 +0300 Subject: [PATCH 24/37] WIP. Bouncer integration --- pom.xml | 12 +++++- .../rbkmoney/anapi/v2/AnapiV2Application.java | 1 + .../anapi/v2/config/ApplicationConfig.java | 12 ++++++ .../anapi/v2/controller/SearchController.java | 7 ++++ .../anapi/v2/security/AccessService.java | 7 ++++ .../v2/security/AnapiBouncerContext.java | 19 +++++++++ .../v2/security/BouncerAccessService.java | 42 +++++++++++++++++++ .../v2/security/BouncerContextFactory.java | 34 +++++++++++++++ .../anapi/v2/service/KeycloakService.java | 17 ++++++++ .../anapi/v2/service/VortigonService.java | 31 ++++++++++++++ 10 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/rbkmoney/anapi/v2/security/AccessService.java create mode 100644 src/main/java/com/rbkmoney/anapi/v2/security/AnapiBouncerContext.java create mode 100644 src/main/java/com/rbkmoney/anapi/v2/security/BouncerAccessService.java create mode 100644 src/main/java/com/rbkmoney/anapi/v2/security/BouncerContextFactory.java create mode 100644 src/main/java/com/rbkmoney/anapi/v2/service/KeycloakService.java create mode 100644 src/main/java/com/rbkmoney/anapi/v2/service/VortigonService.java diff --git a/pom.xml b/pom.xml index 6c33a2f..96be4a4 100644 --- a/pom.xml +++ b/pom.xml @@ -43,6 +43,11 @@ com.rbkmoney custom-actuator-endpoints + + com.rbkmoney + bouncer-spring-boot-starter + 1.0.0 + com.rbkmoney.woody woody-thrift @@ -59,13 +64,18 @@ com.rbkmoney swag-anapi-v2 - 1.42-6975859-server + 1.43-33abd08-server com.rbkmoney magista-proto 1.12-7be076b + + com.rbkmoney + vortigon-proto + 1.13-0c92608 + com.rbkmoney.geck serializer diff --git a/src/main/java/com/rbkmoney/anapi/v2/AnapiV2Application.java b/src/main/java/com/rbkmoney/anapi/v2/AnapiV2Application.java index f7cb15a..04bfb05 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/AnapiV2Application.java +++ b/src/main/java/com/rbkmoney/anapi/v2/AnapiV2Application.java @@ -2,6 +2,7 @@ package com.rbkmoney.anapi.v2; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.web.servlet.ServletComponentScan; @ServletComponentScan diff --git a/src/main/java/com/rbkmoney/anapi/v2/config/ApplicationConfig.java b/src/main/java/com/rbkmoney/anapi/v2/config/ApplicationConfig.java index 070e5a5..41c64cf 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/config/ApplicationConfig.java +++ b/src/main/java/com/rbkmoney/anapi/v2/config/ApplicationConfig.java @@ -1,5 +1,6 @@ package com.rbkmoney.anapi.v2.config; +import com.rbkmoney.damsel.vortigon.VortigonServiceSrv; import com.rbkmoney.magista.MerchantStatisticsServiceSrv; import com.rbkmoney.woody.thrift.impl.http.THSpawnClientBuilder; import org.springframework.beans.factory.annotation.Value; @@ -21,4 +22,15 @@ public class ApplicationConfig { .withNetworkTimeout(networkTimeout) .withAddress(resource.getURI()).build(MerchantStatisticsServiceSrv.Iface.class); } + + @Bean + public VortigonServiceSrv.Iface vortigonClient( + @Value("${service.vortigon.url}") Resource resource, + @Value("${service.vortigon.networkTimeout}") int networkTimeout + ) throws IOException { + return new THSpawnClientBuilder() + .withNetworkTimeout(networkTimeout) + .withAddress(resource.getURI()).build(VortigonServiceSrv.Iface.class); + } + } diff --git a/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java b/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java index 059f6be..4dbcaee 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java +++ b/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java @@ -1,7 +1,9 @@ package com.rbkmoney.anapi.v2.controller; import com.rbkmoney.anapi.v2.converter.search.request.*; +import com.rbkmoney.anapi.v2.security.AccessService; import com.rbkmoney.anapi.v2.service.SearchService; +import com.rbkmoney.anapi.v2.service.VortigonService; import com.rbkmoney.magista.*; import com.rbkmoney.openapi.anapi_v2.api.*; import com.rbkmoney.openapi.anapi_v2.model.*; @@ -29,6 +31,8 @@ import static com.rbkmoney.anapi.v2.util.DeadlineUtil.checkDeadline; public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesApi, PayoutsApi, RefundsApi { private final SearchService searchService; + private final VortigonService vortigonService; + private final AccessService accessService; private final ParamsToPaymentSearchQueryConverter paymentSearchConverter; private final ParamsToChargebackSearchQueryConverter chargebackSearchConverter; private final ParamsToInvoiceSearchQueryConverter invoiceSearchConverter; @@ -72,6 +76,9 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Valid List excludedShops, @Valid String continuationToken) { checkDeadline(xRequestDeadline, xRequestID); + List shopIds = vortigonService.getShopIds(partyID, paymentInstitutionRealm); + //TODO: clarify, which shops should be checked for access. + accessService.checkAccess("searchPayments", partyID, shopID); PaymentSearchQuery query = paymentSearchConverter.convert(partyID, fromTime, toTime, diff --git a/src/main/java/com/rbkmoney/anapi/v2/security/AccessService.java b/src/main/java/com/rbkmoney/anapi/v2/security/AccessService.java new file mode 100644 index 0000000..85255fe --- /dev/null +++ b/src/main/java/com/rbkmoney/anapi/v2/security/AccessService.java @@ -0,0 +1,7 @@ +package com.rbkmoney.anapi.v2.security; + +public interface AccessService { + + void checkAccess(String operationId, String partyId, String shopId); + +} diff --git a/src/main/java/com/rbkmoney/anapi/v2/security/AnapiBouncerContext.java b/src/main/java/com/rbkmoney/anapi/v2/security/AnapiBouncerContext.java new file mode 100644 index 0000000..ce7c4aa --- /dev/null +++ b/src/main/java/com/rbkmoney/anapi/v2/security/AnapiBouncerContext.java @@ -0,0 +1,19 @@ +package com.rbkmoney.anapi.v2.security; + +import com.rbkmoney.bouncer.starter.api.BouncerContext; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Builder +@EqualsAndHashCode(callSuper = true) +@Data +public class AnapiBouncerContext extends BouncerContext { + + private final long tokenExpiration; + private final String tokenId; + private final String userId; + private final String operationId; + private final String partyId; + private final String shopId; +} diff --git a/src/main/java/com/rbkmoney/anapi/v2/security/BouncerAccessService.java b/src/main/java/com/rbkmoney/anapi/v2/security/BouncerAccessService.java new file mode 100644 index 0000000..2cfbfc6 --- /dev/null +++ b/src/main/java/com/rbkmoney/anapi/v2/security/BouncerAccessService.java @@ -0,0 +1,42 @@ +package com.rbkmoney.anapi.v2.security; + +import com.rbkmoney.anapi.v2.service.KeycloakService; +import com.rbkmoney.bouncer.starter.api.BouncerService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +@Slf4j +@RequiredArgsConstructor +@Service +public class BouncerAccessService implements AccessService { + + private final BouncerService bouncerService; + private final KeycloakService keycloakService; + + @Value("${bouncer.auth.enabled}") + private Boolean authEnabled; + + @Override + public void checkAccess(String operationId, String partyId, String shopId) { + log.info("Check the user's rights to perform the operation $operationId"); + var ctx = buildAnapiBouncerContext(operationId, partyId, shopId); + if (!bouncerService.havePrivileges(ctx)) { + if (authEnabled) { + //TODO: custom exception; + } else { + log.warn("No rights to perform $operationId"); + } + } + } + + private AnapiBouncerContext buildAnapiBouncerContext(String operationId, String partyId, String shopId) { + //TODO: Keycloak fields + return AnapiBouncerContext.builder() + .operationId(operationId) + .partyId(partyId) + .shopId(shopId) + .build(); + } +} diff --git a/src/main/java/com/rbkmoney/anapi/v2/security/BouncerContextFactory.java b/src/main/java/com/rbkmoney/anapi/v2/security/BouncerContextFactory.java new file mode 100644 index 0000000..eedbb94 --- /dev/null +++ b/src/main/java/com/rbkmoney/anapi/v2/security/BouncerContextFactory.java @@ -0,0 +1,34 @@ +package com.rbkmoney.anapi.v2.security; + +import com.rbkmoney.bouncer.context.v1.AnalyticsAPIOperation; +import com.rbkmoney.bouncer.context.v1.ContextAnalyticsAPI; +import com.rbkmoney.bouncer.context.v1.ContextFragment; +import com.rbkmoney.bouncer.context.v1.Entity; +import com.rbkmoney.bouncer.starter.AbstractBouncerContextFactory; +import com.rbkmoney.bouncer.starter.api.BouncerContext; +import com.rbkmoney.bouncer.starter.config.properties.BouncerProperties; +import org.springframework.stereotype.Component; + +@Component +public class BouncerContextFactory extends AbstractBouncerContextFactory { + + public BouncerContextFactory(BouncerProperties bouncerProperties, + com.rbkmoney.bouncer.starter.UserAuthContextProvider userAuthContextProvider) { + super(bouncerProperties, userAuthContextProvider); + } + + @Override + protected void customizeContext(@org.jetbrains.annotations.NotNull ContextFragment contextFragment, + @org.jetbrains.annotations.NotNull BouncerContext bouncerContext) { + contextFragment.setAnapi(buildAnapiContext((AnapiBouncerContext) bouncerContext)); + } + + private ContextAnalyticsAPI buildAnapiContext(AnapiBouncerContext ctx) { + return new ContextAnalyticsAPI() + .setOp(new AnalyticsAPIOperation() + .setShop(new Entity().setId(ctx.getShopId())) + .setParty(new Entity().setId(ctx.getPartyId())) + .setId(ctx.getOperationId())); + } + +} diff --git a/src/main/java/com/rbkmoney/anapi/v2/service/KeycloakService.java b/src/main/java/com/rbkmoney/anapi/v2/service/KeycloakService.java new file mode 100644 index 0000000..035a63e --- /dev/null +++ b/src/main/java/com/rbkmoney/anapi/v2/service/KeycloakService.java @@ -0,0 +1,17 @@ +package com.rbkmoney.anapi.v2.service; + +import org.keycloak.KeycloakPrincipal; +import org.keycloak.representations.AccessToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Service; + +@Service +public class KeycloakService { + + public AccessToken getAccessToken() { + var keycloakPrincipal = (KeycloakPrincipal) SecurityContextHolder.getContext() + .getAuthentication() + .getPrincipal(); + return keycloakPrincipal.getKeycloakSecurityContext().getToken(); + } +} diff --git a/src/main/java/com/rbkmoney/anapi/v2/service/VortigonService.java b/src/main/java/com/rbkmoney/anapi/v2/service/VortigonService.java new file mode 100644 index 0000000..4902a8b --- /dev/null +++ b/src/main/java/com/rbkmoney/anapi/v2/service/VortigonService.java @@ -0,0 +1,31 @@ +package com.rbkmoney.anapi.v2.service; + +import com.rbkmoney.anapi.v2.exception.BadRequestException; +import com.rbkmoney.damsel.vortigon.PaymentInstitutionRealm; +import com.rbkmoney.damsel.vortigon.VortigonServiceSrv; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class VortigonService { + + private final VortigonServiceSrv.Iface vortigonClient; + + @SneakyThrows + public List getShopIds(String partyId, String realm) { + return vortigonClient.getShopsIds(partyId, mapToRealm(realm)); + } + + private PaymentInstitutionRealm mapToRealm(String realm) { + return switch (realm) { + case "live" -> PaymentInstitutionRealm.live; + case "test" -> PaymentInstitutionRealm.test; + default -> throw new BadRequestException( + String.format("Realm %s cannot be processed", realm)); + }; + } +} From 36b68761bae7cfb4bbf201eab9cdcb5557d4ea64 Mon Sep 17 00:00:00 2001 From: echerniak Date: Thu, 30 Sep 2021 16:05:22 +0300 Subject: [PATCH 25/37] WIP. Refactor --- .../v2/exception/AuthorizationException.java | 8 ++++++++ .../anapi/v2/security/BouncerAccessService.java | 15 ++++++++++----- 2 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 src/main/java/com/rbkmoney/anapi/v2/exception/AuthorizationException.java diff --git a/src/main/java/com/rbkmoney/anapi/v2/exception/AuthorizationException.java b/src/main/java/com/rbkmoney/anapi/v2/exception/AuthorizationException.java new file mode 100644 index 0000000..2b34037 --- /dev/null +++ b/src/main/java/com/rbkmoney/anapi/v2/exception/AuthorizationException.java @@ -0,0 +1,8 @@ +package com.rbkmoney.anapi.v2.exception; + +public class AuthorizationException extends RuntimeException { + + public AuthorizationException(String s) { + super(s); + } +} diff --git a/src/main/java/com/rbkmoney/anapi/v2/security/BouncerAccessService.java b/src/main/java/com/rbkmoney/anapi/v2/security/BouncerAccessService.java index 2cfbfc6..e3f4a9b 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/security/BouncerAccessService.java +++ b/src/main/java/com/rbkmoney/anapi/v2/security/BouncerAccessService.java @@ -1,9 +1,11 @@ package com.rbkmoney.anapi.v2.security; +import com.rbkmoney.anapi.v2.exception.AuthorizationException; import com.rbkmoney.anapi.v2.service.KeycloakService; import com.rbkmoney.bouncer.starter.api.BouncerService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.keycloak.representations.AccessToken; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; @@ -16,27 +18,30 @@ public class BouncerAccessService implements AccessService { private final KeycloakService keycloakService; @Value("${bouncer.auth.enabled}") - private Boolean authEnabled; + private boolean authEnabled; @Override public void checkAccess(String operationId, String partyId, String shopId) { - log.info("Check the user's rights to perform the operation $operationId"); + log.info("Check the user's rights to perform the operation {}", operationId); var ctx = buildAnapiBouncerContext(operationId, partyId, shopId); if (!bouncerService.havePrivileges(ctx)) { if (authEnabled) { - //TODO: custom exception; + throw new AuthorizationException(String.format("No rights to perform %s", operationId)); } else { - log.warn("No rights to perform $operationId"); + log.warn("No rights to perform {}", operationId); } } } private AnapiBouncerContext buildAnapiBouncerContext(String operationId, String partyId, String shopId) { - //TODO: Keycloak fields + AccessToken token = keycloakService.getAccessToken(); return AnapiBouncerContext.builder() .operationId(operationId) .partyId(partyId) .shopId(shopId) + .tokenExpiration(token.getExp()) + .tokenId(token.getId()) + .userId(token.getSubject()) .build(); } } From 79d0e0eeba2e219f27eacc8016118a7e671238c1 Mon Sep 17 00:00:00 2001 From: echerniak Date: Fri, 1 Oct 2021 15:42:47 +0300 Subject: [PATCH 26/37] WIP. Starter removed (again) --- pom.xml | 15 ++-- .../anapi/v2/config/ApplicationConfig.java | 34 ++++++++ .../config/properties/BouncerProperties.java | 26 ++++++ .../anapi/v2/controller/SearchController.java | 16 +++- .../v2/exception/AuthorizationException.java | 4 + .../anapi/v2/exception/BouncerException.java | 11 +++ .../anapi/v2/security/AccessService.java | 7 -- .../v2/security/AnapiBouncerContext.java | 9 +- .../v2/security/BouncerAccessService.java | 15 ++-- .../v2/security/BouncerContextFactory.java | 83 +++++++++++++++---- .../anapi/v2/service/BouncerService.java | 39 +++++++++ .../anapi/v2/service/OrgMgmtService.java | 22 +++++ 12 files changed, 235 insertions(+), 46 deletions(-) create mode 100644 src/main/java/com/rbkmoney/anapi/v2/config/properties/BouncerProperties.java create mode 100644 src/main/java/com/rbkmoney/anapi/v2/exception/BouncerException.java delete mode 100644 src/main/java/com/rbkmoney/anapi/v2/security/AccessService.java create mode 100644 src/main/java/com/rbkmoney/anapi/v2/service/BouncerService.java create mode 100644 src/main/java/com/rbkmoney/anapi/v2/service/OrgMgmtService.java diff --git a/pom.xml b/pom.xml index 96be4a4..39265dd 100644 --- a/pom.xml +++ b/pom.xml @@ -43,11 +43,6 @@ com.rbkmoney custom-actuator-endpoints - - com.rbkmoney - bouncer-spring-boot-starter - 1.0.0 - com.rbkmoney.woody woody-thrift @@ -56,6 +51,16 @@ com.rbkmoney shared-resources + + com.rbkmoney + bouncer-proto + 1.27-cc50cc4 + + + com.rbkmoney + org-management-proto + 1.3-39d8513 + com.rbkmoney payout-manager-proto diff --git a/src/main/java/com/rbkmoney/anapi/v2/config/ApplicationConfig.java b/src/main/java/com/rbkmoney/anapi/v2/config/ApplicationConfig.java index 41c64cf..d75b87a 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/config/ApplicationConfig.java +++ b/src/main/java/com/rbkmoney/anapi/v2/config/ApplicationConfig.java @@ -1,7 +1,13 @@ package com.rbkmoney.anapi.v2.config; +import com.rbkmoney.bouncer.decisions.ArbiterSrv; import com.rbkmoney.damsel.vortigon.VortigonServiceSrv; import com.rbkmoney.magista.MerchantStatisticsServiceSrv; +import com.rbkmoney.orgmanagement.AuthContextProviderSrv; +import com.rbkmoney.woody.api.trace.context.metadata.user.UserIdentityEmailExtensionKit; +import com.rbkmoney.woody.api.trace.context.metadata.user.UserIdentityIdExtensionKit; +import com.rbkmoney.woody.api.trace.context.metadata.user.UserIdentityRealmExtensionKit; +import com.rbkmoney.woody.api.trace.context.metadata.user.UserIdentityUsernameExtensionKit; import com.rbkmoney.woody.thrift.impl.http.THSpawnClientBuilder; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; @@ -9,6 +15,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.core.io.Resource; import java.io.IOException; +import java.util.List; @Configuration public class ApplicationConfig { @@ -33,4 +40,31 @@ public class ApplicationConfig { .withAddress(resource.getURI()).build(VortigonServiceSrv.Iface.class); } + @Bean + public AuthContextProviderSrv.Iface orgManagerClient( + @Value("${orgManagement.url}") Resource resource, + @Value("${orgManagement.networkTimeout}") int networkTimeout + ) throws IOException { + return new THSpawnClientBuilder() + .withNetworkTimeout(networkTimeout) + .withMetaExtensions( + List.of( + UserIdentityIdExtensionKit.INSTANCE, + UserIdentityEmailExtensionKit.INSTANCE, + UserIdentityUsernameExtensionKit.INSTANCE, + UserIdentityRealmExtensionKit.INSTANCE + ) + ) + .withAddress(resource.getURI()).build(AuthContextProviderSrv.Iface.class); + } + + @Bean + public ArbiterSrv.Iface bouncerClient( + @Value("${bouncer.url}") Resource resource, + @Value("${bouncer.networkTimeout}") int networkTimeout + ) throws IOException { + return new THSpawnClientBuilder() + .withNetworkTimeout(networkTimeout) + .withAddress(resource.getURI()).build(ArbiterSrv.Iface.class); + } } diff --git a/src/main/java/com/rbkmoney/anapi/v2/config/properties/BouncerProperties.java b/src/main/java/com/rbkmoney/anapi/v2/config/properties/BouncerProperties.java new file mode 100644 index 0000000..403590f --- /dev/null +++ b/src/main/java/com/rbkmoney/anapi/v2/config/properties/BouncerProperties.java @@ -0,0 +1,26 @@ +package com.rbkmoney.anapi.v2.config.properties; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import javax.validation.constraints.NotEmpty; + +@Getter +@Setter +@Component +@ConfigurationProperties(prefix = "bouncer") +public class BouncerProperties { + + @NotEmpty + private String contextFragmentId; + @NotEmpty + private String deploymentId; + @NotEmpty + private String authMethod; + @NotEmpty + private String realm; + @NotEmpty + private String ruleSetId; +} diff --git a/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java b/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java index 4dbcaee..5602761 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java +++ b/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java @@ -1,7 +1,7 @@ package com.rbkmoney.anapi.v2.controller; import com.rbkmoney.anapi.v2.converter.search.request.*; -import com.rbkmoney.anapi.v2.security.AccessService; +import com.rbkmoney.anapi.v2.security.BouncerAccessService; import com.rbkmoney.anapi.v2.service.SearchService; import com.rbkmoney.anapi.v2.service.VortigonService; import com.rbkmoney.magista.*; @@ -19,7 +19,9 @@ import javax.validation.constraints.*; import java.time.OffsetDateTime; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; +import static com.rbkmoney.anapi.v2.util.ConverterUtil.merge; import static com.rbkmoney.anapi.v2.util.DeadlineUtil.checkDeadline; @@ -32,7 +34,7 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp private final SearchService searchService; private final VortigonService vortigonService; - private final AccessService accessService; + private final BouncerAccessService accessService; private final ParamsToPaymentSearchQueryConverter paymentSearchConverter; private final ParamsToChargebackSearchQueryConverter chargebackSearchConverter; private final ParamsToInvoiceSearchQueryConverter invoiceSearchConverter; @@ -77,8 +79,14 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Valid String continuationToken) { checkDeadline(xRequestDeadline, xRequestID); List shopIds = vortigonService.getShopIds(partyID, paymentInstitutionRealm); - //TODO: clarify, which shops should be checked for access. - accessService.checkAccess("searchPayments", partyID, shopID); + List requestShopIds = merge(shopID, shopIDs); + if (!requestShopIds.isEmpty()) { + shopIds = requestShopIds.stream() + .filter(shopIds::contains) + .collect(Collectors.toList()); + } + + accessService.checkAccess("searchPayments", partyID, shopIds); PaymentSearchQuery query = paymentSearchConverter.convert(partyID, fromTime, toTime, diff --git a/src/main/java/com/rbkmoney/anapi/v2/exception/AuthorizationException.java b/src/main/java/com/rbkmoney/anapi/v2/exception/AuthorizationException.java index 2b34037..6f5da4b 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/exception/AuthorizationException.java +++ b/src/main/java/com/rbkmoney/anapi/v2/exception/AuthorizationException.java @@ -5,4 +5,8 @@ public class AuthorizationException extends RuntimeException { public AuthorizationException(String s) { super(s); } + + public AuthorizationException(String message, Throwable cause) { + super(message, cause); + } } diff --git a/src/main/java/com/rbkmoney/anapi/v2/exception/BouncerException.java b/src/main/java/com/rbkmoney/anapi/v2/exception/BouncerException.java new file mode 100644 index 0000000..3ce5436 --- /dev/null +++ b/src/main/java/com/rbkmoney/anapi/v2/exception/BouncerException.java @@ -0,0 +1,11 @@ +package com.rbkmoney.anapi.v2.exception; + +public class BouncerException extends AuthorizationException { + public BouncerException(String s) { + super(s); + } + + public BouncerException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/com/rbkmoney/anapi/v2/security/AccessService.java b/src/main/java/com/rbkmoney/anapi/v2/security/AccessService.java deleted file mode 100644 index 85255fe..0000000 --- a/src/main/java/com/rbkmoney/anapi/v2/security/AccessService.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.rbkmoney.anapi.v2.security; - -public interface AccessService { - - void checkAccess(String operationId, String partyId, String shopId); - -} diff --git a/src/main/java/com/rbkmoney/anapi/v2/security/AnapiBouncerContext.java b/src/main/java/com/rbkmoney/anapi/v2/security/AnapiBouncerContext.java index ce7c4aa..c05800f 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/security/AnapiBouncerContext.java +++ b/src/main/java/com/rbkmoney/anapi/v2/security/AnapiBouncerContext.java @@ -1,19 +1,18 @@ package com.rbkmoney.anapi.v2.security; -import com.rbkmoney.bouncer.starter.api.BouncerContext; import lombok.Builder; import lombok.Data; -import lombok.EqualsAndHashCode; + +import java.util.List; @Builder -@EqualsAndHashCode(callSuper = true) @Data -public class AnapiBouncerContext extends BouncerContext { +public class AnapiBouncerContext { private final long tokenExpiration; private final String tokenId; private final String userId; private final String operationId; private final String partyId; - private final String shopId; + private final List shopIds; } diff --git a/src/main/java/com/rbkmoney/anapi/v2/security/BouncerAccessService.java b/src/main/java/com/rbkmoney/anapi/v2/security/BouncerAccessService.java index e3f4a9b..f0d28ad 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/security/BouncerAccessService.java +++ b/src/main/java/com/rbkmoney/anapi/v2/security/BouncerAccessService.java @@ -1,18 +1,20 @@ package com.rbkmoney.anapi.v2.security; import com.rbkmoney.anapi.v2.exception.AuthorizationException; +import com.rbkmoney.anapi.v2.service.BouncerService; import com.rbkmoney.anapi.v2.service.KeycloakService; -import com.rbkmoney.bouncer.starter.api.BouncerService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.keycloak.representations.AccessToken; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import java.util.List; + @Slf4j @RequiredArgsConstructor @Service -public class BouncerAccessService implements AccessService { +public class BouncerAccessService { private final BouncerService bouncerService; private final KeycloakService keycloakService; @@ -20,10 +22,9 @@ public class BouncerAccessService implements AccessService { @Value("${bouncer.auth.enabled}") private boolean authEnabled; - @Override - public void checkAccess(String operationId, String partyId, String shopId) { + public void checkAccess(String operationId, String partyId, List shopIds) { log.info("Check the user's rights to perform the operation {}", operationId); - var ctx = buildAnapiBouncerContext(operationId, partyId, shopId); + var ctx = buildAnapiBouncerContext(operationId, partyId, shopIds); if (!bouncerService.havePrivileges(ctx)) { if (authEnabled) { throw new AuthorizationException(String.format("No rights to perform %s", operationId)); @@ -33,12 +34,12 @@ public class BouncerAccessService implements AccessService { } } - private AnapiBouncerContext buildAnapiBouncerContext(String operationId, String partyId, String shopId) { + private AnapiBouncerContext buildAnapiBouncerContext(String operationId, String partyId, List shopIds) { AccessToken token = keycloakService.getAccessToken(); return AnapiBouncerContext.builder() .operationId(operationId) .partyId(partyId) - .shopId(shopId) + .shopIds(shopIds) .tokenExpiration(token.getExp()) .tokenId(token.getId()) .userId(token.getSubject()) diff --git a/src/main/java/com/rbkmoney/anapi/v2/security/BouncerContextFactory.java b/src/main/java/com/rbkmoney/anapi/v2/security/BouncerContextFactory.java index eedbb94..b74e948 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/security/BouncerContextFactory.java +++ b/src/main/java/com/rbkmoney/anapi/v2/security/BouncerContextFactory.java @@ -1,33 +1,80 @@ package com.rbkmoney.anapi.v2.security; -import com.rbkmoney.bouncer.context.v1.AnalyticsAPIOperation; -import com.rbkmoney.bouncer.context.v1.ContextAnalyticsAPI; -import com.rbkmoney.bouncer.context.v1.ContextFragment; -import com.rbkmoney.bouncer.context.v1.Entity; -import com.rbkmoney.bouncer.starter.AbstractBouncerContextFactory; -import com.rbkmoney.bouncer.starter.api.BouncerContext; -import com.rbkmoney.bouncer.starter.config.properties.BouncerProperties; +import com.rbkmoney.anapi.v2.config.properties.BouncerProperties; +import com.rbkmoney.anapi.v2.service.KeycloakService; +import com.rbkmoney.anapi.v2.service.OrgMgmtService; +import com.rbkmoney.bouncer.base.Entity; +import com.rbkmoney.bouncer.context.v1.*; +import com.rbkmoney.bouncer.ctx.ContextFragmentType; +import com.rbkmoney.bouncer.decisions.Context; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import org.apache.thrift.TSerializer; +import org.keycloak.representations.AccessToken; import org.springframework.stereotype.Component; -@Component -public class BouncerContextFactory extends AbstractBouncerContextFactory { +import java.time.Instant; +import java.util.Set; +import java.util.stream.Collectors; - public BouncerContextFactory(BouncerProperties bouncerProperties, - com.rbkmoney.bouncer.starter.UserAuthContextProvider userAuthContextProvider) { - super(bouncerProperties, userAuthContextProvider); +@RequiredArgsConstructor +@Component +public class BouncerContextFactory { + + private final BouncerProperties bouncerProperties; + private final OrgMgmtService orgMgmtService; + private final KeycloakService keycloakService; + + @SneakyThrows + public Context buildContext(AnapiBouncerContext bouncerContext) { + ContextFragment contextFragment = buildContextFragment(bouncerContext); + var serializer = new TSerializer(); + var fragment = new com.rbkmoney.bouncer.ctx.ContextFragment() + .setType(ContextFragmentType.v1_thrift_binary) + .setContent(serializer.serialize(contextFragment)); + + com.rbkmoney.bouncer.ctx.ContextFragment + userFragment = orgMgmtService.getUserAuthContext(keycloakService.getAccessToken().getSubject()); + Context context = new Context(); + context.putToFragments(bouncerProperties.getContextFragmentId(), fragment); + context.putToFragments("user", userFragment); + return context; } - @Override - protected void customizeContext(@org.jetbrains.annotations.NotNull ContextFragment contextFragment, - @org.jetbrains.annotations.NotNull BouncerContext bouncerContext) { - contextFragment.setAnapi(buildAnapiContext((AnapiBouncerContext) bouncerContext)); + private ContextFragment buildContextFragment(AnapiBouncerContext bouncerContext) { + Environment env = buildEnvironment(); + AccessToken accessToken = keycloakService.getAccessToken(); + ContextAnalyticsAPI contextAnalyticsAPI = buildAnapiContext(bouncerContext); + return new ContextFragment() + .setAuth(buildAuth(bouncerContext, accessToken)) + .setEnv(env) + .setAnapi(contextAnalyticsAPI); + } + + private Auth buildAuth(AnapiBouncerContext bouncerContext, AccessToken accessToken) { + Auth auth = new Auth(); + Set authScopeSet = bouncerContext.getShopIds().stream() + .map(shopId -> new AuthScope() + .setParty(new Entity().setId(bouncerContext.getPartyId())) + .setShop(new Entity().setId(shopId))) + .collect(Collectors.toSet()); + return auth.setToken(new Token().setId(accessToken.getId())) + .setMethod(bouncerProperties.getAuthMethod()) + .setExpiration(Instant.ofEpochSecond(accessToken.getExp()).toString()) + .setScope(authScopeSet); + } + + private Environment buildEnvironment() { + Deployment deployment = new Deployment() + .setId(bouncerProperties.getDeploymentId()); + return new Environment() + .setDeployment(deployment) + .setNow(Instant.now().toString()); } private ContextAnalyticsAPI buildAnapiContext(AnapiBouncerContext ctx) { return new ContextAnalyticsAPI() .setOp(new AnalyticsAPIOperation() - .setShop(new Entity().setId(ctx.getShopId())) - .setParty(new Entity().setId(ctx.getPartyId())) .setId(ctx.getOperationId())); } diff --git a/src/main/java/com/rbkmoney/anapi/v2/service/BouncerService.java b/src/main/java/com/rbkmoney/anapi/v2/service/BouncerService.java new file mode 100644 index 0000000..6925a6d --- /dev/null +++ b/src/main/java/com/rbkmoney/anapi/v2/service/BouncerService.java @@ -0,0 +1,39 @@ +package com.rbkmoney.anapi.v2.service; + +import com.rbkmoney.anapi.v2.config.properties.BouncerProperties; +import com.rbkmoney.anapi.v2.exception.BouncerException; +import com.rbkmoney.anapi.v2.security.AnapiBouncerContext; +import com.rbkmoney.anapi.v2.security.BouncerContextFactory; +import com.rbkmoney.bouncer.decisions.ArbiterSrv; +import com.rbkmoney.bouncer.decisions.Context; +import com.rbkmoney.bouncer.decisions.Judgement; +import com.rbkmoney.bouncer.decisions.Resolution; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.thrift.TException; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@RequiredArgsConstructor +public class BouncerService { + + private final BouncerProperties bouncerProperties; + private final BouncerContextFactory bouncerContextFactory; + private final ArbiterSrv.Iface bouncerClient; + + public boolean havePrivileges(AnapiBouncerContext bouncerContext) { + log.debug("Check access with bouncer context: {}", bouncerContext); + Context context = bouncerContextFactory.buildContext(bouncerContext); + log.debug("Built thrift context: {}", context); + try { + Judgement judge = bouncerClient.judge(bouncerProperties.getRuleSetId(), context); + log.debug("Have judge: {}", judge); + Resolution resolution = judge.getResolution(); + log.debug("Resolution: {}", resolution); + return resolution.isSetAllowed(); + } catch (TException e) { + throw new BouncerException("Error while call bouncer", e); + } + } +} diff --git a/src/main/java/com/rbkmoney/anapi/v2/service/OrgMgmtService.java b/src/main/java/com/rbkmoney/anapi/v2/service/OrgMgmtService.java new file mode 100644 index 0000000..22cb942 --- /dev/null +++ b/src/main/java/com/rbkmoney/anapi/v2/service/OrgMgmtService.java @@ -0,0 +1,22 @@ +package com.rbkmoney.anapi.v2.service; + +import com.rbkmoney.bouncer.ctx.ContextFragment; +import com.rbkmoney.orgmanagement.AuthContextProviderSrv; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@RequiredArgsConstructor +@Service +public class OrgMgmtService { + + private final AuthContextProviderSrv.Iface orgMgmtClient; + + public ContextFragment getUserAuthContext(String userId) { + try { + return orgMgmtClient.getUserContext(userId); + } catch (Exception e) { + throw new RuntimeException(String.format("Can't get user auth context: userId = %s", userId), e); + } + } + +} From 9e904fb195304b5fdf5d9da334321b2dcebf763e Mon Sep 17 00:00:00 2001 From: echerniak Date: Fri, 1 Oct 2021 17:30:55 +0300 Subject: [PATCH 27/37] WIP. Refactoring --- .../anapi/v2/controller/SearchController.java | 35 +++++++----------- ...aramsToChargebackSearchQueryConverter.java | 8 ++--- .../ParamsToInvoiceSearchQueryConverter.java | 7 ++-- .../ParamsToPaymentSearchQueryConverter.java | 6 +--- .../ParamsToPayoutSearchQueryConverter.java | 8 ++--- .../ParamsToRefundSearchQueryConverter.java | 5 +-- ...rAccessService.java => AccessService.java} | 36 +++++++++++++++++-- .../v2/security/BouncerContextFactory.java | 4 +-- .../anapi/v2/service/BouncerService.java | 4 +-- .../v2/controller/ErrorControllerTest.java | 24 ++++--------- 10 files changed, 63 insertions(+), 74 deletions(-) rename src/main/java/com/rbkmoney/anapi/v2/security/{BouncerAccessService.java => AccessService.java} (56%) diff --git a/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java b/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java index 5602761..dd6e21f 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java +++ b/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java @@ -1,9 +1,8 @@ package com.rbkmoney.anapi.v2.controller; import com.rbkmoney.anapi.v2.converter.search.request.*; -import com.rbkmoney.anapi.v2.security.BouncerAccessService; +import com.rbkmoney.anapi.v2.security.AccessService; import com.rbkmoney.anapi.v2.service.SearchService; -import com.rbkmoney.anapi.v2.service.VortigonService; import com.rbkmoney.magista.*; import com.rbkmoney.openapi.anapi_v2.api.*; import com.rbkmoney.openapi.anapi_v2.model.*; @@ -19,7 +18,6 @@ import javax.validation.constraints.*; import java.time.OffsetDateTime; import java.util.List; import java.util.Optional; -import java.util.stream.Collectors; import static com.rbkmoney.anapi.v2.util.ConverterUtil.merge; import static com.rbkmoney.anapi.v2.util.DeadlineUtil.checkDeadline; @@ -33,8 +31,7 @@ import static com.rbkmoney.anapi.v2.util.DeadlineUtil.checkDeadline; public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesApi, PayoutsApi, RefundsApi { private final SearchService searchService; - private final VortigonService vortigonService; - private final BouncerAccessService accessService; + private final AccessService accessService; private final ParamsToPaymentSearchQueryConverter paymentSearchConverter; private final ParamsToChargebackSearchQueryConverter chargebackSearchConverter; private final ParamsToInvoiceSearchQueryConverter invoiceSearchConverter; @@ -78,22 +75,14 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Valid List excludedShops, @Valid String continuationToken) { checkDeadline(xRequestDeadline, xRequestID); - List shopIds = vortigonService.getShopIds(partyID, paymentInstitutionRealm); - List requestShopIds = merge(shopID, shopIDs); - if (!requestShopIds.isEmpty()) { - shopIds = requestShopIds.stream() - .filter(shopIds::contains) - .collect(Collectors.toList()); - } + shopIDs = accessService + .getAccessibleShops("searchPayments", partyID, merge(shopID, shopIDs), paymentInstitutionRealm); - accessService.checkAccess("searchPayments", partyID, shopIds); PaymentSearchQuery query = paymentSearchConverter.convert(partyID, fromTime, toTime, limit, - shopID, shopIDs, - paymentInstitutionRealm, invoiceIDs, paymentStatus, paymentFlow, paymentMethod, @@ -138,13 +127,13 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Valid List chargebackCategories, @Valid String continuationToken) { checkDeadline(xRequestDeadline, xRequestID); + shopIDs = accessService + .getAccessibleShops("searchChargebacks", partyID, merge(shopID, shopIDs), paymentInstitutionRealm); ChargebackSearchQuery query = chargebackSearchConverter.convert(partyID, fromTime, toTime, limit, - shopID, shopIDs, - paymentInstitutionRealm, offset, invoiceID, paymentID, @@ -177,13 +166,13 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Valid List excludedShops, @Valid String continuationToken) { checkDeadline(xRequestDeadline, xRequestID); + shopIDs = accessService + .getAccessibleShops("searchInvoices", partyID, merge(shopID, shopIDs), paymentInstitutionRealm); InvoiceSearchQuery query = invoiceSearchConverter.convert(partyID, fromTime, toTime, limit, - shopID, shopIDs, - paymentInstitutionRealm, invoiceIDs, invoiceStatus, invoiceID, @@ -212,13 +201,13 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Valid List excludedShops, @Valid String continuationToken) { checkDeadline(xRequestDeadline, xRequestID); + shopIDs = accessService + .getAccessibleShops("searchPayouts", partyID, merge(shopID, shopIDs), paymentInstitutionRealm); PayoutSearchQuery query = payoutSearchConverter.convert(partyID, fromTime, toTime, limit, - shopID, shopIDs, - paymentInstitutionRealm, offset, payoutID, payoutToolType, @@ -248,13 +237,13 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Valid List excludedShops, @Valid String continuationToken) { checkDeadline(xRequestDeadline, xRequestID); + shopIDs = accessService + .getAccessibleShops("searchRefunds", partyID, merge(shopID, shopIDs), paymentInstitutionRealm); RefundSearchQuery query = refundSearchConverter.convert(partyID, fromTime, toTime, limit, - shopID, shopIDs, - paymentInstitutionRealm, offset, invoiceIDs, invoiceID, diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToChargebackSearchQueryConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToChargebackSearchQueryConverter.java index 40f183b..47e0bec 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToChargebackSearchQueryConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToChargebackSearchQueryConverter.java @@ -13,7 +13,6 @@ import java.util.List; import java.util.stream.Collectors; import static com.rbkmoney.anapi.v2.util.ConverterUtil.fillCommonParams; -import static com.rbkmoney.anapi.v2.util.ConverterUtil.merge; @Component public class ParamsToChargebackSearchQueryConverter { @@ -22,9 +21,7 @@ public class ParamsToChargebackSearchQueryConverter { OffsetDateTime fromTime, OffsetDateTime toTime, Integer limit, - String shopID, List shopIDs, - String paymentInstitutionRealm, Integer offset, String invoiceID, String paymentID, @@ -33,11 +30,10 @@ public class ParamsToChargebackSearchQueryConverter { List chargebackStages, List chargebackCategories, String continuationToken) { - //TODO: Mapping for paymentInstitutionRealm, offset + //TODO: Mapping for offset return new ChargebackSearchQuery() .setCommonSearchQueryParams( - fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), - continuationToken)) + fillCommonParams(fromTime, toTime, limit, partyID, shopIDs, continuationToken)) .setInvoiceIds(invoiceID != null ? List.of(invoiceID) : null) .setPaymentId(paymentID) .setChargebackId(chargebackID) diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToInvoiceSearchQueryConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToInvoiceSearchQueryConverter.java index ad24ed4..0418356 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToInvoiceSearchQueryConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToInvoiceSearchQueryConverter.java @@ -19,9 +19,7 @@ public class ParamsToInvoiceSearchQueryConverter { OffsetDateTime fromTime, OffsetDateTime toTime, Integer limit, - String shopID, List shopIDs, - String paymentInstitutionRealm, List invoiceIDs, String invoiceStatus, String invoiceID, @@ -30,11 +28,10 @@ public class ParamsToInvoiceSearchQueryConverter { Long invoiceAmountTo, List excludedShops, String continuationToken) { - //TODO: Mapping for paymentInstitutionRealm, excludedShops + //TODO: Mapping for excludedShops return new InvoiceSearchQuery() .setCommonSearchQueryParams( - fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), - continuationToken)) + fillCommonParams(fromTime, toTime, limit, partyID, shopIDs, continuationToken)) .setPaymentParams( getPaymentParams(invoiceAmountFrom, invoiceAmountTo) ) diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPaymentSearchQueryConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPaymentSearchQueryConverter.java index 200d9b9..4617d19 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPaymentSearchQueryConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPaymentSearchQueryConverter.java @@ -30,9 +30,7 @@ public class ParamsToPaymentSearchQueryConverter { OffsetDateTime fromTime, OffsetDateTime toTime, Integer limit, - String shopID, List shopIDs, - String paymentInstitutionRealm, List invoiceIDs, String paymentStatus, String paymentFlow, String paymentMethod, @@ -54,11 +52,9 @@ public class ParamsToPaymentSearchQueryConverter { Long paymentAmountTo, List excludedShops, String continuationToken) { - //TODO: Mapping for paymentInstitutionRealm PaymentSearchQuery query = new PaymentSearchQuery() .setCommonSearchQueryParams( - fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), - continuationToken)) + fillCommonParams(fromTime, toTime, limit, partyID, shopIDs, continuationToken)) .setExcludedShopIds(excludedShops) .setExternalId(externalID) .setInvoiceIds(merge(invoiceID, invoiceIDs)); diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPayoutSearchQueryConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPayoutSearchQueryConverter.java index 7c31c4b..a9e6977 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPayoutSearchQueryConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPayoutSearchQueryConverter.java @@ -12,7 +12,6 @@ import java.time.OffsetDateTime; import java.util.List; import static com.rbkmoney.anapi.v2.util.ConverterUtil.fillCommonParams; -import static com.rbkmoney.anapi.v2.util.ConverterUtil.merge; @Component public class ParamsToPayoutSearchQueryConverter { @@ -21,19 +20,16 @@ public class ParamsToPayoutSearchQueryConverter { OffsetDateTime fromTime, OffsetDateTime toTime, Integer limit, - String shopID, List shopIDs, - String paymentInstitutionRealm, Integer offset, String payoutID, String payoutToolType, List excludedShops, String continuationToken) { - //TODO: Mapping for paymentInstitutionRealm, offset, excludedShops + //TODO: Mapping for offset, excludedShops return new PayoutSearchQuery() .setCommonSearchQueryParams( - fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), - continuationToken)) + fillCommonParams(fromTime, toTime, limit, partyID, shopIDs, continuationToken)) .setPayoutId(payoutID) .setPayoutType(payoutToolType != null ? mapToDamselPayoutToolInfo(payoutToolType) : null); } diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToRefundSearchQueryConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToRefundSearchQueryConverter.java index 13cf8c1..f637883 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToRefundSearchQueryConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToRefundSearchQueryConverter.java @@ -19,9 +19,7 @@ public class ParamsToRefundSearchQueryConverter { OffsetDateTime fromTime, OffsetDateTime toTime, Integer limit, - String shopID, List shopIDs, - String paymentInstitutionRealm, Integer offset, List invoiceIDs, String invoiceID, @@ -34,8 +32,7 @@ public class ParamsToRefundSearchQueryConverter { //TODO: Mapping for paymentInstitutionRealm, offset, excludedShops return new RefundSearchQuery() .setCommonSearchQueryParams( - fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs), - continuationToken)) + fillCommonParams(fromTime, toTime, limit, partyID, shopIDs, continuationToken)) .setRefundStatus(refundStatus != null ? getRefundStatus(refundStatus) : null) .setInvoiceIds(merge(invoiceID, invoiceIDs)) .setExternalId(externalID) diff --git a/src/main/java/com/rbkmoney/anapi/v2/security/BouncerAccessService.java b/src/main/java/com/rbkmoney/anapi/v2/security/AccessService.java similarity index 56% rename from src/main/java/com/rbkmoney/anapi/v2/security/BouncerAccessService.java rename to src/main/java/com/rbkmoney/anapi/v2/security/AccessService.java index f0d28ad..afc88a4 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/security/BouncerAccessService.java +++ b/src/main/java/com/rbkmoney/anapi/v2/security/AccessService.java @@ -3,35 +3,65 @@ package com.rbkmoney.anapi.v2.security; import com.rbkmoney.anapi.v2.exception.AuthorizationException; import com.rbkmoney.anapi.v2.service.BouncerService; import com.rbkmoney.anapi.v2.service.KeycloakService; +import com.rbkmoney.anapi.v2.service.VortigonService; +import com.rbkmoney.bouncer.base.Entity; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.keycloak.representations.AccessToken; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; @Slf4j @RequiredArgsConstructor @Service -public class BouncerAccessService { +public class AccessService { + private final VortigonService vortigonService; private final BouncerService bouncerService; private final KeycloakService keycloakService; @Value("${bouncer.auth.enabled}") private boolean authEnabled; - public void checkAccess(String operationId, String partyId, List shopIds) { + public List getAccessibleShops(String operationId, String partyId, List requestShopIds, + String realm) { + + List shopIds = vortigonService.getShopIds(partyId, realm); + + if (!shopIds.isEmpty()) { + shopIds = requestShopIds.stream() + .filter(shopIds::contains) + .collect(Collectors.toList()); + } + log.info("Check the user's rights to perform the operation {}", operationId); var ctx = buildAnapiBouncerContext(operationId, partyId, shopIds); - if (!bouncerService.havePrivileges(ctx)) { + var resolution = bouncerService.getResolution(ctx); + if (resolution.isSetForbidden()) { if (authEnabled) { throw new AuthorizationException(String.format("No rights to perform %s", operationId)); } else { log.warn("No rights to perform {}", operationId); } } + + if (resolution.isSetRestricted()) { + if (authEnabled) { + return resolution.getRestricted().getRestrictions().getAnapi().getOp().getShops().stream() + .map(Entity::getId) + .collect(Collectors.toList()); + } else { + log.warn("Rights to perform {} are restricted", operationId); + } + } + + return new ArrayList<>(shopIds); + + } private AnapiBouncerContext buildAnapiBouncerContext(String operationId, String partyId, List shopIds) { diff --git a/src/main/java/com/rbkmoney/anapi/v2/security/BouncerContextFactory.java b/src/main/java/com/rbkmoney/anapi/v2/security/BouncerContextFactory.java index b74e948..a849670 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/security/BouncerContextFactory.java +++ b/src/main/java/com/rbkmoney/anapi/v2/security/BouncerContextFactory.java @@ -44,11 +44,11 @@ public class BouncerContextFactory { private ContextFragment buildContextFragment(AnapiBouncerContext bouncerContext) { Environment env = buildEnvironment(); AccessToken accessToken = keycloakService.getAccessToken(); - ContextAnalyticsAPI contextAnalyticsAPI = buildAnapiContext(bouncerContext); + ContextAnalyticsAPI contextAnalyticsApi = buildAnapiContext(bouncerContext); return new ContextFragment() .setAuth(buildAuth(bouncerContext, accessToken)) .setEnv(env) - .setAnapi(contextAnalyticsAPI); + .setAnapi(contextAnalyticsApi); } private Auth buildAuth(AnapiBouncerContext bouncerContext, AccessToken accessToken) { diff --git a/src/main/java/com/rbkmoney/anapi/v2/service/BouncerService.java b/src/main/java/com/rbkmoney/anapi/v2/service/BouncerService.java index 6925a6d..afecf56 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/service/BouncerService.java +++ b/src/main/java/com/rbkmoney/anapi/v2/service/BouncerService.java @@ -22,7 +22,7 @@ public class BouncerService { private final BouncerContextFactory bouncerContextFactory; private final ArbiterSrv.Iface bouncerClient; - public boolean havePrivileges(AnapiBouncerContext bouncerContext) { + public Resolution getResolution(AnapiBouncerContext bouncerContext) { log.debug("Check access with bouncer context: {}", bouncerContext); Context context = bouncerContextFactory.buildContext(bouncerContext); log.debug("Built thrift context: {}", context); @@ -31,7 +31,7 @@ public class BouncerService { log.debug("Have judge: {}", judge); Resolution resolution = judge.getResolution(); log.debug("Resolution: {}", resolution); - return resolution.isSetAllowed(); + return resolution; } catch (TException e) { throw new BouncerException("Error while call bouncer", e); } diff --git a/src/test/java/com/rbkmoney/anapi/v2/controller/ErrorControllerTest.java b/src/test/java/com/rbkmoney/anapi/v2/controller/ErrorControllerTest.java index fbc30a1..30f1e69 100644 --- a/src/test/java/com/rbkmoney/anapi/v2/controller/ErrorControllerTest.java +++ b/src/test/java/com/rbkmoney/anapi/v2/controller/ErrorControllerTest.java @@ -1,9 +1,8 @@ package com.rbkmoney.anapi.v2.controller; import com.rbkmoney.anapi.v2.config.AbstractKeycloakOpenIdAsWiremockConfig; -import com.rbkmoney.anapi.v2.converter.search.request.*; +import com.rbkmoney.anapi.v2.converter.search.request.ParamsToRefundSearchQueryConverter; import com.rbkmoney.anapi.v2.exception.BadRequestException; -import com.rbkmoney.anapi.v2.service.SearchService; import com.rbkmoney.anapi.v2.testutil.OpenApiUtil; import com.rbkmoney.openapi.anapi_v2.model.DefaultLogicError; import org.junit.jupiter.api.Test; @@ -28,17 +27,6 @@ class ErrorControllerTest extends AbstractKeycloakOpenIdAsWiremockConfig { @Autowired private MockMvc mockMvc; - - @MockBean - private SearchService searchService; - @MockBean - private ParamsToPaymentSearchQueryConverter paymentSearchConverter; - @MockBean - private ParamsToChargebackSearchQueryConverter chargebackSearchConverter; - @MockBean - private ParamsToInvoiceSearchQueryConverter invoiceSearchConverter; - @MockBean - private ParamsToPayoutSearchQueryConverter payoutSearchConverter; @MockBean private ParamsToRefundSearchQueryConverter refundSearchConverter; @@ -65,8 +53,8 @@ class ErrorControllerTest extends AbstractKeycloakOpenIdAsWiremockConfig { void testBadRequestException() throws Exception { String message = "Error!"; doThrow(new BadRequestException(message)).when(refundSearchConverter) - .convert(any(), any(), any(), any(), - any(), any(), any(), any(), + .convert(any(), any(), any(), + any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any()); @@ -86,7 +74,7 @@ class ErrorControllerTest extends AbstractKeycloakOpenIdAsWiremockConfig { .andExpect(jsonPath("$.message").value(message)); verify(refundSearchConverter, times(1)) - .convert(any(), any(), any(), any(), + .convert(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any()); @@ -131,7 +119,7 @@ class ErrorControllerTest extends AbstractKeycloakOpenIdAsWiremockConfig { @Test void testInternalException() throws Exception { doThrow(new RuntimeException()).when(refundSearchConverter) - .convert(any(), any(), any(), any(), + .convert(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any()); @@ -150,7 +138,7 @@ class ErrorControllerTest extends AbstractKeycloakOpenIdAsWiremockConfig { .andExpect(status().isInternalServerError()) .andExpect(jsonPath("$").doesNotExist()); verify(refundSearchConverter, times(1)) - .convert(any(), any(), any(), any(), + .convert(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any()); From 9aec6a407dc9f4d4dc1f8c9547764cc835da2b75 Mon Sep 17 00:00:00 2001 From: echerniak Date: Fri, 1 Oct 2021 17:53:52 +0300 Subject: [PATCH 28/37] WIP. Mapping fixes --- .../anapi/v2/controller/SearchController.java | 6 ------ .../ParamsToChargebackSearchQueryConverter.java | 2 -- .../request/ParamsToInvoiceSearchQueryConverter.java | 2 -- .../request/ParamsToPayoutSearchQueryConverter.java | 3 --- .../request/ParamsToRefundSearchQueryConverter.java | 3 --- .../StatPaymentToPaymentSearchResultConverter.java | 2 +- .../anapi/v2/controller/ErrorControllerTest.java | 12 ++++-------- 7 files changed, 5 insertions(+), 25 deletions(-) diff --git a/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java b/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java index dd6e21f..f79a3d8 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java +++ b/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java @@ -134,7 +134,6 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp toTime, limit, shopIDs, - offset, invoiceID, paymentID, chargebackID, @@ -179,7 +178,6 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp externalID, invoiceAmountFrom, invoiceAmountTo, - excludedShops, continuationToken); InlineResponse2009 response = searchService.findInvoices(query); return ResponseEntity.ok(response); @@ -208,10 +206,8 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp toTime, limit, shopIDs, - offset, payoutID, payoutToolType, - excludedShops, continuationToken); InlineResponse20011 response = searchService.findPayouts(query); return ResponseEntity.ok(response); @@ -244,14 +240,12 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp toTime, limit, shopIDs, - offset, invoiceIDs, invoiceID, paymentID, refundID, externalID, refundStatus, - excludedShops, continuationToken); InlineResponse20012 response = searchService.findRefunds(query); return ResponseEntity.ok(response); diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToChargebackSearchQueryConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToChargebackSearchQueryConverter.java index 47e0bec..6dafe5c 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToChargebackSearchQueryConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToChargebackSearchQueryConverter.java @@ -22,7 +22,6 @@ public class ParamsToChargebackSearchQueryConverter { OffsetDateTime toTime, Integer limit, List shopIDs, - Integer offset, String invoiceID, String paymentID, String chargebackID, @@ -30,7 +29,6 @@ public class ParamsToChargebackSearchQueryConverter { List chargebackStages, List chargebackCategories, String continuationToken) { - //TODO: Mapping for offset return new ChargebackSearchQuery() .setCommonSearchQueryParams( fillCommonParams(fromTime, toTime, limit, partyID, shopIDs, continuationToken)) diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToInvoiceSearchQueryConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToInvoiceSearchQueryConverter.java index 0418356..e20b41f 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToInvoiceSearchQueryConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToInvoiceSearchQueryConverter.java @@ -26,9 +26,7 @@ public class ParamsToInvoiceSearchQueryConverter { String externalID, Long invoiceAmountFrom, Long invoiceAmountTo, - List excludedShops, String continuationToken) { - //TODO: Mapping for excludedShops return new InvoiceSearchQuery() .setCommonSearchQueryParams( fillCommonParams(fromTime, toTime, limit, partyID, shopIDs, continuationToken)) diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPayoutSearchQueryConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPayoutSearchQueryConverter.java index a9e6977..7a5a73a 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPayoutSearchQueryConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPayoutSearchQueryConverter.java @@ -21,12 +21,9 @@ public class ParamsToPayoutSearchQueryConverter { OffsetDateTime toTime, Integer limit, List shopIDs, - Integer offset, String payoutID, String payoutToolType, - List excludedShops, String continuationToken) { - //TODO: Mapping for offset, excludedShops return new PayoutSearchQuery() .setCommonSearchQueryParams( fillCommonParams(fromTime, toTime, limit, partyID, shopIDs, continuationToken)) diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToRefundSearchQueryConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToRefundSearchQueryConverter.java index f637883..79dee2e 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToRefundSearchQueryConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToRefundSearchQueryConverter.java @@ -20,16 +20,13 @@ public class ParamsToRefundSearchQueryConverter { OffsetDateTime toTime, Integer limit, List shopIDs, - Integer offset, List invoiceIDs, String invoiceID, String paymentID, String refundID, String externalID, String refundStatus, - List excludedShops, String continuationToken) { - //TODO: Mapping for paymentInstitutionRealm, offset, excludedShops return new RefundSearchQuery() .setCommonSearchQueryParams( fillCommonParams(fromTime, toTime, limit, partyID, shopIDs, continuationToken)) diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverter.java index f287964..b23be5f 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverter.java @@ -71,7 +71,7 @@ public class StatPaymentToPaymentSearchResultConverter { case cancelled -> CANCELLED; case refunded -> REFUNDED; case failed -> FAILED; - //case charged_back ->; TODO: OpenAPI missing status, should be added? + case charged_back -> CHARGEDBACK; default -> throw new IllegalArgumentException( String.format("Payment status %s cannot be processed", status)); diff --git a/src/test/java/com/rbkmoney/anapi/v2/controller/ErrorControllerTest.java b/src/test/java/com/rbkmoney/anapi/v2/controller/ErrorControllerTest.java index 30f1e69..97f23ba 100644 --- a/src/test/java/com/rbkmoney/anapi/v2/controller/ErrorControllerTest.java +++ b/src/test/java/com/rbkmoney/anapi/v2/controller/ErrorControllerTest.java @@ -53,8 +53,7 @@ class ErrorControllerTest extends AbstractKeycloakOpenIdAsWiremockConfig { void testBadRequestException() throws Exception { String message = "Error!"; doThrow(new BadRequestException(message)).when(refundSearchConverter) - .convert(any(), any(), any(), - any(), any(), any(), + .convert(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any()); @@ -74,8 +73,7 @@ class ErrorControllerTest extends AbstractKeycloakOpenIdAsWiremockConfig { .andExpect(jsonPath("$.message").value(message)); verify(refundSearchConverter, times(1)) - .convert(any(), any(), - any(), any(), any(), any(), + .convert(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any()); } @@ -119,8 +117,7 @@ class ErrorControllerTest extends AbstractKeycloakOpenIdAsWiremockConfig { @Test void testInternalException() throws Exception { doThrow(new RuntimeException()).when(refundSearchConverter) - .convert(any(), any(), - any(), any(), any(), any(), + .convert(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any()); @@ -138,8 +135,7 @@ class ErrorControllerTest extends AbstractKeycloakOpenIdAsWiremockConfig { .andExpect(status().isInternalServerError()) .andExpect(jsonPath("$").doesNotExist()); verify(refundSearchConverter, times(1)) - .convert(any(), any(), - any(), any(), any(), any(), + .convert(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any()); } From 5a30de28491ee1c8ad22b3c140532141f12f1ed2 Mon Sep 17 00:00:00 2001 From: echerniak Date: Mon, 4 Oct 2021 16:38:44 +0300 Subject: [PATCH 29/37] Tests fixed --- pom.xml | 2 +- .../anapi/v2/config/ApplicationConfig.java | 8 +- .../config/properties/BouncerProperties.java | 4 +- .../ParamsToPaymentSearchQueryConverter.java | 7 +- .../ParamsToPayoutSearchQueryConverter.java | 24 ++---- .../anapi/v2/security/AccessService.java | 5 +- src/main/resources/application.yml | 16 ++++ .../anapi/v2/SearchChargebacksTest.java | 68 ++++++++++----- .../rbkmoney/anapi/v2/SearchInvoicesTest.java | 32 ++++++- .../rbkmoney/anapi/v2/SearchPaymentsTest.java | 32 ++++++- .../rbkmoney/anapi/v2/SearchPayoutsTest.java | 32 ++++++- .../rbkmoney/anapi/v2/SearchRefundsTest.java | 34 +++++++- .../v2/controller/ErrorControllerTest.java | 78 +++++++++++++---- .../anapi/v2/testutil/MagistaUtil.java | 85 ++++++++++--------- .../anapi/v2/util/ConverterUtilTest.java | 4 +- 15 files changed, 321 insertions(+), 110 deletions(-) diff --git a/pom.xml b/pom.xml index 39265dd..a50dba7 100644 --- a/pom.xml +++ b/pom.xml @@ -74,7 +74,7 @@ com.rbkmoney magista-proto - 1.12-7be076b + SNAPSHOT com.rbkmoney diff --git a/src/main/java/com/rbkmoney/anapi/v2/config/ApplicationConfig.java b/src/main/java/com/rbkmoney/anapi/v2/config/ApplicationConfig.java index d75b87a..ab02d07 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/config/ApplicationConfig.java +++ b/src/main/java/com/rbkmoney/anapi/v2/config/ApplicationConfig.java @@ -42,8 +42,8 @@ public class ApplicationConfig { @Bean public AuthContextProviderSrv.Iface orgManagerClient( - @Value("${orgManagement.url}") Resource resource, - @Value("${orgManagement.networkTimeout}") int networkTimeout + @Value("${service.orgManagement.url}") Resource resource, + @Value("${service.orgManagement.networkTimeout}") int networkTimeout ) throws IOException { return new THSpawnClientBuilder() .withNetworkTimeout(networkTimeout) @@ -60,8 +60,8 @@ public class ApplicationConfig { @Bean public ArbiterSrv.Iface bouncerClient( - @Value("${bouncer.url}") Resource resource, - @Value("${bouncer.networkTimeout}") int networkTimeout + @Value("${service.bouncer.url}") Resource resource, + @Value("${service.bouncer.networkTimeout}") int networkTimeout ) throws IOException { return new THSpawnClientBuilder() .withNetworkTimeout(networkTimeout) diff --git a/src/main/java/com/rbkmoney/anapi/v2/config/properties/BouncerProperties.java b/src/main/java/com/rbkmoney/anapi/v2/config/properties/BouncerProperties.java index 403590f..a0cd5f6 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/config/properties/BouncerProperties.java +++ b/src/main/java/com/rbkmoney/anapi/v2/config/properties/BouncerProperties.java @@ -4,13 +4,15 @@ import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; +import org.springframework.validation.annotation.Validated; import javax.validation.constraints.NotEmpty; @Getter @Setter @Component -@ConfigurationProperties(prefix = "bouncer") +@Validated +@ConfigurationProperties(prefix = "service.bouncer") public class BouncerProperties { @NotEmpty diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPaymentSearchQueryConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPaymentSearchQueryConverter.java index 4617d19..27d67cf 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPaymentSearchQueryConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPaymentSearchQueryConverter.java @@ -7,6 +7,7 @@ import com.rbkmoney.damsel.domain.LegacyTerminalPaymentProvider; import com.rbkmoney.magista.InvoicePaymentStatus; import com.rbkmoney.magista.PaymentParams; import com.rbkmoney.magista.PaymentSearchQuery; +import com.rbkmoney.magista.PaymentToolType; import com.rbkmoney.openapi.anapi_v2.model.BankCardPaymentSystem; import com.rbkmoney.openapi.anapi_v2.model.BankCardTokenProvider; import com.rbkmoney.openapi.anapi_v2.model.PaymentStatus; @@ -20,8 +21,8 @@ import static com.rbkmoney.anapi.v2.util.ConverterUtil.merge; import static com.rbkmoney.magista.InvoicePaymentFlowType.hold; import static com.rbkmoney.magista.InvoicePaymentFlowType.instant; import static com.rbkmoney.magista.InvoicePaymentStatus.*; -import static com.rbkmoney.magista.PaymentTool.bank_card; -import static com.rbkmoney.magista.PaymentTool.payment_terminal; +import static com.rbkmoney.magista.PaymentToolType.bank_card; +import static com.rbkmoney.magista.PaymentToolType.payment_terminal; @Component public class ParamsToPaymentSearchQueryConverter { @@ -95,7 +96,7 @@ public class ParamsToPaymentSearchQueryConverter { return query; } - private com.rbkmoney.magista.PaymentTool mapToPaymentTool(String paymentMethod) { + private PaymentToolType mapToPaymentTool(String paymentMethod) { return switch (paymentMethod) { case "bankCard" -> bank_card; case "paymentTerminal" -> payment_terminal; diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPayoutSearchQueryConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPayoutSearchQueryConverter.java index 7a5a73a..aaabe3a 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPayoutSearchQueryConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPayoutSearchQueryConverter.java @@ -1,11 +1,8 @@ package com.rbkmoney.anapi.v2.converter.search.request; import com.rbkmoney.anapi.v2.exception.BadRequestException; -import com.rbkmoney.damsel.domain.PaymentInstitutionAccount; -import com.rbkmoney.damsel.domain.PayoutToolInfo; -import com.rbkmoney.damsel.domain.RussianBankAccount; -import com.rbkmoney.damsel.domain.WalletInfo; import com.rbkmoney.magista.PayoutSearchQuery; +import com.rbkmoney.magista.PayoutToolType; import org.springframework.stereotype.Component; import java.time.OffsetDateTime; @@ -28,21 +25,16 @@ public class ParamsToPayoutSearchQueryConverter { .setCommonSearchQueryParams( fillCommonParams(fromTime, toTime, limit, partyID, shopIDs, continuationToken)) .setPayoutId(payoutID) - .setPayoutType(payoutToolType != null ? mapToDamselPayoutToolInfo(payoutToolType) : null); + .setPayoutType(payoutToolType != null ? mapToPayoutToolType(payoutToolType) : null); } - private PayoutToolInfo mapToDamselPayoutToolInfo(String payoutToolType) { - var payoutToolInfo = new PayoutToolInfo(); - switch (payoutToolType) { - case "PayoutAccount" -> payoutToolInfo - .setRussianBankAccount(new RussianBankAccount());//TODO: Russian or International? - case "Wallet" -> payoutToolInfo.setWalletInfo(new WalletInfo()); - case "PaymentInstitutionAccount" -> payoutToolInfo - .setPaymentInstitutionAccount(new PaymentInstitutionAccount()); + private PayoutToolType mapToPayoutToolType(String payoutToolType) { + return switch (payoutToolType) { + case "PayoutAccount" -> PayoutToolType.payout_account; + case "Wallet" -> PayoutToolType.wallet; + case "PaymentInstitutionAccount" -> PayoutToolType.payment_institution_account; default -> throw new BadRequestException( String.format("PayoutToolType %s cannot be processed", payoutToolType)); - } - - return payoutToolInfo; + }; } } diff --git a/src/main/java/com/rbkmoney/anapi/v2/security/AccessService.java b/src/main/java/com/rbkmoney/anapi/v2/security/AccessService.java index afc88a4..cba7cef 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/security/AccessService.java +++ b/src/main/java/com/rbkmoney/anapi/v2/security/AccessService.java @@ -13,6 +13,7 @@ import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.stream.Collectors; @Slf4j @@ -24,13 +25,13 @@ public class AccessService { private final BouncerService bouncerService; private final KeycloakService keycloakService; - @Value("${bouncer.auth.enabled}") + @Value("${service.bouncer.auth.enabled}") private boolean authEnabled; public List getAccessibleShops(String operationId, String partyId, List requestShopIds, String realm) { - List shopIds = vortigonService.getShopIds(partyId, realm); + List shopIds = vortigonService.getShopIds(partyId, Objects.requireNonNullElse(realm, "live")); if (!shopIds.isEmpty()) { shopIds = requestShopIds.stream() diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 0b75ee6..16e0794 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -29,6 +29,22 @@ service: magista: url: http://magista:8022/stat networkTimeout: 5000 + vortigon: + url: http://hellgate:8022/v1/processing/partymgmt + networkTimeout: 5000 + orgManagement: + url: http://localhost:8022/change_it + networkTimeout: 5000 + bouncer: + url: http://localhost:8022/change_it + networkTimeout: 10000 + context-fragment-id: anapi + deployment-id: production + auth-method: SessionToken + realm: external + rule-set-id: change_it + auth: + enabled: false spring: application: diff --git a/src/test/java/com/rbkmoney/anapi/v2/SearchChargebacksTest.java b/src/test/java/com/rbkmoney/anapi/v2/SearchChargebacksTest.java index 1665ecf..9540384 100644 --- a/src/test/java/com/rbkmoney/anapi/v2/SearchChargebacksTest.java +++ b/src/test/java/com/rbkmoney/anapi/v2/SearchChargebacksTest.java @@ -3,8 +3,11 @@ package com.rbkmoney.anapi.v2; import com.rbkmoney.anapi.v2.config.AbstractKeycloakOpenIdAsWiremockConfig; import com.rbkmoney.anapi.v2.testutil.MagistaUtil; import com.rbkmoney.anapi.v2.testutil.OpenApiUtil; +import com.rbkmoney.bouncer.decisions.ArbiterSrv; +import com.rbkmoney.damsel.vortigon.VortigonServiceSrv; import com.rbkmoney.magista.MerchantStatisticsServiceSrv; import com.rbkmoney.openapi.anapi_v2.model.DefaultLogicError; +import com.rbkmoney.orgmanagement.AuthContextProviderSrv; import lombok.SneakyThrows; import org.apache.thrift.TException; import org.junit.jupiter.api.AfterEach; @@ -19,7 +22,10 @@ import org.springframework.util.MultiValueMap; import java.time.Instant; import java.time.temporal.ChronoUnit; +import java.util.List; +import static com.rbkmoney.anapi.v2.testutil.MagistaUtil.createContextFragment; +import static com.rbkmoney.anapi.v2.testutil.MagistaUtil.createJudgementAllowed; import static java.util.UUID.randomUUID; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; @@ -33,6 +39,12 @@ class SearchChargebacksTest extends AbstractKeycloakOpenIdAsWiremockConfig { @MockBean public MerchantStatisticsServiceSrv.Iface magistaClient; + @MockBean + public VortigonServiceSrv.Iface vortigonClient; + @MockBean + public AuthContextProviderSrv.Iface orgMgmtClient; + @MockBean + public ArbiterSrv.Iface bouncerClient; @Autowired private MockMvc mvc; @@ -44,7 +56,7 @@ class SearchChargebacksTest extends AbstractKeycloakOpenIdAsWiremockConfig { @BeforeEach public void init() { mocks = MockitoAnnotations.openMocks(this); - preparedMocks = new Object[] {magistaClient}; + preparedMocks = new Object[] {magistaClient, vortigonClient, orgMgmtClient, bouncerClient}; } @AfterEach @@ -56,34 +68,46 @@ class SearchChargebacksTest extends AbstractKeycloakOpenIdAsWiremockConfig { @Test @SneakyThrows void searchChargebacksRequiredParamsRequestSuccess() { + when(vortigonClient.getShopsIds(any(), any())).thenReturn(List.of("1", "2", "3")); + when(orgMgmtClient.getUserContext(any())).thenReturn(createContextFragment()); + when(bouncerClient.judge(any(), any())).thenReturn(createJudgementAllowed()); when(magistaClient.searchChargebacks(any())).thenReturn(MagistaUtil.createSearchChargebackRequiredResponse()); mvc.perform(get("/chargebacks") - .header("Authorization", "Bearer " + generateInvoicesReadJwt()) - .header("X-Request-ID", randomUUID()) - .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) - .params(OpenApiUtil.getSearchRequiredParams()) - .contentType(MediaType.APPLICATION_JSON_UTF8) - .content("")) + .header("Authorization", "Bearer " + generateInvoicesReadJwt()) + .header("X-Request-ID", randomUUID()) + .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) + .params(OpenApiUtil.getSearchRequiredParams()) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content("")) .andDo(print()) .andExpect(status().is2xxSuccessful()) .andExpect(jsonPath("$").exists()); + verify(vortigonClient, times(1)).getShopsIds(any(), any()); + verify(orgMgmtClient, times(1)).getUserContext(any()); + verify(bouncerClient, times(1)).judge(any(), any()); verify(magistaClient, times(1)).searchChargebacks(any()); } @Test @SneakyThrows void searchChargebacksAllParamsRequestSuccess() { + when(vortigonClient.getShopsIds(any(), any())).thenReturn(List.of("1", "2", "3")); + when(orgMgmtClient.getUserContext(any())).thenReturn(createContextFragment()); + when(bouncerClient.judge(any(), any())).thenReturn(createJudgementAllowed()); when(magistaClient.searchChargebacks(any())).thenReturn(MagistaUtil.createSearchChargebackAllResponse()); mvc.perform(get("/chargebacks") - .header("Authorization", "Bearer " + generateInvoicesReadJwt()) - .header("X-Request-ID", randomUUID()) - .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) - .params(OpenApiUtil.getSearchChargebackAllParams()) - .contentType(MediaType.APPLICATION_JSON_UTF8) - .content("")) + .header("Authorization", "Bearer " + generateInvoicesReadJwt()) + .header("X-Request-ID", randomUUID()) + .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) + .params(OpenApiUtil.getSearchChargebackAllParams()) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content("")) .andDo(print()) .andExpect(status().is2xxSuccessful()) .andExpect(jsonPath("$").exists()); + verify(vortigonClient, times(1)).getShopsIds(any(), any()); + verify(orgMgmtClient, times(1)).getUserContext(any()); + verify(bouncerClient, times(1)).judge(any(), any()); verify(magistaClient, times(1)).searchChargebacks(any()); } @@ -108,16 +132,22 @@ class SearchChargebacksTest extends AbstractKeycloakOpenIdAsWiremockConfig { @Test @SneakyThrows void searchChargebacksRequestMagistaUnavailable() { + when(vortigonClient.getShopsIds(any(), any())).thenReturn(List.of("1", "2", "3")); + when(orgMgmtClient.getUserContext(any())).thenReturn(createContextFragment()); + when(bouncerClient.judge(any(), any())).thenReturn(createJudgementAllowed()); when(magistaClient.searchPayments(any())).thenThrow(TException.class); mvc.perform(get("/chargebacks") - .header("Authorization", "Bearer " + generateInvoicesReadJwt()) - .header("X-Request-ID", randomUUID()) - .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) - .params(OpenApiUtil.getSearchRequiredParams()) - .contentType(MediaType.APPLICATION_JSON_UTF8) - .content("")) + .header("Authorization", "Bearer " + generateInvoicesReadJwt()) + .header("X-Request-ID", randomUUID()) + .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) + .params(OpenApiUtil.getSearchRequiredParams()) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content("")) .andDo(print()) .andExpect(status().is5xxServerError()); + verify(vortigonClient, times(1)).getShopsIds(any(), any()); + verify(orgMgmtClient, times(1)).getUserContext(any()); + verify(bouncerClient, times(1)).judge(any(), any()); verify(magistaClient, times(1)).searchChargebacks(any()); } } diff --git a/src/test/java/com/rbkmoney/anapi/v2/SearchInvoicesTest.java b/src/test/java/com/rbkmoney/anapi/v2/SearchInvoicesTest.java index 25a143d..206da06 100644 --- a/src/test/java/com/rbkmoney/anapi/v2/SearchInvoicesTest.java +++ b/src/test/java/com/rbkmoney/anapi/v2/SearchInvoicesTest.java @@ -3,8 +3,11 @@ package com.rbkmoney.anapi.v2; import com.rbkmoney.anapi.v2.config.AbstractKeycloakOpenIdAsWiremockConfig; import com.rbkmoney.anapi.v2.testutil.MagistaUtil; import com.rbkmoney.anapi.v2.testutil.OpenApiUtil; +import com.rbkmoney.bouncer.decisions.ArbiterSrv; +import com.rbkmoney.damsel.vortigon.VortigonServiceSrv; import com.rbkmoney.magista.MerchantStatisticsServiceSrv; import com.rbkmoney.openapi.anapi_v2.model.DefaultLogicError; +import com.rbkmoney.orgmanagement.AuthContextProviderSrv; import lombok.SneakyThrows; import org.apache.thrift.TException; import org.junit.jupiter.api.AfterEach; @@ -19,7 +22,10 @@ import org.springframework.util.MultiValueMap; import java.time.Instant; import java.time.temporal.ChronoUnit; +import java.util.List; +import static com.rbkmoney.anapi.v2.testutil.MagistaUtil.createContextFragment; +import static com.rbkmoney.anapi.v2.testutil.MagistaUtil.createJudgementAllowed; import static java.util.UUID.randomUUID; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; @@ -32,6 +38,12 @@ class SearchInvoicesTest extends AbstractKeycloakOpenIdAsWiremockConfig { @MockBean public MerchantStatisticsServiceSrv.Iface magistaClient; + @MockBean + public VortigonServiceSrv.Iface vortigonClient; + @MockBean + public AuthContextProviderSrv.Iface orgMgmtClient; + @MockBean + public ArbiterSrv.Iface bouncerClient; @Autowired private MockMvc mvc; @@ -43,7 +55,7 @@ class SearchInvoicesTest extends AbstractKeycloakOpenIdAsWiremockConfig { @BeforeEach public void init() { mocks = MockitoAnnotations.openMocks(this); - preparedMocks = new Object[] {magistaClient}; + preparedMocks = new Object[] {magistaClient, vortigonClient, orgMgmtClient, bouncerClient}; } @AfterEach @@ -55,6 +67,9 @@ class SearchInvoicesTest extends AbstractKeycloakOpenIdAsWiremockConfig { @Test @SneakyThrows void searchInvoicesRequiredParamsRequestSuccess() { + when(vortigonClient.getShopsIds(any(), any())).thenReturn(List.of("1", "2", "3")); + when(orgMgmtClient.getUserContext(any())).thenReturn(createContextFragment()); + when(bouncerClient.judge(any(), any())).thenReturn(createJudgementAllowed()); when(magistaClient.searchInvoices(any())).thenReturn(MagistaUtil.createSearchInvoiceRequiredResponse()); mvc.perform(get("/invoices") .header("Authorization", "Bearer " + generateInvoicesReadJwt()) @@ -66,12 +81,18 @@ class SearchInvoicesTest extends AbstractKeycloakOpenIdAsWiremockConfig { .andDo(print()) .andExpect(status().is2xxSuccessful()) .andExpect(jsonPath("$").exists()); + verify(vortigonClient, times(1)).getShopsIds(any(), any()); + verify(orgMgmtClient, times(1)).getUserContext(any()); + verify(bouncerClient, times(1)).judge(any(), any()); verify(magistaClient, times(1)).searchInvoices(any()); } @Test @SneakyThrows void searchInvoicesAllParamsRequestSuccess() { + when(vortigonClient.getShopsIds(any(), any())).thenReturn(List.of("1", "2", "3")); + when(orgMgmtClient.getUserContext(any())).thenReturn(createContextFragment()); + when(bouncerClient.judge(any(), any())).thenReturn(createJudgementAllowed()); when(magistaClient.searchInvoices(any())).thenReturn(MagistaUtil.createSearchInvoiceAllResponse()); mvc.perform(get("/invoices") .header("Authorization", "Bearer " + generateInvoicesReadJwt()) @@ -83,6 +104,9 @@ class SearchInvoicesTest extends AbstractKeycloakOpenIdAsWiremockConfig { .andDo(print()) .andExpect(status().is2xxSuccessful()) .andExpect(jsonPath("$").exists()); + verify(vortigonClient, times(1)).getShopsIds(any(), any()); + verify(orgMgmtClient, times(1)).getUserContext(any()); + verify(bouncerClient, times(1)).judge(any(), any()); verify(magistaClient, times(1)).searchInvoices(any()); } @@ -107,6 +131,9 @@ class SearchInvoicesTest extends AbstractKeycloakOpenIdAsWiremockConfig { @Test @SneakyThrows void searchInvoicesRequestMagistaUnavailable() { + when(vortigonClient.getShopsIds(any(), any())).thenReturn(List.of("1", "2", "3")); + when(orgMgmtClient.getUserContext(any())).thenReturn(createContextFragment()); + when(bouncerClient.judge(any(), any())).thenReturn(createJudgementAllowed()); when(magistaClient.searchInvoices(any())).thenThrow(TException.class); mvc.perform(get("/invoices") .header("Authorization", "Bearer " + generateInvoicesReadJwt()) @@ -117,6 +144,9 @@ class SearchInvoicesTest extends AbstractKeycloakOpenIdAsWiremockConfig { .content("")) .andDo(print()) .andExpect(status().is5xxServerError()); + verify(vortigonClient, times(1)).getShopsIds(any(), any()); + verify(orgMgmtClient, times(1)).getUserContext(any()); + verify(bouncerClient, times(1)).judge(any(), any()); verify(magistaClient, times(1)).searchInvoices(any()); } } diff --git a/src/test/java/com/rbkmoney/anapi/v2/SearchPaymentsTest.java b/src/test/java/com/rbkmoney/anapi/v2/SearchPaymentsTest.java index df76a09..e96a3a4 100644 --- a/src/test/java/com/rbkmoney/anapi/v2/SearchPaymentsTest.java +++ b/src/test/java/com/rbkmoney/anapi/v2/SearchPaymentsTest.java @@ -3,8 +3,11 @@ package com.rbkmoney.anapi.v2; import com.rbkmoney.anapi.v2.config.AbstractKeycloakOpenIdAsWiremockConfig; import com.rbkmoney.anapi.v2.testutil.MagistaUtil; import com.rbkmoney.anapi.v2.testutil.OpenApiUtil; +import com.rbkmoney.bouncer.decisions.ArbiterSrv; +import com.rbkmoney.damsel.vortigon.VortigonServiceSrv; import com.rbkmoney.magista.MerchantStatisticsServiceSrv; import com.rbkmoney.openapi.anapi_v2.model.DefaultLogicError; +import com.rbkmoney.orgmanagement.AuthContextProviderSrv; import lombok.SneakyThrows; import org.apache.thrift.TException; import org.junit.jupiter.api.AfterEach; @@ -19,7 +22,10 @@ import org.springframework.util.MultiValueMap; import java.time.Instant; import java.time.temporal.ChronoUnit; +import java.util.List; +import static com.rbkmoney.anapi.v2.testutil.MagistaUtil.createContextFragment; +import static com.rbkmoney.anapi.v2.testutil.MagistaUtil.createJudgementAllowed; import static java.util.UUID.randomUUID; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; @@ -33,6 +39,12 @@ class SearchPaymentsTest extends AbstractKeycloakOpenIdAsWiremockConfig { @MockBean public MerchantStatisticsServiceSrv.Iface magistaClient; + @MockBean + public VortigonServiceSrv.Iface vortigonClient; + @MockBean + public AuthContextProviderSrv.Iface orgMgmtClient; + @MockBean + public ArbiterSrv.Iface bouncerClient; @Autowired private MockMvc mvc; @@ -44,7 +56,7 @@ class SearchPaymentsTest extends AbstractKeycloakOpenIdAsWiremockConfig { @BeforeEach public void init() { mocks = MockitoAnnotations.openMocks(this); - preparedMocks = new Object[] {magistaClient}; + preparedMocks = new Object[] {magistaClient, vortigonClient, orgMgmtClient, bouncerClient}; } @AfterEach @@ -56,6 +68,9 @@ class SearchPaymentsTest extends AbstractKeycloakOpenIdAsWiremockConfig { @Test @SneakyThrows void searchPaymentsRequiredParamsRequestSuccess() { + when(vortigonClient.getShopsIds(any(), any())).thenReturn(List.of("1", "2", "3")); + when(orgMgmtClient.getUserContext(any())).thenReturn(createContextFragment()); + when(bouncerClient.judge(any(), any())).thenReturn(createJudgementAllowed()); when(magistaClient.searchPayments(any())).thenReturn(MagistaUtil.createSearchPaymentRequiredResponse()); mvc.perform(get("/payments") .header("Authorization", "Bearer " + generateInvoicesReadJwt()) @@ -67,12 +82,18 @@ class SearchPaymentsTest extends AbstractKeycloakOpenIdAsWiremockConfig { .andDo(print()) .andExpect(status().is2xxSuccessful()) .andExpect(jsonPath("$").exists()); + verify(vortigonClient, times(1)).getShopsIds(any(), any()); + verify(orgMgmtClient, times(1)).getUserContext(any()); + verify(bouncerClient, times(1)).judge(any(), any()); verify(magistaClient, times(1)).searchPayments(any()); } @Test @SneakyThrows void searchPaymentsAllParamsRequestSuccess() { + when(vortigonClient.getShopsIds(any(), any())).thenReturn(List.of("1", "2", "3")); + when(orgMgmtClient.getUserContext(any())).thenReturn(createContextFragment()); + when(bouncerClient.judge(any(), any())).thenReturn(createJudgementAllowed()); when(magistaClient.searchPayments(any())).thenReturn(MagistaUtil.createSearchPaymentAllResponse()); mvc.perform(get("/payments") .header("Authorization", "Bearer " + generateInvoicesReadJwt()) @@ -84,6 +105,9 @@ class SearchPaymentsTest extends AbstractKeycloakOpenIdAsWiremockConfig { .andDo(print()) .andExpect(status().is2xxSuccessful()) .andExpect(jsonPath("$").exists()); + verify(vortigonClient, times(1)).getShopsIds(any(), any()); + verify(orgMgmtClient, times(1)).getUserContext(any()); + verify(bouncerClient, times(1)).judge(any(), any()); verify(magistaClient, times(1)).searchPayments(any()); } @@ -108,6 +132,9 @@ class SearchPaymentsTest extends AbstractKeycloakOpenIdAsWiremockConfig { @Test @SneakyThrows void searchPaymentsRequestMagistaUnavailable() { + when(vortigonClient.getShopsIds(any(), any())).thenReturn(List.of("1", "2", "3")); + when(orgMgmtClient.getUserContext(any())).thenReturn(createContextFragment()); + when(bouncerClient.judge(any(), any())).thenReturn(createJudgementAllowed()); when(magistaClient.searchPayments(any())).thenThrow(TException.class); mvc.perform(get("/payments") .header("Authorization", "Bearer " + generateInvoicesReadJwt()) @@ -118,6 +145,9 @@ class SearchPaymentsTest extends AbstractKeycloakOpenIdAsWiremockConfig { .content("")) .andDo(print()) .andExpect(status().is5xxServerError()); + verify(vortigonClient, times(1)).getShopsIds(any(), any()); + verify(orgMgmtClient, times(1)).getUserContext(any()); + verify(bouncerClient, times(1)).judge(any(), any()); verify(magistaClient, times(1)).searchPayments(any()); } } diff --git a/src/test/java/com/rbkmoney/anapi/v2/SearchPayoutsTest.java b/src/test/java/com/rbkmoney/anapi/v2/SearchPayoutsTest.java index 12d0f07..02e35d9 100644 --- a/src/test/java/com/rbkmoney/anapi/v2/SearchPayoutsTest.java +++ b/src/test/java/com/rbkmoney/anapi/v2/SearchPayoutsTest.java @@ -3,8 +3,11 @@ package com.rbkmoney.anapi.v2; import com.rbkmoney.anapi.v2.config.AbstractKeycloakOpenIdAsWiremockConfig; import com.rbkmoney.anapi.v2.testutil.MagistaUtil; import com.rbkmoney.anapi.v2.testutil.OpenApiUtil; +import com.rbkmoney.bouncer.decisions.ArbiterSrv; +import com.rbkmoney.damsel.vortigon.VortigonServiceSrv; import com.rbkmoney.magista.MerchantStatisticsServiceSrv; import com.rbkmoney.openapi.anapi_v2.model.DefaultLogicError; +import com.rbkmoney.orgmanagement.AuthContextProviderSrv; import lombok.SneakyThrows; import org.apache.thrift.TException; import org.junit.jupiter.api.AfterEach; @@ -19,7 +22,10 @@ import org.springframework.util.MultiValueMap; import java.time.Instant; import java.time.temporal.ChronoUnit; +import java.util.List; +import static com.rbkmoney.anapi.v2.testutil.MagistaUtil.createContextFragment; +import static com.rbkmoney.anapi.v2.testutil.MagistaUtil.createJudgementAllowed; import static java.util.UUID.randomUUID; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; @@ -32,6 +38,12 @@ class SearchPayoutsTest extends AbstractKeycloakOpenIdAsWiremockConfig { @MockBean public MerchantStatisticsServiceSrv.Iface magistaClient; + @MockBean + public VortigonServiceSrv.Iface vortigonClient; + @MockBean + public AuthContextProviderSrv.Iface orgMgmtClient; + @MockBean + public ArbiterSrv.Iface bouncerClient; @Autowired private MockMvc mvc; @@ -43,7 +55,7 @@ class SearchPayoutsTest extends AbstractKeycloakOpenIdAsWiremockConfig { @BeforeEach public void init() { mocks = MockitoAnnotations.openMocks(this); - preparedMocks = new Object[] {magistaClient}; + preparedMocks = new Object[] {magistaClient, vortigonClient, orgMgmtClient, bouncerClient}; } @AfterEach @@ -55,6 +67,9 @@ class SearchPayoutsTest extends AbstractKeycloakOpenIdAsWiremockConfig { @Test @SneakyThrows void searchPayoutsRequiredParamsRequestSuccess() { + when(vortigonClient.getShopsIds(any(), any())).thenReturn(List.of("1", "2", "3")); + when(orgMgmtClient.getUserContext(any())).thenReturn(createContextFragment()); + when(bouncerClient.judge(any(), any())).thenReturn(createJudgementAllowed()); when(magistaClient.searchPayouts(any())).thenReturn(MagistaUtil.createSearchPayoutRequiredResponse()); mvc.perform(get("/payouts") .header("Authorization", "Bearer " + generateInvoicesReadJwt()) @@ -66,12 +81,18 @@ class SearchPayoutsTest extends AbstractKeycloakOpenIdAsWiremockConfig { .andDo(print()) .andExpect(status().is2xxSuccessful()) .andExpect(jsonPath("$").exists()); + verify(vortigonClient, times(1)).getShopsIds(any(), any()); + verify(orgMgmtClient, times(1)).getUserContext(any()); + verify(bouncerClient, times(1)).judge(any(), any()); verify(magistaClient, times(1)).searchPayouts(any()); } @Test @SneakyThrows void searchPayoutsAllParamsRequestSuccess() { + when(vortigonClient.getShopsIds(any(), any())).thenReturn(List.of("1", "2", "3")); + when(orgMgmtClient.getUserContext(any())).thenReturn(createContextFragment()); + when(bouncerClient.judge(any(), any())).thenReturn(createJudgementAllowed()); when(magistaClient.searchPayouts(any())).thenReturn(MagistaUtil.createSearchPayoutAllResponse()); mvc.perform(get("/payouts") .header("Authorization", "Bearer " + generateInvoicesReadJwt()) @@ -83,6 +104,9 @@ class SearchPayoutsTest extends AbstractKeycloakOpenIdAsWiremockConfig { .andDo(print()) .andExpect(status().is2xxSuccessful()) .andExpect(jsonPath("$").exists()); + verify(vortigonClient, times(1)).getShopsIds(any(), any()); + verify(orgMgmtClient, times(1)).getUserContext(any()); + verify(bouncerClient, times(1)).judge(any(), any()); verify(magistaClient, times(1)).searchPayouts(any()); } @@ -107,6 +131,9 @@ class SearchPayoutsTest extends AbstractKeycloakOpenIdAsWiremockConfig { @Test @SneakyThrows void searchPayoutsRequestMagistaUnavailable() { + when(vortigonClient.getShopsIds(any(), any())).thenReturn(List.of("1", "2", "3")); + when(orgMgmtClient.getUserContext(any())).thenReturn(createContextFragment()); + when(bouncerClient.judge(any(), any())).thenReturn(createJudgementAllowed()); when(magistaClient.searchPayouts(any())).thenThrow(TException.class); mvc.perform(get("/payouts") .header("Authorization", "Bearer " + generateInvoicesReadJwt()) @@ -117,6 +144,9 @@ class SearchPayoutsTest extends AbstractKeycloakOpenIdAsWiremockConfig { .content("")) .andDo(print()) .andExpect(status().is5xxServerError()); + verify(vortigonClient, times(1)).getShopsIds(any(), any()); + verify(orgMgmtClient, times(1)).getUserContext(any()); + verify(bouncerClient, times(1)).judge(any(), any()); verify(magistaClient, times(1)).searchPayouts(any()); } } diff --git a/src/test/java/com/rbkmoney/anapi/v2/SearchRefundsTest.java b/src/test/java/com/rbkmoney/anapi/v2/SearchRefundsTest.java index 6356935..4e82332 100644 --- a/src/test/java/com/rbkmoney/anapi/v2/SearchRefundsTest.java +++ b/src/test/java/com/rbkmoney/anapi/v2/SearchRefundsTest.java @@ -3,8 +3,11 @@ package com.rbkmoney.anapi.v2; import com.rbkmoney.anapi.v2.config.AbstractKeycloakOpenIdAsWiremockConfig; import com.rbkmoney.anapi.v2.testutil.MagistaUtil; import com.rbkmoney.anapi.v2.testutil.OpenApiUtil; +import com.rbkmoney.bouncer.decisions.ArbiterSrv; +import com.rbkmoney.damsel.vortigon.VortigonServiceSrv; import com.rbkmoney.magista.MerchantStatisticsServiceSrv; import com.rbkmoney.openapi.anapi_v2.model.DefaultLogicError; +import com.rbkmoney.orgmanagement.AuthContextProviderSrv; import lombok.SneakyThrows; import org.apache.thrift.TException; import org.junit.jupiter.api.AfterEach; @@ -19,7 +22,10 @@ import org.springframework.util.MultiValueMap; import java.time.Instant; import java.time.temporal.ChronoUnit; +import java.util.List; +import static com.rbkmoney.anapi.v2.testutil.MagistaUtil.createContextFragment; +import static com.rbkmoney.anapi.v2.testutil.MagistaUtil.createJudgementAllowed; import static java.util.UUID.randomUUID; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; @@ -32,6 +38,12 @@ class SearchRefundsTest extends AbstractKeycloakOpenIdAsWiremockConfig { @MockBean public MerchantStatisticsServiceSrv.Iface magistaClient; + @MockBean + public VortigonServiceSrv.Iface vortigonClient; + @MockBean + public AuthContextProviderSrv.Iface orgMgmtClient; + @MockBean + public ArbiterSrv.Iface bouncerClient; @Autowired private MockMvc mvc; @@ -43,7 +55,7 @@ class SearchRefundsTest extends AbstractKeycloakOpenIdAsWiremockConfig { @BeforeEach public void init() { mocks = MockitoAnnotations.openMocks(this); - preparedMocks = new Object[] {magistaClient}; + preparedMocks = new Object[] {magistaClient, vortigonClient, orgMgmtClient, bouncerClient}; } @AfterEach @@ -55,6 +67,9 @@ class SearchRefundsTest extends AbstractKeycloakOpenIdAsWiremockConfig { @Test @SneakyThrows void searchRefundsRequiredParamsRequestSuccess() { + when(vortigonClient.getShopsIds(any(), any())).thenReturn(List.of("1", "2", "3")); + when(orgMgmtClient.getUserContext(any())).thenReturn(createContextFragment()); + when(bouncerClient.judge(any(), any())).thenReturn(createJudgementAllowed()); when(magistaClient.searchRefunds(any())).thenReturn(MagistaUtil.createSearchRefundRequiredResponse()); mvc.perform(get("/refunds") .header("Authorization", "Bearer " + generateInvoicesReadJwt()) @@ -66,13 +81,19 @@ class SearchRefundsTest extends AbstractKeycloakOpenIdAsWiremockConfig { .andDo(print()) .andExpect(status().is2xxSuccessful()) .andExpect(jsonPath("$").exists()); + verify(vortigonClient, times(1)).getShopsIds(any(), any()); + verify(orgMgmtClient, times(1)).getUserContext(any()); + verify(bouncerClient, times(1)).judge(any(), any()); verify(magistaClient, times(1)).searchRefunds(any()); } @Test @SneakyThrows void searchRefundsAllParamsRequestSuccess() { - when(magistaClient.searchRefunds(any())).thenReturn(MagistaUtil.createsearchRefundAllResponse()); + when(vortigonClient.getShopsIds(any(), any())).thenReturn(List.of("1", "2", "3")); + when(orgMgmtClient.getUserContext(any())).thenReturn(createContextFragment()); + when(bouncerClient.judge(any(), any())).thenReturn(createJudgementAllowed()); + when(magistaClient.searchRefunds(any())).thenReturn(MagistaUtil.createSearchRefundAllResponse()); mvc.perform(get("/refunds") .header("Authorization", "Bearer " + generateInvoicesReadJwt()) .header("X-Request-ID", randomUUID()) @@ -83,6 +104,9 @@ class SearchRefundsTest extends AbstractKeycloakOpenIdAsWiremockConfig { .andDo(print()) .andExpect(status().is2xxSuccessful()) .andExpect(jsonPath("$").exists()); + verify(vortigonClient, times(1)).getShopsIds(any(), any()); + verify(orgMgmtClient, times(1)).getUserContext(any()); + verify(bouncerClient, times(1)).judge(any(), any()); verify(magistaClient, times(1)).searchRefunds(any()); } @@ -107,6 +131,9 @@ class SearchRefundsTest extends AbstractKeycloakOpenIdAsWiremockConfig { @Test @SneakyThrows void searchRefundsRequestMagistaUnavailable() { + when(vortigonClient.getShopsIds(any(), any())).thenReturn(List.of("1", "2", "3")); + when(orgMgmtClient.getUserContext(any())).thenReturn(createContextFragment()); + when(bouncerClient.judge(any(), any())).thenReturn(createJudgementAllowed()); when(magistaClient.searchRefunds(any())).thenThrow(TException.class); mvc.perform(get("/refunds") .header("Authorization", "Bearer " + generateInvoicesReadJwt()) @@ -117,6 +144,9 @@ class SearchRefundsTest extends AbstractKeycloakOpenIdAsWiremockConfig { .content("")) .andDo(print()) .andExpect(status().is5xxServerError()); + verify(vortigonClient, times(1)).getShopsIds(any(), any()); + verify(orgMgmtClient, times(1)).getUserContext(any()); + verify(bouncerClient, times(1)).judge(any(), any()); verify(magistaClient, times(1)).searchRefunds(any()); } } diff --git a/src/test/java/com/rbkmoney/anapi/v2/controller/ErrorControllerTest.java b/src/test/java/com/rbkmoney/anapi/v2/controller/ErrorControllerTest.java index 97f23ba..918c78c 100644 --- a/src/test/java/com/rbkmoney/anapi/v2/controller/ErrorControllerTest.java +++ b/src/test/java/com/rbkmoney/anapi/v2/controller/ErrorControllerTest.java @@ -4,8 +4,14 @@ import com.rbkmoney.anapi.v2.config.AbstractKeycloakOpenIdAsWiremockConfig; import com.rbkmoney.anapi.v2.converter.search.request.ParamsToRefundSearchQueryConverter; import com.rbkmoney.anapi.v2.exception.BadRequestException; import com.rbkmoney.anapi.v2.testutil.OpenApiUtil; +import com.rbkmoney.bouncer.decisions.ArbiterSrv; +import com.rbkmoney.damsel.vortigon.VortigonServiceSrv; import com.rbkmoney.openapi.anapi_v2.model.DefaultLogicError; +import com.rbkmoney.orgmanagement.AuthContextProviderSrv; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.MockitoAnnotations; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.http.MediaType; @@ -14,7 +20,10 @@ import org.springframework.util.MultiValueMap; import java.time.Instant; import java.time.temporal.ChronoUnit; +import java.util.List; +import static com.rbkmoney.anapi.v2.testutil.MagistaUtil.createContextFragment; +import static com.rbkmoney.anapi.v2.testutil.MagistaUtil.createJudgementAllowed; import static java.util.UUID.randomUUID; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; @@ -29,6 +38,28 @@ class ErrorControllerTest extends AbstractKeycloakOpenIdAsWiremockConfig { private MockMvc mockMvc; @MockBean private ParamsToRefundSearchQueryConverter refundSearchConverter; + @MockBean + public VortigonServiceSrv.Iface vortigonClient; + @MockBean + public AuthContextProviderSrv.Iface orgMgmtClient; + @MockBean + public ArbiterSrv.Iface bouncerClient; + + private AutoCloseable mocks; + + private Object[] preparedMocks; + + @BeforeEach + public void init() { + mocks = MockitoAnnotations.openMocks(this); + preparedMocks = new Object[] {refundSearchConverter, vortigonClient, orgMgmtClient, bouncerClient}; + } + + @AfterEach + public void clean() throws Exception { + verifyNoMoreInteractions(preparedMocks); + mocks.close(); + } @Test void testConstraintViolationException() throws Exception { @@ -36,9 +67,9 @@ class ErrorControllerTest extends AbstractKeycloakOpenIdAsWiremockConfig { params.set("limit", "1001"); mockMvc.perform( - get("/payments") - .header("Authorization", "Bearer " + generateInvoicesReadJwt()) - .header("X-Request-ID", randomUUID()) + get("/payments") + .header("Authorization", "Bearer " + generateInvoicesReadJwt()) + .header("X-Request-ID", randomUUID()) .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) .params(params) .contentType(MediaType.APPLICATION_JSON_UTF8) @@ -51,6 +82,9 @@ class ErrorControllerTest extends AbstractKeycloakOpenIdAsWiremockConfig { @Test void testBadRequestException() throws Exception { + when(vortigonClient.getShopsIds(any(), any())).thenReturn(List.of("1", "2", "3")); + when(orgMgmtClient.getUserContext(any())).thenReturn(createContextFragment()); + when(bouncerClient.judge(any(), any())).thenReturn(createJudgementAllowed()); String message = "Error!"; doThrow(new BadRequestException(message)).when(refundSearchConverter) .convert(any(), any(), any(), any(), @@ -60,18 +94,20 @@ class ErrorControllerTest extends AbstractKeycloakOpenIdAsWiremockConfig { MultiValueMap params = OpenApiUtil.getSearchRequiredParams(); mockMvc.perform( - get("/refunds") - .header("Authorization", "Bearer " + generateInvoicesReadJwt()) - .header("X-Request-ID", randomUUID()) - .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) - .params(params) - .contentType(MediaType.APPLICATION_JSON_UTF8) - .content("")) + get("/refunds") + .header("Authorization", "Bearer " + generateInvoicesReadJwt()) + .header("X-Request-ID", randomUUID()) + .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) + .params(params) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content("")) .andDo(print()) .andExpect(status().isBadRequest()) .andExpect(jsonPath("$.code").value(DefaultLogicError.CodeEnum.INVALIDREQUEST.getValue())) .andExpect(jsonPath("$.message").value(message)); - + verify(vortigonClient, times(1)).getShopsIds(any(), any()); + verify(orgMgmtClient, times(1)).getUserContext(any()); + verify(bouncerClient, times(1)).judge(any(), any()); verify(refundSearchConverter, times(1)) .convert(any(), any(), any(), any(), any(), any(), any(), any(), @@ -116,6 +152,9 @@ class ErrorControllerTest extends AbstractKeycloakOpenIdAsWiremockConfig { @Test void testInternalException() throws Exception { + when(vortigonClient.getShopsIds(any(), any())).thenReturn(List.of("1", "2", "3")); + when(orgMgmtClient.getUserContext(any())).thenReturn(createContextFragment()); + when(bouncerClient.judge(any(), any())).thenReturn(createJudgementAllowed()); doThrow(new RuntimeException()).when(refundSearchConverter) .convert(any(), any(), any(), any(), any(), any(), any(), any(), @@ -124,16 +163,19 @@ class ErrorControllerTest extends AbstractKeycloakOpenIdAsWiremockConfig { MultiValueMap params = OpenApiUtil.getSearchRequiredParams(); mockMvc.perform( - get("/refunds") - .header("Authorization", "Bearer " + generateInvoicesReadJwt()) - .header("X-Request-ID", randomUUID()) - .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) - .params(params) - .contentType(MediaType.APPLICATION_JSON_UTF8) - .content("")) + get("/refunds") + .header("Authorization", "Bearer " + generateInvoicesReadJwt()) + .header("X-Request-ID", randomUUID()) + .header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString()) + .params(params) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content("")) .andDo(print()) .andExpect(status().isInternalServerError()) .andExpect(jsonPath("$").doesNotExist()); + verify(vortigonClient, times(1)).getShopsIds(any(), any()); + verify(orgMgmtClient, times(1)).getUserContext(any()); + verify(bouncerClient, times(1)).judge(any(), any()); verify(refundSearchConverter, times(1)) .convert(any(), any(), any(), any(), any(), any(), any(), any(), diff --git a/src/test/java/com/rbkmoney/anapi/v2/testutil/MagistaUtil.java b/src/test/java/com/rbkmoney/anapi/v2/testutil/MagistaUtil.java index 519a173..fcc9481 100644 --- a/src/test/java/com/rbkmoney/anapi/v2/testutil/MagistaUtil.java +++ b/src/test/java/com/rbkmoney/anapi/v2/testutil/MagistaUtil.java @@ -1,5 +1,9 @@ package com.rbkmoney.anapi.v2.testutil; +import com.rbkmoney.bouncer.ctx.ContextFragment; +import com.rbkmoney.bouncer.decisions.Judgement; +import com.rbkmoney.bouncer.decisions.Resolution; +import com.rbkmoney.bouncer.decisions.ResolutionAllowed; import com.rbkmoney.damsel.domain.InvoicePaymentRefundStatus; import com.rbkmoney.damsel.domain.InvoiceStatus; import com.rbkmoney.damsel.domain.*; @@ -24,8 +28,7 @@ import java.util.Map; public class MagistaUtil { private static final MockTBaseProcessor mockRequiredTBaseProcessor; - private static final MockTBaseProcessor mockFullTBaseProcessor; - + static { mockRequiredTBaseProcessor = new MockTBaseProcessor(MockMode.REQUIRED_ONLY, 15, 1); Map.Entry timeFields = Map.entry( @@ -33,9 +36,6 @@ public class MagistaUtil { new String[] {"created_at", "at", "due", "status_changed_at"} ); mockRequiredTBaseProcessor.addFieldHandler(timeFields.getKey(), timeFields.getValue()); - - mockFullTBaseProcessor = new MockTBaseProcessor(MockMode.ALL, 15, 1); - mockFullTBaseProcessor.addFieldHandler(timeFields.getKey(), timeFields.getValue()); } public static StatPaymentResponse createSearchPaymentRequiredResponse() { @@ -47,12 +47,12 @@ public class MagistaUtil { } public static StatPaymentResponse createSearchPaymentAllResponse() { - var payment = fillAllTBaseObject(new StatPayment(), StatPayment.class); - var cart = fillAllTBaseObject(new InvoiceCart(), InvoiceCart.class); - var line = fillAllTBaseObject(new InvoiceLine(), InvoiceLine.class); - var instant = fillAllTBaseObject(new InvoicePaymentFlowInstant(), InvoicePaymentFlowInstant.class); - var locationInfo = fillAllTBaseObject(new LocationInfo(), LocationInfo.class); - var response = fillAllTBaseObject(new StatPaymentResponse(), StatPaymentResponse.class); + var payment = fillRequiredTBaseObject(new StatPayment(), StatPayment.class); + var cart = fillRequiredTBaseObject(new InvoiceCart(), InvoiceCart.class); + var line = fillRequiredTBaseObject(new InvoiceLine(), InvoiceLine.class); + var instant = fillRequiredTBaseObject(new InvoicePaymentFlowInstant(), InvoicePaymentFlowInstant.class); + var locationInfo = fillRequiredTBaseObject(new LocationInfo(), LocationInfo.class); + var response = fillRequiredTBaseObject(new StatPaymentResponse(), StatPaymentResponse.class); return response.setPayments( List.of(payment @@ -65,11 +65,13 @@ public class MagistaUtil { } public static StatChargebackResponse createSearchChargebackAllResponse() { - var chargeback = fillAllTBaseObject(new StatChargeback(), StatChargeback.class); - var stage = fillAllTBaseObject(new InvoicePaymentChargebackStage(), InvoicePaymentChargebackStage.class); - var reason = fillAllTBaseObject(new InvoicePaymentChargebackReason(), InvoicePaymentChargebackReason.class); - var status = fillAllTBaseObject(new InvoicePaymentChargebackStatus(), InvoicePaymentChargebackStatus.class); - var response = fillAllTBaseObject(new StatChargebackResponse(), StatChargebackResponse.class); + var chargeback = fillRequiredTBaseObject(new StatChargeback(), StatChargeback.class); + var stage = fillRequiredTBaseObject(new InvoicePaymentChargebackStage(), InvoicePaymentChargebackStage.class); + var reason = + fillRequiredTBaseObject(new InvoicePaymentChargebackReason(), InvoicePaymentChargebackReason.class); + var status = + fillRequiredTBaseObject(new InvoicePaymentChargebackStatus(), InvoicePaymentChargebackStatus.class); + var response = fillRequiredTBaseObject(new StatChargebackResponse(), StatChargebackResponse.class); return response.setChargebacks( List.of(chargeback @@ -83,13 +85,13 @@ public class MagistaUtil { return fillRequiredTBaseObject(new StatRefundResponse(), StatRefundResponse.class); } - public static StatRefundResponse createsearchRefundAllResponse() { - var refund = fillAllTBaseObject(new StatRefund(), StatRefund.class); - var cart = fillAllTBaseObject(new InvoiceCart(), InvoiceCart.class); - var line = fillAllTBaseObject(new InvoiceLine(), InvoiceLine.class); - var cash = fillAllTBaseObject(new Cash(), Cash.class); - var status = fillAllTBaseObject(new InvoicePaymentRefundStatus(), InvoicePaymentRefundStatus.class); - var response = fillAllTBaseObject(new StatRefundResponse(), StatRefundResponse.class); + public static StatRefundResponse createSearchRefundAllResponse() { + var refund = fillRequiredTBaseObject(new StatRefund(), StatRefund.class); + var cart = fillRequiredTBaseObject(new InvoiceCart(), InvoiceCart.class); + var line = fillRequiredTBaseObject(new InvoiceLine(), InvoiceLine.class); + var cash = fillRequiredTBaseObject(new Cash(), Cash.class); + var status = fillRequiredTBaseObject(new InvoicePaymentRefundStatus(), InvoicePaymentRefundStatus.class); + var response = fillRequiredTBaseObject(new StatRefundResponse(), StatRefundResponse.class); return response.setRefunds( List.of(refund @@ -104,13 +106,13 @@ public class MagistaUtil { } public static StatInvoiceResponse createSearchInvoiceAllResponse() { - var invoice = fillAllTBaseObject(new StatInvoice(), StatInvoice.class); - var cart = fillAllTBaseObject(new InvoiceCart(), InvoiceCart.class); - var line = fillAllTBaseObject(new InvoiceLine(), InvoiceLine.class); - var cash = fillAllTBaseObject(new Cash(), Cash.class); - var status = fillAllTBaseObject(new InvoiceStatus(), + var invoice = fillRequiredTBaseObject(new StatInvoice(), StatInvoice.class); + var cart = fillRequiredTBaseObject(new InvoiceCart(), InvoiceCart.class); + var line = fillRequiredTBaseObject(new InvoiceLine(), InvoiceLine.class); + var cash = fillRequiredTBaseObject(new Cash(), Cash.class); + var status = fillRequiredTBaseObject(new InvoiceStatus(), InvoiceStatus.class); - var response = fillAllTBaseObject(new StatInvoiceResponse(), StatInvoiceResponse.class); + var response = fillRequiredTBaseObject(new StatInvoiceResponse(), StatInvoiceResponse.class); return response.setInvoices( List.of(invoice @@ -125,11 +127,11 @@ public class MagistaUtil { } public static StatPayoutResponse createSearchPayoutAllResponse() { - var payout = fillAllTBaseObject(new StatPayout(), StatPayout.class); - var toolInfo = fillAllTBaseObject(new PayoutToolInfo(), PayoutToolInfo.class); - var bank = fillAllTBaseObject(new RussianBankAccount(), RussianBankAccount.class); - var status = fillAllTBaseObject(new PayoutStatus(), PayoutStatus.class); - var response = fillAllTBaseObject(new StatPayoutResponse(), StatPayoutResponse.class); + var payout = fillRequiredTBaseObject(new StatPayout(), StatPayout.class); + var toolInfo = fillRequiredTBaseObject(new PayoutToolInfo(), PayoutToolInfo.class); + var bank = fillRequiredTBaseObject(new RussianBankAccount(), RussianBankAccount.class); + var status = fillRequiredTBaseObject(new PayoutStatus(), PayoutStatus.class); + var response = fillRequiredTBaseObject(new StatPayoutResponse(), StatPayoutResponse.class); toolInfo.setRussianBankAccount(bank); return response.setPayouts( List.of(payout @@ -138,13 +140,18 @@ public class MagistaUtil { ); } + public static ContextFragment createContextFragment() { + return fillRequiredTBaseObject(new ContextFragment(), ContextFragment.class); + } + + public static Judgement createJudgementAllowed() { + Resolution resolution = new Resolution(); + resolution.setAllowed(new ResolutionAllowed()); + return new Judgement().setResolution(resolution); + } + @SneakyThrows public static T fillRequiredTBaseObject(T tbase, Class type) { return mockRequiredTBaseProcessor.process(tbase, new TBaseHandler<>(type)); } - - @SneakyThrows - public static T fillAllTBaseObject(T tbase, Class type) { - return mockFullTBaseProcessor.process(tbase, new TBaseHandler<>(type)); - } } diff --git a/src/test/java/com/rbkmoney/anapi/v2/util/ConverterUtilTest.java b/src/test/java/com/rbkmoney/anapi/v2/util/ConverterUtilTest.java index 82b5109..8361c7f 100644 --- a/src/test/java/com/rbkmoney/anapi/v2/util/ConverterUtilTest.java +++ b/src/test/java/com/rbkmoney/anapi/v2/util/ConverterUtilTest.java @@ -5,7 +5,7 @@ import org.junit.jupiter.api.Test; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertNotNull; class ConverterUtilTest { @@ -23,6 +23,6 @@ class ConverterUtilTest { assertEquals(List.of("1"), result); result = ConverterUtil.merge(null, null); - assertNull(result); + assertNotNull(result); } } From cdd015fe754c31bef409f55cfb1230bc852cf8e9 Mon Sep 17 00:00:00 2001 From: echerniak Date: Tue, 5 Oct 2021 11:05:12 +0300 Subject: [PATCH 30/37] Tests for request converters added --- .../anapi/v2/controller/SearchController.java | 4 +- ...aramsToChargebackSearchQueryConverter.java | 96 ++++++++++------- .../ParamsToInvoiceSearchQueryConverter.java | 20 ++-- .../ParamsToPaymentSearchQueryConverter.java | 98 ++++++++++------- .../ParamsToPayoutSearchQueryConverter.java | 4 +- .../ParamsToRefundSearchQueryConverter.java | 20 ++-- ...sToChargebackSearchQueryConverterTest.java | 61 +++++++++++ ...ramsToInvoiceSearchQueryConverterTest.java | 52 +++++++++ ...ramsToPaymentSearchQueryConverterTest.java | 100 ++++++++++++++++++ ...aramsToPayoutSearchQueryConverterTest.java | 38 +++++++ ...aramsToRefundSearchQueryConverterTest.java | 41 +++++++ .../anapi/v2/testutil/OpenApiUtil.java | 18 ++-- 12 files changed, 435 insertions(+), 117 deletions(-) create mode 100644 src/test/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToChargebackSearchQueryConverterTest.java create mode 100644 src/test/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToInvoiceSearchQueryConverterTest.java create mode 100644 src/test/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPaymentSearchQueryConverterTest.java create mode 100644 src/test/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPayoutSearchQueryConverterTest.java create mode 100644 src/test/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToRefundSearchQueryConverterTest.java diff --git a/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java b/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java index f79a3d8..08fe055 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java +++ b/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java @@ -68,8 +68,8 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Pattern(regexp = "^\\d{4}$") @Valid String last4, @Pattern(regexp = "^[a-zA-Z0-9]{12}$") @Valid String rrn, @Size(min = 1, max = 40) @Valid String approvalCode, - @Valid BankCardTokenProvider bankCardTokenProvider, - @Valid BankCardPaymentSystem bankCardPaymentSystem, + @Valid String bankCardTokenProvider, + @Valid String bankCardPaymentSystem, @Min(1L) @Valid Long paymentAmountFrom, @Min(1L) @Valid Long paymentAmountTo, @Valid List excludedShops, diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToChargebackSearchQueryConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToChargebackSearchQueryConverter.java index 6dafe5c..b3f9f7d 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToChargebackSearchQueryConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToChargebackSearchQueryConverter.java @@ -37,66 +37,82 @@ public class ParamsToChargebackSearchQueryConverter { .setChargebackId(chargebackID) .setChargebackStatuses(chargebackStatuses != null ? chargebackStatuses.stream() - .map(this::mapToDamselStatus) + .map(this::mapStatus) .collect(Collectors.toList()) : null ) .setChargebackStages(chargebackStages != null ? chargebackStages.stream() - .map(this::mapToDamselStage) + .map(this::mapStage) .collect(Collectors.toList()) : null ) .setChargebackCategories(chargebackCategories != null ? chargebackCategories.stream() - .map(this::mapToDamselCategory) + .map(this::mapCategory) .collect(Collectors.toList()) : null); } - private InvoicePaymentChargebackStage mapToDamselStage(String chargebackStage) { - var stage = Enum.valueOf(ChargebackStage.class, chargebackStage); - var damselStage = new InvoicePaymentChargebackStage(); - switch (stage) { - case CHARGEBACK -> damselStage.setChargeback(new InvoicePaymentChargebackStageChargeback()); - case PRE_ARBITRATION -> damselStage.setPreArbitration(new InvoicePaymentChargebackStagePreArbitration()); - case ARBITRATION -> damselStage.setArbitration(new InvoicePaymentChargebackStageArbitration()); - default -> throw new BadRequestException( - String.format("Chargeback stage %s cannot be processed", chargebackStage)); - } + protected InvoicePaymentChargebackStage mapStage(String chargebackStage) { + try { + var stage = ChargebackStage.fromValue(chargebackStage); + var damselStage = new InvoicePaymentChargebackStage(); + switch (stage) { + case CHARGEBACK -> damselStage.setChargeback(new InvoicePaymentChargebackStageChargeback()); + case PRE_ARBITRATION -> damselStage.setPreArbitration( + new InvoicePaymentChargebackStagePreArbitration()); + case ARBITRATION -> damselStage.setArbitration(new InvoicePaymentChargebackStageArbitration()); + default -> throw new BadRequestException( + String.format("Chargeback stage %s cannot be processed", chargebackStage)); + } - return damselStage; + return damselStage; + + } catch (Exception e) { + throw new BadRequestException(e.getMessage()); + } } - private InvoicePaymentChargebackStatus mapToDamselStatus(String chargebackStatus) { - var status = Enum.valueOf(ChargebackStatus.class, chargebackStatus); - var damselStatus = new InvoicePaymentChargebackStatus(); - switch (status) { - case PENDING -> damselStatus.setPending(new InvoicePaymentChargebackPending()); - case ACCEPTED -> damselStatus.setAccepted(new InvoicePaymentChargebackAccepted()); - case REJECTED -> damselStatus.setRejected(new InvoicePaymentChargebackRejected()); - case CANCELLED -> damselStatus.setCancelled(new InvoicePaymentChargebackCancelled()); - default -> throw new BadRequestException( - String.format("Chargeback status %s cannot be processed", chargebackStatus)); - } + protected InvoicePaymentChargebackStatus mapStatus(String chargebackStatus) { + try { + var status = ChargebackStatus.fromValue(chargebackStatus); + var damselStatus = new InvoicePaymentChargebackStatus(); + switch (status) { + case PENDING -> damselStatus.setPending(new InvoicePaymentChargebackPending()); + case ACCEPTED -> damselStatus.setAccepted(new InvoicePaymentChargebackAccepted()); + case REJECTED -> damselStatus.setRejected(new InvoicePaymentChargebackRejected()); + case CANCELLED -> damselStatus.setCancelled(new InvoicePaymentChargebackCancelled()); + default -> throw new BadRequestException( + String.format("Chargeback status %s cannot be processed", chargebackStatus)); + } - return damselStatus; + return damselStatus; + + } catch (Exception e) { + throw new BadRequestException(e.getMessage()); + } } - private InvoicePaymentChargebackCategory mapToDamselCategory(String chargebackCategory) { - var category = Enum.valueOf(ChargebackCategory.class, chargebackCategory); - var damselCategory = new InvoicePaymentChargebackCategory(); - switch (category) { - case FRAUD -> damselCategory.setFraud(new InvoicePaymentChargebackCategoryFraud()); - case DISPUTE -> damselCategory.setDispute(new InvoicePaymentChargebackCategoryDispute()); - case AUTHORISATION -> damselCategory - .setAuthorisation(new InvoicePaymentChargebackCategoryAuthorisation()); - case PROCESSING_ERROR -> damselCategory - .setProcessingError(new InvoicePaymentChargebackCategoryProcessingError()); - default -> throw new BadRequestException( - String.format("Chargeback category %s cannot be processed", chargebackCategory)); - } + protected InvoicePaymentChargebackCategory mapCategory(String chargebackCategory) { + try { + var category = ChargebackCategory.fromValue(chargebackCategory); + var damselCategory = new InvoicePaymentChargebackCategory(); + switch (category) { + case FRAUD -> damselCategory.setFraud(new InvoicePaymentChargebackCategoryFraud()); + case DISPUTE -> damselCategory.setDispute(new InvoicePaymentChargebackCategoryDispute()); + case AUTHORISATION -> damselCategory + .setAuthorisation(new InvoicePaymentChargebackCategoryAuthorisation()); + case PROCESSING_ERROR -> damselCategory + .setProcessingError(new InvoicePaymentChargebackCategoryProcessingError()); + default -> throw new BadRequestException( + String.format("Chargeback category %s cannot be processed", chargebackCategory)); + } - return damselCategory; + return damselCategory; + + } catch (Exception e) { + throw new BadRequestException(e.getMessage()); + } } } diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToInvoiceSearchQueryConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToInvoiceSearchQueryConverter.java index e20b41f..2e33a64 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToInvoiceSearchQueryConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToInvoiceSearchQueryConverter.java @@ -31,14 +31,14 @@ public class ParamsToInvoiceSearchQueryConverter { .setCommonSearchQueryParams( fillCommonParams(fromTime, toTime, limit, partyID, shopIDs, continuationToken)) .setPaymentParams( - getPaymentParams(invoiceAmountFrom, invoiceAmountTo) + mapPaymentParams(invoiceAmountFrom, invoiceAmountTo) ) .setInvoiceStatus(invoiceStatus != null ? mapStatus(invoiceStatus) : null) .setInvoiceIds(merge(invoiceID, invoiceIDs)) .setExternalId(externalID); } - private PaymentParams getPaymentParams(Long invoiceAmountFrom, Long invoiceAmountTo) { + protected PaymentParams mapPaymentParams(Long invoiceAmountFrom, Long invoiceAmountTo) { var params = new PaymentParams(); if (invoiceAmountFrom != null) { params.setPaymentAmountFrom(invoiceAmountFrom); @@ -49,15 +49,11 @@ public class ParamsToInvoiceSearchQueryConverter { return params; } - private InvoiceStatus mapStatus(String statusParam) { - var status = Enum.valueOf(com.rbkmoney.openapi.anapi_v2.model.InvoiceStatus.StatusEnum.class, statusParam); - return switch (status) { - case CANCELLED -> InvoiceStatus.cancelled; - case FULFILLED -> InvoiceStatus.fulfilled; - case PAID -> InvoiceStatus.paid; - case UNPAID -> InvoiceStatus.unpaid; - default -> throw new BadRequestException( - String.format("Invoice status %s cannot be processed", status)); - }; + protected InvoiceStatus mapStatus(String status) { + try { + return InvoiceStatus.valueOf(status); + } catch (IllegalArgumentException e) { + throw new BadRequestException(String.format("Invoice status %s cannot be processed", status)); + } } } diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPaymentSearchQueryConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPaymentSearchQueryConverter.java index 27d67cf..43ccb1f 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPaymentSearchQueryConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPaymentSearchQueryConverter.java @@ -4,12 +4,7 @@ import com.rbkmoney.anapi.v2.exception.BadRequestException; import com.rbkmoney.damsel.domain.LegacyBankCardPaymentSystem; import com.rbkmoney.damsel.domain.LegacyBankCardTokenProvider; import com.rbkmoney.damsel.domain.LegacyTerminalPaymentProvider; -import com.rbkmoney.magista.InvoicePaymentStatus; -import com.rbkmoney.magista.PaymentParams; -import com.rbkmoney.magista.PaymentSearchQuery; -import com.rbkmoney.magista.PaymentToolType; -import com.rbkmoney.openapi.anapi_v2.model.BankCardPaymentSystem; -import com.rbkmoney.openapi.anapi_v2.model.BankCardTokenProvider; +import com.rbkmoney.magista.*; import com.rbkmoney.openapi.anapi_v2.model.PaymentStatus; import org.springframework.stereotype.Component; @@ -18,8 +13,6 @@ import java.util.List; import static com.rbkmoney.anapi.v2.util.ConverterUtil.fillCommonParams; import static com.rbkmoney.anapi.v2.util.ConverterUtil.merge; -import static com.rbkmoney.magista.InvoicePaymentFlowType.hold; -import static com.rbkmoney.magista.InvoicePaymentFlowType.instant; import static com.rbkmoney.magista.InvoicePaymentStatus.*; import static com.rbkmoney.magista.PaymentToolType.bank_card; import static com.rbkmoney.magista.PaymentToolType.payment_terminal; @@ -47,8 +40,8 @@ public class ParamsToPaymentSearchQueryConverter { String last4, String rrn, String approvalCode, - BankCardTokenProvider bankCardTokenProvider, - BankCardPaymentSystem bankCardPaymentSystem, + String bankCardTokenProvider, + String bankCardPaymentSystem, Long paymentAmountFrom, Long paymentAmountTo, List excludedShops, @@ -61,18 +54,12 @@ public class ParamsToPaymentSearchQueryConverter { .setInvoiceIds(merge(invoiceID, invoiceIDs)); PaymentParams paymentParams = new PaymentParams() - .setPaymentTool(paymentMethod != null ? mapToPaymentTool(paymentMethod) : null) - .setPaymentFlow(paymentFlow != null ? mapToInvoicePaymentFlow(paymentFlow) : null) + .setPaymentTool(paymentMethod != null ? mapPaymentTool(paymentMethod) : null) + .setPaymentFlow(paymentFlow != null ? mapInvoicePaymentFlow(paymentFlow) : null) .setPaymentTerminalProvider( - paymentTerminalProvider != null - ? LegacyTerminalPaymentProvider.valueOf(paymentTerminalProvider) : - null) + paymentTerminalProvider != null ? mapTerminalProvider(paymentTerminalProvider) : null) .setPaymentTokenProvider( - bankCardTokenProvider != null - ? - LegacyBankCardTokenProvider - .valueOf(bankCardTokenProvider.getValue()) : - null) + bankCardTokenProvider != null ? mapTokenProvider(bankCardTokenProvider) : null) .setPaymentEmail(payerEmail) .setPaymentApprovalCode(approvalCode) .setPaymentCustomerId(customerID) @@ -84,8 +71,7 @@ public class ParamsToPaymentSearchQueryConverter { .setPaymentRrn(rrn) .setPaymentStatus(paymentStatus != null ? mapStatus(paymentStatus) : null) .setPaymentSystem(bankCardPaymentSystem != null - ? LegacyBankCardPaymentSystem.valueOf(bankCardPaymentSystem.getValue()) : - null); + ? mapPaymentSystem(bankCardPaymentSystem) : null); if (paymentAmountFrom != null) { paymentParams.setPaymentAmountFrom(paymentAmountFrom); } @@ -96,7 +82,32 @@ public class ParamsToPaymentSearchQueryConverter { return query; } - private PaymentToolType mapToPaymentTool(String paymentMethod) { + protected LegacyTerminalPaymentProvider mapTerminalProvider(String provider) { + try { + return LegacyTerminalPaymentProvider.valueOf(provider); + } catch (IllegalArgumentException e) { + throw new BadRequestException(String.format("Terminal provider %s cannot be processed", provider)); + } + } + + protected LegacyBankCardTokenProvider mapTokenProvider(String provider) { + try { + return LegacyBankCardTokenProvider.valueOf(provider); + } catch (IllegalArgumentException e) { + throw new BadRequestException(String.format("Token provider %s cannot be processed", provider)); + } + } + + protected LegacyBankCardPaymentSystem mapPaymentSystem(String system) { + try { + return LegacyBankCardPaymentSystem.valueOf(system); + } catch (IllegalArgumentException e) { + throw new BadRequestException( + String.format("Payment system %s cannot be processed", system)); + } + } + + protected PaymentToolType mapPaymentTool(String paymentMethod) { return switch (paymentMethod) { case "bankCard" -> bank_card; case "paymentTerminal" -> payment_terminal; @@ -105,26 +116,31 @@ public class ParamsToPaymentSearchQueryConverter { }; } - private com.rbkmoney.magista.InvoicePaymentFlowType mapToInvoicePaymentFlow(String paymentFlow) { - return switch (paymentFlow) { - case "instant" -> instant; - case "hold" -> hold; - default -> throw new BadRequestException( + protected com.rbkmoney.magista.InvoicePaymentFlowType mapInvoicePaymentFlow(String paymentFlow) { + try { + return InvoicePaymentFlowType.valueOf(paymentFlow); + } catch (IllegalArgumentException e) { + throw new BadRequestException( String.format("Payment flow %s cannot be processed", paymentFlow)); - }; + } } - private InvoicePaymentStatus mapStatus(String paymentStatus) { - var status = Enum.valueOf(PaymentStatus.StatusEnum.class, paymentStatus); - return switch (status) { - case PENDING -> pending; - case PROCESSED -> processed; - case CAPTURED -> captured; - case CANCELLED -> cancelled; - case REFUNDED -> refunded; - case FAILED -> failed; - default -> throw new BadRequestException( - String.format("Payment status %s cannot be processed", paymentStatus)); - }; + protected InvoicePaymentStatus mapStatus(String paymentStatus) { + try { + var status = PaymentStatus.StatusEnum.fromValue(paymentStatus); + return switch (status) { + case PENDING -> pending; + case PROCESSED -> processed; + case CAPTURED -> captured; + case CANCELLED -> cancelled; + case REFUNDED -> refunded; + case FAILED -> failed; + case CHARGEDBACK -> charged_back; + default -> throw new BadRequestException( + String.format("Payment status %s cannot be processed", paymentStatus)); + }; + } catch (Exception e) { + throw new BadRequestException(e.getMessage()); + } } } diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPayoutSearchQueryConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPayoutSearchQueryConverter.java index aaabe3a..79620ed 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPayoutSearchQueryConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPayoutSearchQueryConverter.java @@ -25,10 +25,10 @@ public class ParamsToPayoutSearchQueryConverter { .setCommonSearchQueryParams( fillCommonParams(fromTime, toTime, limit, partyID, shopIDs, continuationToken)) .setPayoutId(payoutID) - .setPayoutType(payoutToolType != null ? mapToPayoutToolType(payoutToolType) : null); + .setPayoutType(payoutToolType != null ? mapPayoutToolType(payoutToolType) : null); } - private PayoutToolType mapToPayoutToolType(String payoutToolType) { + protected PayoutToolType mapPayoutToolType(String payoutToolType) { return switch (payoutToolType) { case "PayoutAccount" -> PayoutToolType.payout_account; case "Wallet" -> PayoutToolType.wallet; diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToRefundSearchQueryConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToRefundSearchQueryConverter.java index 79dee2e..b856b4b 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToRefundSearchQueryConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToRefundSearchQueryConverter.java @@ -1,8 +1,8 @@ package com.rbkmoney.anapi.v2.converter.search.request; import com.rbkmoney.anapi.v2.exception.BadRequestException; +import com.rbkmoney.magista.InvoicePaymentRefundStatus; import com.rbkmoney.magista.RefundSearchQuery; -import com.rbkmoney.openapi.anapi_v2.model.RefundStatus; import org.springframework.stereotype.Component; import java.time.OffsetDateTime; @@ -10,7 +10,6 @@ import java.util.List; import static com.rbkmoney.anapi.v2.util.ConverterUtil.fillCommonParams; import static com.rbkmoney.anapi.v2.util.ConverterUtil.merge; -import static com.rbkmoney.magista.InvoicePaymentRefundStatus.*; @Component public class ParamsToRefundSearchQueryConverter { @@ -30,21 +29,20 @@ public class ParamsToRefundSearchQueryConverter { return new RefundSearchQuery() .setCommonSearchQueryParams( fillCommonParams(fromTime, toTime, limit, partyID, shopIDs, continuationToken)) - .setRefundStatus(refundStatus != null ? getRefundStatus(refundStatus) : null) + .setRefundStatus(refundStatus != null ? mapStatus(refundStatus) : null) .setInvoiceIds(merge(invoiceID, invoiceIDs)) .setExternalId(externalID) .setPaymentId(paymentID) .setRefundId(refundID); } - private com.rbkmoney.magista.InvoicePaymentRefundStatus getRefundStatus(String refundStatus) { - return switch (Enum.valueOf(RefundStatus.StatusEnum.class, refundStatus)) { - case PENDING -> pending; - case SUCCEEDED -> succeeded; - case FAILED -> failed; - default -> throw new BadRequestException( - String.format("Refund status %s cannot be processed", refundStatus)); - }; + protected InvoicePaymentRefundStatus mapStatus(String status) { + try { + return InvoicePaymentRefundStatus.valueOf(status); + } catch (IllegalArgumentException e) { + throw new BadRequestException( + String.format("Refund status %s cannot be processed", status)); + } } } diff --git a/src/test/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToChargebackSearchQueryConverterTest.java b/src/test/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToChargebackSearchQueryConverterTest.java new file mode 100644 index 0000000..8ef7970 --- /dev/null +++ b/src/test/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToChargebackSearchQueryConverterTest.java @@ -0,0 +1,61 @@ +package com.rbkmoney.anapi.v2.converter.search.request; + +import com.rbkmoney.anapi.v2.exception.BadRequestException; +import com.rbkmoney.magista.ChargebackSearchQuery; +import org.junit.jupiter.api.Test; + +import java.time.OffsetDateTime; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class ParamsToChargebackSearchQueryConverterTest { + + private static final ParamsToChargebackSearchQueryConverter converter = + new ParamsToChargebackSearchQueryConverter(); + + @Test + void convert() { + ChargebackSearchQuery query = converter.convert("1", + OffsetDateTime.MIN, + OffsetDateTime.MAX, + 10, + List.of("1", "2"), + "1", + "2", + "3", + List.of("pending", "accepted"), + List.of("pre_arbitration"), + List.of("fraud"), + "test" + ); + assertNotNull(query); + } + + @Test + void mapStage() { + assertTrue(converter.mapStage("chargeback").isSetChargeback()); + assertTrue(converter.mapStage("pre_arbitration").isSetPreArbitration()); + assertTrue(converter.mapStage("arbitration").isSetArbitration()); + assertThrows(BadRequestException.class, () -> converter.mapStage("unexpected")); + } + + @Test + void mapStatus() { + assertTrue(converter.mapStatus("pending").isSetPending()); + assertTrue(converter.mapStatus("accepted").isSetAccepted()); + assertTrue(converter.mapStatus("rejected").isSetRejected()); + assertTrue(converter.mapStatus("cancelled").isSetCancelled()); + assertThrows(BadRequestException.class, () -> converter.mapStatus("unexpected")); + } + + @Test + void mapCategory() { + assertTrue(converter.mapCategory("fraud").isSetFraud()); + assertTrue(converter.mapCategory("dispute").isSetDispute()); + assertTrue(converter.mapCategory("authorisation").isSetAuthorisation()); + assertTrue(converter.mapCategory("processing_error").isSetProcessingError()); + assertThrows(BadRequestException.class, () -> converter.mapCategory("unexpected")); + + } +} \ No newline at end of file diff --git a/src/test/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToInvoiceSearchQueryConverterTest.java b/src/test/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToInvoiceSearchQueryConverterTest.java new file mode 100644 index 0000000..4baa991 --- /dev/null +++ b/src/test/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToInvoiceSearchQueryConverterTest.java @@ -0,0 +1,52 @@ +package com.rbkmoney.anapi.v2.converter.search.request; + +import com.rbkmoney.anapi.v2.exception.BadRequestException; +import com.rbkmoney.magista.InvoiceSearchQuery; +import com.rbkmoney.magista.InvoiceStatus; +import com.rbkmoney.magista.PaymentParams; +import org.junit.jupiter.api.Test; + +import java.time.OffsetDateTime; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class ParamsToInvoiceSearchQueryConverterTest { + + private static final ParamsToInvoiceSearchQueryConverter converter = + new ParamsToInvoiceSearchQueryConverter(); + + @Test + void convert() { + InvoiceSearchQuery query = converter.convert("1", + OffsetDateTime.MIN, + OffsetDateTime.MAX, + 10, + List.of("1", "2"), + List.of("1", "2"), + "paid", + "1", + "2", + 0L, + 1000L, + "test"); + assertNotNull(query); + } + + @Test + void mapPaymentParams() { + Long amountFrom = 0L; + Long amountTo = 1000L; + PaymentParams params = converter.mapPaymentParams(amountFrom, amountTo); + assertEquals(amountFrom, params.getPaymentAmountFrom()); + assertEquals(amountTo, params.getPaymentAmountTo()); + } + + @Test + void mapStatus() { + for (InvoiceStatus status : InvoiceStatus.values()) { + assertEquals(status, converter.mapStatus(status.name())); + } + assertThrows(BadRequestException.class, () -> converter.mapStatus("unexpected")); + } +} \ No newline at end of file diff --git a/src/test/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPaymentSearchQueryConverterTest.java b/src/test/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPaymentSearchQueryConverterTest.java new file mode 100644 index 0000000..636ac24 --- /dev/null +++ b/src/test/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPaymentSearchQueryConverterTest.java @@ -0,0 +1,100 @@ +package com.rbkmoney.anapi.v2.converter.search.request; + +import com.rbkmoney.anapi.v2.exception.BadRequestException; +import com.rbkmoney.damsel.domain.LegacyBankCardPaymentSystem; +import com.rbkmoney.damsel.domain.LegacyBankCardTokenProvider; +import com.rbkmoney.damsel.domain.LegacyTerminalPaymentProvider; +import com.rbkmoney.magista.InvoicePaymentFlowType; +import com.rbkmoney.magista.PaymentSearchQuery; +import com.rbkmoney.magista.PaymentToolType; +import com.rbkmoney.openapi.anapi_v2.model.PaymentStatus; +import org.junit.jupiter.api.Test; + +import java.time.OffsetDateTime; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class ParamsToPaymentSearchQueryConverterTest { + + private static final ParamsToPaymentSearchQueryConverter converter = new ParamsToPaymentSearchQueryConverter(); + + @Test + void convert() { + PaymentSearchQuery query = converter.convert("1", + OffsetDateTime.MIN, + OffsetDateTime.MAX, + 10, + List.of("1", "2", "3"), + List.of("1", "2", "3"), + "cancelled", + "hold", + "paymentTerminal", + "euroset", + "1", + "1", + "1", + "mail@mail.com", + "127.0.0.1", + "fingerprint", + "1", + "123456", + "7890", + "012345678", + "123456", + "applepay", + "mastercard", + 0L, + 1000L, + List.of("1", "2", "3"), + "test"); + assertNotNull(query); + } + + @Test + void mapPaymentTool() { + assertEquals(PaymentToolType.bank_card, converter.mapPaymentTool("bankCard")); + assertEquals(PaymentToolType.payment_terminal, converter.mapPaymentTool("paymentTerminal")); + assertThrows(BadRequestException.class, () -> converter.mapPaymentTool("unexpected")); + } + + @Test + void mapInvoicePaymentFlow() { + assertEquals(InvoicePaymentFlowType.instant, converter.mapInvoicePaymentFlow("instant")); + assertEquals(InvoicePaymentFlowType.hold, converter.mapInvoicePaymentFlow("hold")); + assertThrows(BadRequestException.class, () -> converter.mapInvoicePaymentFlow("unexpected")); + } + + @Test + void mapStatus() { + for (PaymentStatus.StatusEnum status : PaymentStatus.StatusEnum.values()) { + assertNotNull(converter.mapStatus(status.getValue())); + } + + assertThrows(BadRequestException.class, () -> converter.mapStatus("unexpected")); + } + + @Test + void mapTerminalProvider() { + for (LegacyTerminalPaymentProvider provider : LegacyTerminalPaymentProvider.values()) { + assertEquals(provider, converter.mapTerminalProvider(provider.name())); + } + assertThrows(BadRequestException.class, () -> converter.mapTerminalProvider("unexpected")); + } + + @Test + void mapTokenProvider() { + for (LegacyBankCardTokenProvider provider : LegacyBankCardTokenProvider.values()) { + assertEquals(provider, converter.mapTokenProvider(provider.name())); + } + assertThrows(BadRequestException.class, () -> converter.mapTokenProvider("unexpected")); + } + + @Test + void mapPaymentSystem() { + for (LegacyBankCardPaymentSystem system : LegacyBankCardPaymentSystem.values()) { + assertEquals(system, converter.mapPaymentSystem(system.name())); + } + assertThrows(BadRequestException.class, () -> converter.mapPaymentSystem("unexpected")); + } +} \ No newline at end of file diff --git a/src/test/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPayoutSearchQueryConverterTest.java b/src/test/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPayoutSearchQueryConverterTest.java new file mode 100644 index 0000000..e0c759a --- /dev/null +++ b/src/test/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToPayoutSearchQueryConverterTest.java @@ -0,0 +1,38 @@ +package com.rbkmoney.anapi.v2.converter.search.request; + +import com.rbkmoney.anapi.v2.exception.BadRequestException; +import com.rbkmoney.magista.PayoutSearchQuery; +import com.rbkmoney.magista.PayoutToolType; +import org.junit.jupiter.api.Test; + +import java.time.OffsetDateTime; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class ParamsToPayoutSearchQueryConverterTest { + + private static final ParamsToPayoutSearchQueryConverter converter = new ParamsToPayoutSearchQueryConverter(); + + @Test + void convert() { + PayoutSearchQuery query = converter.convert("1", + OffsetDateTime.MIN, + OffsetDateTime.MAX, + 10, + List.of("1", "2", "3"), + "1", + "Wallet", + "test"); + assertNotNull(query); + } + + @Test + void mapPayoutToolType() { + assertEquals(PayoutToolType.payout_account, converter.mapPayoutToolType("PayoutAccount")); + assertEquals(PayoutToolType.wallet, converter.mapPayoutToolType("Wallet")); + assertEquals(PayoutToolType.payment_institution_account, + converter.mapPayoutToolType("PaymentInstitutionAccount")); + assertThrows(BadRequestException.class, () -> converter.mapPayoutToolType("unexpected")); + } +} \ No newline at end of file diff --git a/src/test/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToRefundSearchQueryConverterTest.java b/src/test/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToRefundSearchQueryConverterTest.java new file mode 100644 index 0000000..51f78b4 --- /dev/null +++ b/src/test/java/com/rbkmoney/anapi/v2/converter/search/request/ParamsToRefundSearchQueryConverterTest.java @@ -0,0 +1,41 @@ +package com.rbkmoney.anapi.v2.converter.search.request; + +import com.rbkmoney.anapi.v2.exception.BadRequestException; +import com.rbkmoney.magista.InvoicePaymentRefundStatus; +import com.rbkmoney.magista.RefundSearchQuery; +import org.junit.jupiter.api.Test; + +import java.time.OffsetDateTime; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class ParamsToRefundSearchQueryConverterTest { + + private static final ParamsToRefundSearchQueryConverter converter = new ParamsToRefundSearchQueryConverter(); + + @Test + void convert() { + RefundSearchQuery query = converter.convert("1", + OffsetDateTime.MIN, + OffsetDateTime.MAX, + 10, + List.of("1", "2", "3"), + List.of("1", "2", "3"), + "1", + "1", + "1", + "1", + "pending", + "test"); + assertNotNull(query); + } + + @Test + void mapRefundStatus() { + assertEquals(InvoicePaymentRefundStatus.succeeded, converter.mapStatus("succeeded")); + assertEquals(InvoicePaymentRefundStatus.failed, converter.mapStatus("failed")); + assertEquals(InvoicePaymentRefundStatus.pending, converter.mapStatus("pending")); + assertThrows(BadRequestException.class, () -> converter.mapStatus("unexpected")); + } +} \ No newline at end of file diff --git a/src/test/java/com/rbkmoney/anapi/v2/testutil/OpenApiUtil.java b/src/test/java/com/rbkmoney/anapi/v2/testutil/OpenApiUtil.java index 780b06f..c8e27b5 100644 --- a/src/test/java/com/rbkmoney/anapi/v2/testutil/OpenApiUtil.java +++ b/src/test/java/com/rbkmoney/anapi/v2/testutil/OpenApiUtil.java @@ -29,7 +29,7 @@ public class OpenApiUtil { params.add("paymentInstitutionRealm", PaymentInstitutionRealm.live.name()); params.add("invoiceIDs", randomInteger(1, 10)); params.add("invoiceIDs", randomInteger(11, 20)); - params.add("paymentStatus", PaymentStatus.StatusEnum.PENDING.name()); + params.add("paymentStatus", PaymentStatus.StatusEnum.PENDING.getValue()); params.add("paymentFlow", "instant"); params.add("paymentMethod", "paymentTerminal"); params.add("paymentTerminalProvider", TerminalPaymentProvider.alipay.name()); @@ -43,8 +43,8 @@ public class OpenApiUtil { params.add("last4", randomInteger(1000, 9999)); params.add("rrn", "123456789010"); params.add("approvalCode", "QWERTY"); - params.add("bankCardTokenProvider", BankCardTokenProvider.APPLEPAY.name()); - params.add("bankCardPaymentSystem", BankCardPaymentSystem.MASTERCARD.name()); + params.add("bankCardTokenProvider", "applepay"); + params.add("bankCardPaymentSystem", "mastercard"); params.add("paymentAmountFrom", randomInteger(1, 9999)); params.add("paymentAmountTo", randomInteger(9999, 999999)); params.add("excludedShops", randomInteger(1, 10)); @@ -63,10 +63,10 @@ public class OpenApiUtil { params.add("invoiceID", randomInteger(1, 1000)); params.add("paymentID", randomInteger(1, 1000)); params.add("chargebackID", randomInteger(1, 1000)); - params.add("chargebackStatuses", ChargebackStatus.PENDING.name()); - params.add("chargebackStages", ChargebackStage.CHARGEBACK.name()); - params.add("chargebackCategories", ChargebackCategory.AUTHORISATION.name()); - //params.add("continuationToken", "test"); + params.add("chargebackStatuses", ChargebackStatus.PENDING.getValue()); + params.add("chargebackStages", ChargebackStage.CHARGEBACK.getValue()); + params.add("chargebackCategories", ChargebackCategory.AUTHORISATION.getValue()); + params.add("continuationToken", "test"); return params; } @@ -83,7 +83,7 @@ public class OpenApiUtil { params.add("paymentID", randomInteger(1, 1000)); params.add("refundID", randomInteger(1, 1000)); params.add("externalID", randomInteger(1, 1000)); - params.add("refundStatus", RefundStatus.StatusEnum.PENDING.name()); + params.add("refundStatus", "pending"); params.add("excludedShops", randomInteger(1, 10)); params.add("excludedShops", randomInteger(11, 20)); params.add("continuationToken", "test"); @@ -98,7 +98,7 @@ public class OpenApiUtil { params.add("paymentInstitutionRealm", PaymentInstitutionRealm.live.name()); params.add("invoiceIDs", randomInteger(1, 10)); params.add("invoiceIDs", randomInteger(11, 20)); - params.add("invoiceStatus", Invoice.StatusEnum.PAID.name()); + params.add("invoiceStatus", "paid"); params.add("invoiceID", randomInteger(1, 1000)); params.add("externalID", randomInteger(1, 1000)); params.add("invoiceAmountFrom", randomInteger(1, 1000)); From b4070bd5d4729b6b724c6bafb2210badf9b86af2 Mon Sep 17 00:00:00 2001 From: echerniak Date: Tue, 5 Oct 2021 19:35:46 +0300 Subject: [PATCH 31/37] Response converters tests added --- .../StatChargebackToChargebackConverter.java | 5 +- .../StatInvoiceToInvoiceConverter.java | 8 +- ...PaymentToPaymentSearchResultConverter.java | 22 +- .../response/StatPayoutToPayoutConverter.java | 22 +- ...atRefundToRefundSearchResultConverter.java | 8 +- ...atChargebackToChargebackConverterTest.java | 56 ++++++ .../StatInvoiceToInvoiceConverterTest.java | 91 +++++++++ ...entToPaymentSearchResultConverterTest.java | 73 +++++++ .../StatPayoutToPayoutConverterTest.java | 190 ++++++++++++++++++ ...fundToRefundSearchResultConverterTest.java | 63 ++++++ .../anapi/v2/testutil/OpenApiUtil.java | 108 +++++----- .../anapi/v2/testutil/RandomUtil.java | 17 +- 12 files changed, 576 insertions(+), 87 deletions(-) create mode 100644 src/test/java/com/rbkmoney/anapi/v2/converter/search/response/StatChargebackToChargebackConverterTest.java create mode 100644 src/test/java/com/rbkmoney/anapi/v2/converter/search/response/StatInvoiceToInvoiceConverterTest.java create mode 100644 src/test/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverterTest.java create mode 100644 src/test/java/com/rbkmoney/anapi/v2/converter/search/response/StatPayoutToPayoutConverterTest.java create mode 100644 src/test/java/com/rbkmoney/anapi/v2/converter/search/response/StatRefundToRefundSearchResultConverterTest.java diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatChargebackToChargebackConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatChargebackToChargebackConverter.java index 7008ef6..4ffa3db 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatChargebackToChargebackConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatChargebackToChargebackConverter.java @@ -22,7 +22,7 @@ public class StatChargebackToChargebackConverter { .fee(chargeback.getFee()) .chargebackReason(chargeback.getChargebackReason() != null ? new ChargebackReason() - .category(mapToChargebackCategory(chargeback.getChargebackReason().getCategory())) + .category(mapCategory(chargeback.getChargebackReason().getCategory())) .code(chargeback.getChargebackReason().getCode()) : null) .content(chargeback.getContent() != null ? new Content().data(chargeback.getContent().getData()) @@ -30,9 +30,8 @@ public class StatChargebackToChargebackConverter { .bodyCurrency(chargeback.getCurrencyCode().getSymbolicCode()); } - private ChargebackCategory mapToChargebackCategory(InvoicePaymentChargebackCategory chargebackCategory) { + protected ChargebackCategory mapCategory(InvoicePaymentChargebackCategory chargebackCategory) { if (chargebackCategory.isSetAuthorisation()) { - //TODO: Is it a typo? Could be fixed? (authorization) return ChargebackCategory.AUTHORISATION; } diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatInvoiceToInvoiceConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatInvoiceToInvoiceConverter.java index 3697b53..1389bee 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatInvoiceToInvoiceConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatInvoiceToInvoiceConverter.java @@ -28,7 +28,7 @@ public class StatInvoiceToInvoiceConverter { .cost(invoiceLine.getQuantity() * invoiceLine.getPrice().getAmount()) .price(invoiceLine.getPrice().getAmount()) .product(invoiceLine.getProduct()) - .taxMode(getTaxMode(invoiceLine.getMetadata())) + .taxMode(mapTaxMode(invoiceLine.getMetadata())) ).collect(Collectors.toList()) : null) .description(invoice.getDescription()) .dueDate(TypeUtil.stringToInstant(invoice.getDue()).atOffset(ZoneOffset.UTC)) @@ -36,11 +36,11 @@ public class StatInvoiceToInvoiceConverter { .product(invoice.getProduct()) .shopID(invoice.getShopId()); - fillStatusInfo(result, invoice.getStatus()); + mapStatusInfo(result, invoice.getStatus()); return result; } - private void fillStatusInfo(Invoice invoice, InvoiceStatus status) { + protected void mapStatusInfo(Invoice invoice, InvoiceStatus status) { if (status.isSetFulfilled()) { invoice.setStatus(Invoice.StatusEnum.FULFILLED); invoice.setReason(status.getFulfilled().getDetails()); @@ -67,7 +67,7 @@ public class StatInvoiceToInvoiceConverter { String.format("Invoice status %s cannot be processed", status)); } - private InvoiceLineTaxMode getTaxMode(Map metadata) { + protected InvoiceLineTaxMode mapTaxMode(Map metadata) { Value taxMode = metadata.get("TaxMode"); if (taxMode != null) { return new InvoiceLineTaxVAT() diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverter.java index b23be5f..83dfa21 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverter.java @@ -33,7 +33,7 @@ public class StatPaymentToPaymentSearchResultConverter { .id(payment.getId()) .invoiceID(payment.getInvoiceId()) .makeRecurrent(payment.isMakeRecurrent()) - .payer(getPayer(payment)) + .payer(mapPayer(payment.getPayer())) .shopID(payment.getShopId()) .shortID(payment.getShortId()) .transactionInfo(payment.getAdditionalTransactionInfo() != null @@ -43,27 +43,25 @@ public class StatPaymentToPaymentSearchResultConverter { : null); } - private Payer getPayer(StatPayment payment) { - var statPayer = payment.getPayer(); - Payer payer = new Payer(); + protected Payer mapPayer(com.rbkmoney.magista.Payer payer) { - if (statPayer.isSetCustomer()) { - return payer.payerType(Payer.PayerTypeEnum.CUSTOMERPAYER); + if (payer.isSetCustomer()) { + return new Payer().payerType(Payer.PayerTypeEnum.CUSTOMERPAYER); } - if (statPayer.isSetPaymentResource()) { - return payer.payerType(Payer.PayerTypeEnum.PAYMENTRESOURCEPAYER); + if (payer.isSetPaymentResource()) { + return new Payer().payerType(Payer.PayerTypeEnum.PAYMENTRESOURCEPAYER); } - if (statPayer.isSetRecurrent()) { - return payer.payerType(Payer.PayerTypeEnum.RECURRENTPAYER); + if (payer.isSetRecurrent()) { + return new Payer().payerType(Payer.PayerTypeEnum.RECURRENTPAYER); } throw new IllegalArgumentException( - String.format("Payer %s cannot be processed", statPayer)); + String.format("Payer %s cannot be processed", payer)); } - private PaymentSearchResult.StatusEnum mapStatus(InvoicePaymentStatus status) { + protected PaymentSearchResult.StatusEnum mapStatus(InvoicePaymentStatus status) { return switch (status) { case pending -> PENDING; case processed -> PROCESSED; diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPayoutToPayoutConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPayoutToPayoutConverter.java index fb426ab..a5e93b1 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPayoutToPayoutConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPayoutToPayoutConverter.java @@ -21,16 +21,16 @@ public class StatPayoutToPayoutConverter { .currency(payout.getCurrencySymbolicCode()) .fee(payout.getFee()) .id(payout.getId()) - .payoutToolDetails(mapToPayoutToolDetails(payout.getPayoutToolInfo())) + .payoutToolDetails(mapPayoutToolDetails(payout.getPayoutToolInfo())) .shopID(payout.getShopId()) - .status(mapToPayoutStatus(payout.getStatus())) + .status(mapStatus(payout.getStatus())) .cancellationDetails( payout.getStatus().isSetCancelled() ? payout.getStatus().getCancelled().getDetails() : null); } - private String mapToPayoutStatus(PayoutStatus status) { + protected String mapStatus(PayoutStatus status) { if (status.isSetCancelled()) { return "Cancelled"; } @@ -51,7 +51,7 @@ public class StatPayoutToPayoutConverter { String.format("Payout status %s cannot be processed", status)); } - private PayoutToolDetails mapToPayoutToolDetails(PayoutToolInfo payoutToolInfo) { + protected PayoutToolDetails mapPayoutToolDetails(PayoutToolInfo payoutToolInfo) { if (payoutToolInfo.isSetRussianBankAccount()) { var account = payoutToolInfo.getRussianBankAccount(); return new PayoutToolDetailsBankAccount() @@ -71,11 +71,12 @@ public class StatPayoutToPayoutConverter { ? new InternationalBankDetails() .name(account.getBank().getName()) .bic(account.getBank().getBic()) - .countryCode(getCountryCode(account.getBank().getCountry())) + .countryCode(mapCountryCode(account.getBank().getCountry())) .address(account.getBank().getAddress()) .abartn(account.getBank().getAbaRtn()) : null) - .correspondentBankAccount(mapToInternationalCorrespondentBankAccount(account)) + .correspondentBankAccount( + mapInternationalCorrespondentBankAccount(account.getCorrespondentAccount())) .detailsType("PayoutToolDetailsInternationalBankAccount"); } @@ -95,15 +96,18 @@ public class StatPayoutToPayoutConverter { } - private String getCountryCode(@Nullable CountryCode countryCode) { + protected String mapCountryCode(@Nullable CountryCode countryCode) { if (countryCode == null) { return null; } return countryCode.name(); } - private InternationalCorrespondentBankAccount mapToInternationalCorrespondentBankAccount( + protected InternationalCorrespondentBankAccount mapInternationalCorrespondentBankAccount( com.rbkmoney.damsel.domain.InternationalBankAccount account) { + if (account == null) { + return null; + } var details = account.getBank(); return new InternationalCorrespondentBankAccount() .bankDetails(details != null @@ -117,7 +121,7 @@ public class StatPayoutToPayoutConverter { .iban(account.getIban()) .number(account.getNumber()) .correspondentBankAccount(account.getCorrespondentAccount() != null - ? mapToInternationalCorrespondentBankAccount(account.getCorrespondentAccount()) + ? mapInternationalCorrespondentBankAccount(account.getCorrespondentAccount()) : null); } } diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatRefundToRefundSearchResultConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatRefundToRefundSearchResultConverter.java index 49db712..030c7af 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatRefundToRefundSearchResultConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatRefundToRefundSearchResultConverter.java @@ -19,15 +19,15 @@ public class StatRefundToRefundSearchResultConverter { .currency(refund.getCurrencySymbolicCode()) .id(refund.getId()) .shopID(refund.getShopId()) - .status(mapToRefundStatus(refund.getStatus())) + .status(mapStatus(refund.getStatus())) .externalID(refund.getExternalId()) - .error(mapToRefundStatusError(refund.getStatus())) + .error(mapStatusError(refund.getStatus())) .invoiceID(refund.getInvoiceId()) .paymentID(refund.getPaymentId()) .reason(refund.getReason()); } - private RefundStatusError mapToRefundStatusError(InvoicePaymentRefundStatus status) { + protected RefundStatusError mapStatusError(InvoicePaymentRefundStatus status) { if (status.isSetFailed() && status.getFailed().getFailure().isSetFailure()) { var failure = status.getFailed().getFailure().getFailure(); return new RefundStatusError() @@ -38,7 +38,7 @@ public class StatRefundToRefundSearchResultConverter { return null; } - private RefundSearchResult.StatusEnum mapToRefundStatus(InvoicePaymentRefundStatus status) { + protected RefundSearchResult.StatusEnum mapStatus(InvoicePaymentRefundStatus status) { if (status.isSetPending()) { return RefundSearchResult.StatusEnum.PENDING; } diff --git a/src/test/java/com/rbkmoney/anapi/v2/converter/search/response/StatChargebackToChargebackConverterTest.java b/src/test/java/com/rbkmoney/anapi/v2/converter/search/response/StatChargebackToChargebackConverterTest.java new file mode 100644 index 0000000..7b7dd5b --- /dev/null +++ b/src/test/java/com/rbkmoney/anapi/v2/converter/search/response/StatChargebackToChargebackConverterTest.java @@ -0,0 +1,56 @@ +package com.rbkmoney.anapi.v2.converter.search.response; + +import com.rbkmoney.damsel.base.Content; +import com.rbkmoney.damsel.domain.*; +import com.rbkmoney.magista.StatChargeback; +import com.rbkmoney.magista.StatChargebackResponse; +import com.rbkmoney.openapi.anapi_v2.model.Chargeback; +import org.junit.jupiter.api.Test; + +import static com.rbkmoney.anapi.v2.testutil.MagistaUtil.createSearchChargebackAllResponse; +import static com.rbkmoney.anapi.v2.testutil.RandomUtil.randomBytes; +import static com.rbkmoney.anapi.v2.testutil.RandomUtil.randomString; +import static com.rbkmoney.openapi.anapi_v2.model.ChargebackCategory.*; +import static org.junit.jupiter.api.Assertions.*; + +class StatChargebackToChargebackConverterTest { + + private static final StatChargebackToChargebackConverter converter = new StatChargebackToChargebackConverter(); + + @Test + void convert() { + StatChargebackResponse magistaResponse = createSearchChargebackAllResponse(); + StatChargeback magistaChargeback = magistaResponse.getChargebacks().get(0); + magistaChargeback.setContent(new Content() + .setType(randomString(10)) + .setData(randomBytes(10))); + Chargeback result = converter.convert(magistaChargeback); + assertAll( + () -> assertEquals(magistaChargeback.getAmount(), result.getBodyAmount()), + () -> assertEquals(magistaChargeback.getCreatedAt(), result.getCreatedAt().toString()), + () -> assertEquals(magistaChargeback.getChargebackId(), result.getChargebackId()), + () -> assertEquals(magistaChargeback.getChargebackReason().getCode(), + result.getChargebackReason().getCode()), + () -> assertArrayEquals(magistaChargeback.getContent().getData(), result.getContent().getData()), + () -> assertEquals(magistaChargeback.getContent().getType(), result.getContent().getType()), + () -> assertEquals(magistaChargeback.getCurrencyCode().getSymbolicCode(), result.getBodyCurrency()) + ); + } + + @Test + void mapCategory() { + assertAll( + () -> assertEquals(AUTHORISATION, converter.mapCategory(InvoicePaymentChargebackCategory.authorisation( + new InvoicePaymentChargebackCategoryAuthorisation()))), + () -> assertEquals(DISPUTE, converter.mapCategory( + InvoicePaymentChargebackCategory.dispute(new InvoicePaymentChargebackCategoryDispute()))), + () -> assertEquals(FRAUD, converter.mapCategory( + InvoicePaymentChargebackCategory.fraud(new InvoicePaymentChargebackCategoryFraud()))), + () -> assertEquals(PROCESSING_ERROR, converter.mapCategory( + InvoicePaymentChargebackCategory.processing_error( + new InvoicePaymentChargebackCategoryProcessingError()))), + () -> assertThrows(IllegalArgumentException.class, + () -> converter.mapCategory(new InvoicePaymentChargebackCategory())) + ); + } +} \ No newline at end of file diff --git a/src/test/java/com/rbkmoney/anapi/v2/converter/search/response/StatInvoiceToInvoiceConverterTest.java b/src/test/java/com/rbkmoney/anapi/v2/converter/search/response/StatInvoiceToInvoiceConverterTest.java new file mode 100644 index 0000000..e1e306f --- /dev/null +++ b/src/test/java/com/rbkmoney/anapi/v2/converter/search/response/StatInvoiceToInvoiceConverterTest.java @@ -0,0 +1,91 @@ +package com.rbkmoney.anapi.v2.converter.search.response; + +import com.rbkmoney.damsel.domain.*; +import com.rbkmoney.damsel.msgpack.Value; +import com.rbkmoney.magista.StatInvoice; +import com.rbkmoney.magista.StatInvoiceResponse; +import com.rbkmoney.openapi.anapi_v2.model.Invoice; +import com.rbkmoney.openapi.anapi_v2.model.InvoiceLineTaxVAT; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; + +import static com.rbkmoney.anapi.v2.testutil.MagistaUtil.createSearchInvoiceAllResponse; +import static com.rbkmoney.anapi.v2.testutil.RandomUtil.randomInt; +import static com.rbkmoney.anapi.v2.testutil.RandomUtil.randomString; +import static com.rbkmoney.openapi.anapi_v2.model.Invoice.StatusEnum.*; +import static org.junit.jupiter.api.Assertions.*; + +class StatInvoiceToInvoiceConverterTest { + + private static final StatInvoiceToInvoiceConverter converter = new StatInvoiceToInvoiceConverter(); + + @Test + void convert() { + StatInvoiceResponse magistaResponse = createSearchInvoiceAllResponse(); + StatInvoice magistaInvoice = magistaResponse.getInvoices().get(0); + magistaInvoice.getCart().getLines().get(0).setMetadata(Map.of("TaxMode", Value.str("10%"))) + .setQuantity(randomInt(1, 10000)) + .setPrice(new Cash() + .setAmount(randomInt(0, 1000000))); + Invoice result = converter.convert(magistaInvoice); + var expectedLine = magistaInvoice.getCart().getLines().get(0); + var actualLine = result.getCart().get(0); + assertAll( + () -> assertEquals(magistaInvoice.getAmount(), result.getAmount()), + () -> assertEquals(magistaInvoice.getCreatedAt(), result.getCreatedAt().toString()), + () -> assertEquals(magistaInvoice.getCurrencySymbolicCode(), result.getCurrency()), + () -> assertEquals(magistaInvoice.getExternalId(), result.getExternalID()), + () -> assertEquals(expectedLine.getPrice().getAmount(), actualLine.getPrice()), + () -> assertEquals(expectedLine.getProduct(), actualLine.getProduct()), + () -> assertEquals(expectedLine.getQuantity() * expectedLine.getPrice().getAmount(), + actualLine.getCost()), + () -> assertEquals(expectedLine.getMetadata().get("TaxMode").getStr(), + ((InvoiceLineTaxVAT) actualLine.getTaxMode()).getRate().getValue()), + () -> assertEquals(magistaInvoice.getDescription(), result.getDescription()), + () -> assertEquals(magistaInvoice.getDue(), result.getDueDate().toString()), + () -> assertEquals(magistaInvoice.getId(), result.getId()), + () -> assertEquals(magistaInvoice.getProduct(), result.getProduct()), + () -> assertEquals(magistaInvoice.getShopId(), result.getShopID()) + ); + } + + @Test + void mapStatusInfo() { + Invoice invoice = new Invoice(); + InvoiceStatus status = InvoiceStatus.fulfilled(new InvoiceFulfilled() + .setDetails(randomString(10))); + converter.mapStatusInfo(invoice, status); + assertEquals(FULFILLED, invoice.getStatus()); + assertEquals(status.getFulfilled().getDetails(), invoice.getReason()); + + invoice = new Invoice(); + status = InvoiceStatus.paid(new InvoicePaid()); + converter.mapStatusInfo(invoice, status); + assertEquals(PAID, invoice.getStatus()); + + invoice = new Invoice(); + status = InvoiceStatus.unpaid(new InvoiceUnpaid()); + converter.mapStatusInfo(invoice, status); + assertEquals(UNPAID, invoice.getStatus()); + + invoice = new Invoice(); + status = InvoiceStatus.cancelled(new InvoiceCancelled() + .setDetails(randomString(10))); + converter.mapStatusInfo(invoice, status); + assertEquals(CANCELLED, invoice.getStatus()); + assertEquals(status.getCancelled().getDetails(), invoice.getReason()); + + assertThrows(IllegalArgumentException.class, () -> converter.mapStatusInfo(new Invoice(), new InvoiceStatus())); + } + + @Test + void mapTaxMode() { + assertNull(converter.mapTaxMode(Map.of())); + String taxMode = "10%"; + Map metadata = new HashMap<>(); + metadata.put("TaxMode", Value.str(taxMode)); + assertEquals(taxMode, ((InvoiceLineTaxVAT)converter.mapTaxMode(metadata)).getRate().getValue()); + } +} \ No newline at end of file diff --git a/src/test/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverterTest.java b/src/test/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverterTest.java new file mode 100644 index 0000000..2f85bca --- /dev/null +++ b/src/test/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverterTest.java @@ -0,0 +1,73 @@ +package com.rbkmoney.anapi.v2.converter.search.response; + +import com.rbkmoney.damsel.domain.AdditionalTransactionInfo; +import com.rbkmoney.damsel.domain.PaymentResourcePayer; +import com.rbkmoney.damsel.domain.RecurrentPayer; +import com.rbkmoney.geck.common.util.TypeUtil; +import com.rbkmoney.magista.*; +import com.rbkmoney.openapi.anapi_v2.model.PaymentFlow; +import com.rbkmoney.openapi.anapi_v2.model.PaymentSearchResult; +import org.junit.jupiter.api.Test; + +import java.time.OffsetDateTime; + +import static com.rbkmoney.anapi.v2.testutil.MagistaUtil.createSearchPaymentAllResponse; +import static com.rbkmoney.anapi.v2.testutil.RandomUtil.randomString; +import static com.rbkmoney.openapi.anapi_v2.model.Payer.PayerTypeEnum.*; +import static org.junit.jupiter.api.Assertions.*; + +class StatPaymentToPaymentSearchResultConverterTest { + + private static final StatPaymentToPaymentSearchResultConverter converter = + new StatPaymentToPaymentSearchResultConverter(); + + @Test + void convert() { + StatPaymentResponse magistaResponse = createSearchPaymentAllResponse(); + StatPayment magistaPayment = magistaResponse.getPayments().get(0); + magistaPayment.setFlow(InvoicePaymentFlow.hold(new InvoicePaymentFlowHold())); + magistaPayment.setStatusChangedAt(TypeUtil.temporalToString(OffsetDateTime.now().toLocalDateTime())); + magistaPayment.setAdditionalTransactionInfo(new AdditionalTransactionInfo() + .setRrn(randomString(10)) + .setApprovalCode(randomString(10))); + PaymentSearchResult result = converter.convert(magistaPayment); + assertAll( + () -> assertEquals(magistaPayment.getAmount(), result.getAmount()), + () -> assertEquals(magistaPayment.getCreatedAt(), result.getCreatedAt().toString()), + () -> assertEquals(magistaPayment.getCurrencySymbolicCode(), result.getCurrency()), + () -> assertEquals(magistaPayment.getExternalId(), result.getExternalID()), + () -> assertEquals(magistaPayment.getFee(), result.getFee()), + () -> assertEquals(PaymentFlow.TypeEnum.PAYMENTFLOWHOLD, result.getFlow().getType()), + () -> assertEquals(magistaPayment.getLocationInfo().getCityGeoId(), + result.getGeoLocationInfo().getCityGeoID()), + () -> assertEquals(magistaPayment.getLocationInfo().getCountryGeoId(), + result.getGeoLocationInfo().getCountryGeoID()), + () -> assertEquals(magistaPayment.getStatusChangedAt(), result.getStatusChangedAt().toString()), + () -> assertEquals(magistaPayment.getId(), result.getId()), + () -> assertEquals(magistaPayment.getInvoiceId(), result.getInvoiceID()), + () -> assertEquals(magistaPayment.isMakeRecurrent(), result.getMakeRecurrent()), + () -> assertEquals(magistaPayment.getShopId(), result.getShopID()), + () -> assertEquals(magistaPayment.getShortId(), result.getShortID()), + () -> assertEquals(magistaPayment.getAdditionalTransactionInfo().getApprovalCode(), + result.getTransactionInfo().getApprovalCode()), + () -> assertEquals(magistaPayment.getAdditionalTransactionInfo().getRrn(), + result.getTransactionInfo().getRrn()) + ); + } + + @Test + void mapPayer() { + assertEquals(CUSTOMERPAYER, converter.mapPayer(Payer.customer(new CustomerPayer())).getPayerType()); + assertEquals(PAYMENTRESOURCEPAYER, + converter.mapPayer(Payer.payment_resource(new PaymentResourcePayer())).getPayerType()); + assertEquals(RECURRENTPAYER, converter.mapPayer(Payer.recurrent(new RecurrentPayer())).getPayerType()); + assertThrows(IllegalArgumentException.class, () -> converter.mapPayer(new Payer())); + } + + @Test + void mapStatus() { + for (InvoicePaymentStatus status : InvoicePaymentStatus.values()) { + assertNotNull(converter.mapStatus(status)); + } + } +} \ No newline at end of file diff --git a/src/test/java/com/rbkmoney/anapi/v2/converter/search/response/StatPayoutToPayoutConverterTest.java b/src/test/java/com/rbkmoney/anapi/v2/converter/search/response/StatPayoutToPayoutConverterTest.java new file mode 100644 index 0000000..ac3f5e8 --- /dev/null +++ b/src/test/java/com/rbkmoney/anapi/v2/converter/search/response/StatPayoutToPayoutConverterTest.java @@ -0,0 +1,190 @@ +package com.rbkmoney.anapi.v2.converter.search.response; + +import com.rbkmoney.damsel.domain.InternationalBankAccount; +import com.rbkmoney.damsel.domain.InternationalBankDetails; +import com.rbkmoney.damsel.domain.*; +import com.rbkmoney.magista.*; +import com.rbkmoney.openapi.anapi_v2.model.*; +import org.junit.jupiter.api.Test; + +import static com.rbkmoney.anapi.v2.testutil.MagistaUtil.createSearchPayoutAllResponse; +import static com.rbkmoney.anapi.v2.testutil.RandomUtil.randomString; +import static org.junit.jupiter.api.Assertions.*; + +class StatPayoutToPayoutConverterTest { + + private static final StatPayoutToPayoutConverter converter = new StatPayoutToPayoutConverter(); + + @Test + void convert() { + StatPayoutResponse magistaResponse = createSearchPayoutAllResponse(); + StatPayout magistaPayout = magistaResponse.getPayouts().get(0); + Payout result = converter.convert(magistaPayout); + assertAll( + () -> assertEquals(magistaPayout.getAmount(), result.getAmount()), + () -> assertEquals(magistaPayout.getCreatedAt(), result.getCreatedAt().toString()), + () -> assertEquals(magistaPayout.getCurrencySymbolicCode(), result.getCurrency()), + () -> assertEquals(magistaPayout.getFee(), result.getFee()), + () -> assertEquals(magistaPayout.getId(), result.getId()), + () -> assertEquals(magistaPayout.getShopId(), result.getShopID()) + ); + + } + + @Test + void mapPayoutStatus() { + assertAll( + () -> assertEquals("Cancelled", converter.mapStatus(PayoutStatus.cancelled(new PayoutCancelled()))), + () -> assertEquals("Paid", converter.mapStatus(PayoutStatus.paid(new PayoutPaid()))), + () -> assertEquals("Confirmed", converter.mapStatus(PayoutStatus.confirmed(new PayoutConfirmed()))), + () -> assertEquals("Unpaid", converter.mapStatus(PayoutStatus.unpaid(new PayoutUnpaid()))), + () -> assertThrows(IllegalArgumentException.class, () -> converter.mapStatus(new PayoutStatus())) + ); + } + + @Test + void mapPayoutToolDetails() { + //RussianBankAccount + PayoutToolInfo toolInfo = new PayoutToolInfo(); + toolInfo.setRussianBankAccount(new RussianBankAccount() + .setAccount(randomString(10)) + .setBankBik(randomString(10)) + .setBankName(randomString(10)) + .setBankPostAccount(randomString(10))); + + PayoutToolDetailsBankAccount actualRussianBankAccount = + (PayoutToolDetailsBankAccount) converter.mapPayoutToolDetails(toolInfo); + RussianBankAccount expectedRussianBankAccount = toolInfo.getRussianBankAccount(); + assertAll( + () -> assertEquals(expectedRussianBankAccount.getAccount(), actualRussianBankAccount.getAccount()), + () -> assertEquals(expectedRussianBankAccount.getBankBik(), actualRussianBankAccount.getBankBik()), + () -> assertEquals(expectedRussianBankAccount.getBankName(), actualRussianBankAccount.getBankName()), + () -> assertEquals(expectedRussianBankAccount.getBankPostAccount(), + actualRussianBankAccount.getBankPostAccount()) + ); + + //WalletInfo + toolInfo = new PayoutToolInfo(); + toolInfo.setWalletInfo(new WalletInfo() + .setWalletId(randomString(10))); + + PayoutToolDetailsWalletInfo walletActual = + (PayoutToolDetailsWalletInfo) converter.mapPayoutToolDetails(toolInfo); + WalletInfo walletExpected = toolInfo.getWalletInfo(); + assertEquals(walletExpected.getWalletId(), walletActual.getWalletID()); + + //PaymentInstitutionAccount + toolInfo = new PayoutToolInfo(); + toolInfo.setPaymentInstitutionAccount(new PaymentInstitutionAccount()); + + PayoutToolDetailsPaymentInstitutionAccount actualPaymentInstitutionAccount = + (PayoutToolDetailsPaymentInstitutionAccount) converter.mapPayoutToolDetails(toolInfo); + PaymentInstitutionAccount expectedPaymentInstitutionAccount = toolInfo.getPaymentInstitutionAccount(); + assertNotNull(expectedPaymentInstitutionAccount); + + //InternationalBankAccount + toolInfo = new PayoutToolInfo(); + toolInfo.setInternationalBankAccount(new InternationalBankAccount() + .setAccountHolder(randomString(10)) + .setIban(randomString(10)) + .setNumber(randomString(10)) + .setBank(new InternationalBankDetails() + .setName(randomString(10)) + .setAbaRtn(randomString(10)) + .setAddress(randomString(10)) + .setBic(randomString(10)) + .setCountry(CountryCode.ABW))); + + PayoutToolDetailsInternationalBankAccount actualInternationalBankAccount = + (PayoutToolDetailsInternationalBankAccount) converter.mapPayoutToolDetails(toolInfo); + InternationalBankAccount expectedInternationalBankAccount = toolInfo.getInternationalBankAccount(); + + assertAll( + () -> assertEquals(expectedInternationalBankAccount.getIban(), + actualInternationalBankAccount.getIban()), + () -> assertEquals(expectedInternationalBankAccount.getNumber(), + actualInternationalBankAccount.getNumber()), + () -> assertEquals(expectedInternationalBankAccount.getBank().getAbaRtn(), + actualInternationalBankAccount.getBankDetails().getAbartn()), + () -> assertEquals(expectedInternationalBankAccount.getBank().getAddress(), + actualInternationalBankAccount.getBankDetails().getAddress()), + () -> assertEquals(expectedInternationalBankAccount.getBank().getBic(), + actualInternationalBankAccount.getBankDetails().getBic()), + () -> assertEquals(expectedInternationalBankAccount.getBank().getName(), + actualInternationalBankAccount.getBankDetails().getName()), + () -> assertEquals(expectedInternationalBankAccount.getBank().getCountry().name(), + actualInternationalBankAccount.getBankDetails().getCountryCode()), + //tested via mapInternationalCorrespondentBankAccount test + () -> assertNull(actualInternationalBankAccount.getCorrespondentBankAccount()) + ); + + //Some missing type + assertThrows(IllegalArgumentException.class, () -> converter.mapPayoutToolDetails(new PayoutToolInfo())); + + } + + @Test + void mapCountryCode() { + CountryCode countryCode = CountryCode.ABH; + assertEquals("ABH", converter.mapCountryCode(countryCode)); + assertNull(converter.mapCountryCode(null)); + } + + @Test + void mapInternationalCorrespondentBankAccount() { + InternationalBankAccount expected = new InternationalBankAccount() + .setAccountHolder(randomString(10)) + .setIban(randomString(10)) + .setNumber(randomString(10)) + .setBank(new InternationalBankDetails() + .setName(randomString(10)) + .setAbaRtn(randomString(10)) + .setAddress(randomString(10)) + .setBic(randomString(10)) + .setCountry(CountryCode.ABW)) + .setCorrespondentAccount(new InternationalBankAccount() + .setAccountHolder(randomString(5)) + .setIban(randomString(5)) + .setNumber(randomString(5)) + .setBank(new InternationalBankDetails() + .setName(randomString(5)) + .setAbaRtn(randomString(5)) + .setAddress(randomString(5)) + .setBic(randomString(5)) + .setCountry(CountryCode.RUS))); + + InternationalCorrespondentBankAccount actual = converter.mapInternationalCorrespondentBankAccount(expected); + + assertAll( + () -> assertEquals(expected.getIban(), + actual.getIban()), + () -> assertEquals(expected.getNumber(), + actual.getNumber()), + () -> assertEquals(expected.getBank().getAbaRtn(), + actual.getBankDetails().getAbartn()), + () -> assertEquals(expected.getBank().getAddress(), + actual.getBankDetails().getAddress()), + () -> assertEquals(expected.getBank().getBic(), + actual.getBankDetails().getBic()), + () -> assertEquals(expected.getBank().getName(), + actual.getBankDetails().getName()), + () -> assertEquals(expected.getBank().getCountry().name(), + actual.getBankDetails().getCountryCode()), + () -> assertEquals(expected.getCorrespondentAccount().getIban(), + actual.getCorrespondentBankAccount().getIban()), + () -> assertEquals(expected.getCorrespondentAccount().getNumber(), + actual.getCorrespondentBankAccount().getNumber()), + () -> assertEquals(expected.getCorrespondentAccount().getBank().getAbaRtn(), + actual.getCorrespondentBankAccount().getBankDetails().getAbartn()), + () -> assertEquals(expected.getCorrespondentAccount().getBank().getAddress(), + actual.getCorrespondentBankAccount().getBankDetails().getAddress()), + () -> assertEquals(expected.getCorrespondentAccount().getBank().getBic(), + actual.getCorrespondentBankAccount().getBankDetails().getBic()), + () -> assertEquals(expected.getCorrespondentAccount().getBank().getName(), + actual.getCorrespondentBankAccount().getBankDetails().getName()), + () -> assertEquals(expected.getCorrespondentAccount().getBank().getCountry().name(), + actual.getCorrespondentBankAccount().getBankDetails().getCountryCode()), + () -> assertNull(actual.getCorrespondentBankAccount().getCorrespondentBankAccount()) + ); + } +} \ No newline at end of file diff --git a/src/test/java/com/rbkmoney/anapi/v2/converter/search/response/StatRefundToRefundSearchResultConverterTest.java b/src/test/java/com/rbkmoney/anapi/v2/converter/search/response/StatRefundToRefundSearchResultConverterTest.java new file mode 100644 index 0000000..e0e1024 --- /dev/null +++ b/src/test/java/com/rbkmoney/anapi/v2/converter/search/response/StatRefundToRefundSearchResultConverterTest.java @@ -0,0 +1,63 @@ +package com.rbkmoney.anapi.v2.converter.search.response; + +import com.rbkmoney.damsel.domain.*; +import com.rbkmoney.magista.StatRefund; +import com.rbkmoney.magista.StatRefundResponse; +import com.rbkmoney.openapi.anapi_v2.model.RefundSearchResult; +import com.rbkmoney.openapi.anapi_v2.model.RefundStatusError; +import org.junit.jupiter.api.Test; + +import static com.rbkmoney.anapi.v2.testutil.MagistaUtil.createSearchRefundAllResponse; +import static com.rbkmoney.anapi.v2.testutil.RandomUtil.randomString; +import static com.rbkmoney.openapi.anapi_v2.model.RefundSearchResult.StatusEnum.*; +import static org.junit.jupiter.api.Assertions.*; + +class StatRefundToRefundSearchResultConverterTest { + + private static final StatRefundToRefundSearchResultConverter converter = + new StatRefundToRefundSearchResultConverter(); + + @Test + void convert() { + StatRefundResponse magistaResponse = createSearchRefundAllResponse(); + StatRefund magistaRefund = magistaResponse.getRefunds().get(0); + RefundSearchResult result = converter.convert(magistaRefund); + assertAll( + () -> assertEquals(magistaRefund.getAmount(), result.getAmount()), + () -> assertEquals(magistaRefund.getCreatedAt(), result.getCreatedAt().toString()), + () -> assertEquals(magistaRefund.getCurrencySymbolicCode(), result.getCurrency()), + () -> assertEquals(magistaRefund.getId(), result.getId()), + () -> assertEquals(magistaRefund.getShopId(), result.getShopID()), + () -> assertEquals(magistaRefund.getExternalId(), result.getExternalID()), + () -> assertEquals(magistaRefund.getInvoiceId(), result.getInvoiceID()), + () -> assertEquals(magistaRefund.getPaymentId(), result.getPaymentID()), + () -> assertEquals(magistaRefund.getReason(), result.getReason()) + ); + } + + @Test + void mapStatusError() { + assertNull(converter.mapStatusError(new InvoicePaymentRefundStatus())); + String reason = randomString(10); + String code = randomString(10); + InvoicePaymentRefundStatus status = InvoicePaymentRefundStatus + .failed(new InvoicePaymentRefundFailed() + .setFailure(OperationFailure.failure(new Failure() + .setReason(reason) + .setCode(code)))); + RefundStatusError statusError = converter.mapStatusError(status); + assertEquals(reason, statusError.getMessage()); + assertEquals(code, statusError.getCode()); + } + + @Test + void mapStatus() { + assertEquals(PENDING, + converter.mapStatus(InvoicePaymentRefundStatus.pending(new InvoicePaymentRefundPending()))); + assertEquals(FAILED, + converter.mapStatus(InvoicePaymentRefundStatus.failed(new InvoicePaymentRefundFailed()))); + assertEquals(SUCCEEDED, + converter.mapStatus(InvoicePaymentRefundStatus.succeeded(new InvoicePaymentRefundSucceeded()))); + assertThrows(IllegalArgumentException.class, () -> converter.mapStatus(new InvoicePaymentRefundStatus())); + } +} \ No newline at end of file diff --git a/src/test/java/com/rbkmoney/anapi/v2/testutil/OpenApiUtil.java b/src/test/java/com/rbkmoney/anapi/v2/testutil/OpenApiUtil.java index c8e27b5..3ae70fb 100644 --- a/src/test/java/com/rbkmoney/anapi/v2/testutil/OpenApiUtil.java +++ b/src/test/java/com/rbkmoney/anapi/v2/testutil/OpenApiUtil.java @@ -7,62 +7,62 @@ import lombok.experimental.UtilityClass; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; -import static com.rbkmoney.anapi.v2.testutil.RandomUtil.randomInteger; +import static com.rbkmoney.anapi.v2.testutil.RandomUtil.randomIntegerAsString; @UtilityClass public class OpenApiUtil { public static MultiValueMap getSearchRequiredParams() { MultiValueMap params = new LinkedMultiValueMap<>(); - params.add("partyID", randomInteger(1, 1000)); + params.add("partyID", randomIntegerAsString(1, 1000)); params.add("fromTime", "2007-12-03T10:15:30+01:00"); params.add("toTime", "2020-12-03T10:15:30+01:00"); - params.add("limit", randomInteger(1, 40)); + params.add("limit", randomIntegerAsString(1, 40)); return params; } public static MultiValueMap getSearchPaymentAllParams() { MultiValueMap params = getSearchRequiredParams(); - params.add("shopID", randomInteger(1, 10)); - params.add("shopIDs", randomInteger(11, 20)); - params.add("shopIDs", randomInteger(21, 30)); + params.add("shopID", randomIntegerAsString(1, 10)); + params.add("shopIDs", randomIntegerAsString(11, 20)); + params.add("shopIDs", randomIntegerAsString(21, 30)); params.add("paymentInstitutionRealm", PaymentInstitutionRealm.live.name()); - params.add("invoiceIDs", randomInteger(1, 10)); - params.add("invoiceIDs", randomInteger(11, 20)); + params.add("invoiceIDs", randomIntegerAsString(1, 10)); + params.add("invoiceIDs", randomIntegerAsString(11, 20)); params.add("paymentStatus", PaymentStatus.StatusEnum.PENDING.getValue()); params.add("paymentFlow", "instant"); params.add("paymentMethod", "paymentTerminal"); params.add("paymentTerminalProvider", TerminalPaymentProvider.alipay.name()); - params.add("invoiceID", randomInteger(1, 1000)); - params.add("paymentID", randomInteger(1, 1000)); - params.add("externalID", randomInteger(1, 1000)); + params.add("invoiceID", randomIntegerAsString(1, 1000)); + params.add("paymentID", randomIntegerAsString(1, 1000)); + params.add("externalID", randomIntegerAsString(1, 1000)); params.add("payerEmail", "payer@mail.com"); params.add("payerIP", "0.0.0.0"); params.add("payerFingerprint", "iamveryunique"); - params.add("first6", randomInteger(100000, 999999)); - params.add("last4", randomInteger(1000, 9999)); + params.add("first6", randomIntegerAsString(100000, 999999)); + params.add("last4", randomIntegerAsString(1000, 9999)); params.add("rrn", "123456789010"); params.add("approvalCode", "QWERTY"); params.add("bankCardTokenProvider", "applepay"); params.add("bankCardPaymentSystem", "mastercard"); - params.add("paymentAmountFrom", randomInteger(1, 9999)); - params.add("paymentAmountTo", randomInteger(9999, 999999)); - params.add("excludedShops", randomInteger(1, 10)); - params.add("excludedShops", randomInteger(11, 20)); + params.add("paymentAmountFrom", randomIntegerAsString(1, 9999)); + params.add("paymentAmountTo", randomIntegerAsString(9999, 999999)); + params.add("excludedShops", randomIntegerAsString(1, 10)); + params.add("excludedShops", randomIntegerAsString(11, 20)); params.add("continuationToken", "test"); return params; } public static MultiValueMap getSearchChargebackAllParams() { MultiValueMap params = getSearchRequiredParams(); - params.add("shopID", randomInteger(1, 10)); - params.add("shopIDs", randomInteger(11, 20)); - params.add("shopIDs", randomInteger(21, 30)); + params.add("shopID", randomIntegerAsString(1, 10)); + params.add("shopIDs", randomIntegerAsString(11, 20)); + params.add("shopIDs", randomIntegerAsString(21, 30)); params.add("paymentInstitutionRealm", PaymentInstitutionRealm.live.name()); - params.add("offset", randomInteger(1, 10)); - params.add("invoiceID", randomInteger(1, 1000)); - params.add("paymentID", randomInteger(1, 1000)); - params.add("chargebackID", randomInteger(1, 1000)); + params.add("offset", randomIntegerAsString(1, 10)); + params.add("invoiceID", randomIntegerAsString(1, 1000)); + params.add("paymentID", randomIntegerAsString(1, 1000)); + params.add("chargebackID", randomIntegerAsString(1, 1000)); params.add("chargebackStatuses", ChargebackStatus.PENDING.getValue()); params.add("chargebackStages", ChargebackStage.CHARGEBACK.getValue()); params.add("chargebackCategories", ChargebackCategory.AUTHORISATION.getValue()); @@ -72,39 +72,39 @@ public class OpenApiUtil { public static MultiValueMap getSearchRefundAllParams() { MultiValueMap params = getSearchRequiredParams(); - params.add("shopID", randomInteger(1, 10)); - params.add("shopIDs", randomInteger(11, 20)); - params.add("shopIDs", randomInteger(21, 30)); + params.add("shopID", randomIntegerAsString(1, 10)); + params.add("shopIDs", randomIntegerAsString(11, 20)); + params.add("shopIDs", randomIntegerAsString(21, 30)); params.add("paymentInstitutionRealm", PaymentInstitutionRealm.live.name()); - params.add("offset", randomInteger(1, 10)); - params.add("invoiceIDs", randomInteger(1, 10)); - params.add("invoiceIDs", randomInteger(11, 20)); - params.add("invoiceID", randomInteger(1, 1000)); - params.add("paymentID", randomInteger(1, 1000)); - params.add("refundID", randomInteger(1, 1000)); - params.add("externalID", randomInteger(1, 1000)); + params.add("offset", randomIntegerAsString(1, 10)); + params.add("invoiceIDs", randomIntegerAsString(1, 10)); + params.add("invoiceIDs", randomIntegerAsString(11, 20)); + params.add("invoiceID", randomIntegerAsString(1, 1000)); + params.add("paymentID", randomIntegerAsString(1, 1000)); + params.add("refundID", randomIntegerAsString(1, 1000)); + params.add("externalID", randomIntegerAsString(1, 1000)); params.add("refundStatus", "pending"); - params.add("excludedShops", randomInteger(1, 10)); - params.add("excludedShops", randomInteger(11, 20)); + params.add("excludedShops", randomIntegerAsString(1, 10)); + params.add("excludedShops", randomIntegerAsString(11, 20)); params.add("continuationToken", "test"); return params; } public static MultiValueMap getSearchInvoiceAllParams() { MultiValueMap params = getSearchRequiredParams(); - params.add("shopID", randomInteger(1, 10)); - params.add("shopIDs", randomInteger(11, 20)); - params.add("shopIDs", randomInteger(21, 30)); + params.add("shopID", randomIntegerAsString(1, 10)); + params.add("shopIDs", randomIntegerAsString(11, 20)); + params.add("shopIDs", randomIntegerAsString(21, 30)); params.add("paymentInstitutionRealm", PaymentInstitutionRealm.live.name()); - params.add("invoiceIDs", randomInteger(1, 10)); - params.add("invoiceIDs", randomInteger(11, 20)); + params.add("invoiceIDs", randomIntegerAsString(1, 10)); + params.add("invoiceIDs", randomIntegerAsString(11, 20)); params.add("invoiceStatus", "paid"); - params.add("invoiceID", randomInteger(1, 1000)); - params.add("externalID", randomInteger(1, 1000)); - params.add("invoiceAmountFrom", randomInteger(1, 1000)); - params.add("invoiceAmountTo", randomInteger(1, 1000)); - params.add("excludedShops", randomInteger(1, 10)); - params.add("excludedShops", randomInteger(11, 20)); + params.add("invoiceID", randomIntegerAsString(1, 1000)); + params.add("externalID", randomIntegerAsString(1, 1000)); + params.add("invoiceAmountFrom", randomIntegerAsString(1, 1000)); + params.add("invoiceAmountTo", randomIntegerAsString(1, 1000)); + params.add("excludedShops", randomIntegerAsString(1, 10)); + params.add("excludedShops", randomIntegerAsString(11, 20)); params.add("continuationToken", "test"); return params; @@ -112,15 +112,15 @@ public class OpenApiUtil { public static MultiValueMap getSearchPayoutAllParams() { MultiValueMap params = getSearchRequiredParams(); - params.add("shopID", randomInteger(1, 10)); - params.add("shopIDs", randomInteger(11, 20)); - params.add("shopIDs", randomInteger(21, 30)); + params.add("shopID", randomIntegerAsString(1, 10)); + params.add("shopIDs", randomIntegerAsString(11, 20)); + params.add("shopIDs", randomIntegerAsString(21, 30)); params.add("paymentInstitutionRealm", PaymentInstitutionRealm.live.name()); - params.add("offset", randomInteger(1, 10)); - params.add("payoutID", randomInteger(1, 1000)); + params.add("offset", randomIntegerAsString(1, 10)); + params.add("payoutID", randomIntegerAsString(1, 1000)); params.add("payoutToolType", "PayoutAccount"); - params.add("excludedShops", randomInteger(1, 10)); - params.add("excludedShops", randomInteger(11, 20)); + params.add("excludedShops", randomIntegerAsString(1, 10)); + params.add("excludedShops", randomIntegerAsString(11, 20)); params.add("continuationToken", "test"); return params; } diff --git a/src/test/java/com/rbkmoney/anapi/v2/testutil/RandomUtil.java b/src/test/java/com/rbkmoney/anapi/v2/testutil/RandomUtil.java index a8f98a2..fb061f8 100644 --- a/src/test/java/com/rbkmoney/anapi/v2/testutil/RandomUtil.java +++ b/src/test/java/com/rbkmoney/anapi/v2/testutil/RandomUtil.java @@ -2,6 +2,7 @@ package com.rbkmoney.anapi.v2.testutil; import lombok.experimental.UtilityClass; +import java.nio.charset.StandardCharsets; import java.util.Random; @UtilityClass @@ -9,8 +10,22 @@ public class RandomUtil { private static final Random random = new Random(); - public static String randomInteger(int from, int to) { + public static int randomInt(int from, int to) { + return random.nextInt(to - from) + from; + } + + public static String randomIntegerAsString(int from, int to) { return String.valueOf(random.nextInt(to - from) + from); } + public static String randomString(int length) { + return new String(randomBytes(length), StandardCharsets.UTF_8); + } + + public static byte[] randomBytes(int length) { + byte[] array = new byte[length]; + new Random().nextBytes(array); + return array; + } + } From ce482e96d3c0e5d964f6e444dbf747bb3f5ead90 Mon Sep 17 00:00:00 2001 From: echerniak Date: Tue, 5 Oct 2021 20:37:40 +0300 Subject: [PATCH 32/37] Cleanup --- pom.xml | 2 +- .../rbkmoney/anapi/v2/AnapiV2Application.java | 1 - .../v2/controller/ErrorControllerAdvice.java | 14 ++++++++++ .../anapi/v2/controller/SearchController.java | 28 +++++++++++++++---- .../anapi/v2/security/AccessService.java | 2 -- .../anapi/v2/service/VortigonService.java | 14 +++++----- src/main/resources/application.yml | 4 +-- .../anapi/v2/auth/utils/JwtTokenBuilder.java | 15 ---------- ...bstractKeycloakOpenIdAsWiremockConfig.java | 4 --- 9 files changed, 46 insertions(+), 38 deletions(-) diff --git a/pom.xml b/pom.xml index a50dba7..b269dce 100644 --- a/pom.xml +++ b/pom.xml @@ -74,7 +74,7 @@ com.rbkmoney magista-proto - SNAPSHOT + 1.18-e470bc7 com.rbkmoney diff --git a/src/main/java/com/rbkmoney/anapi/v2/AnapiV2Application.java b/src/main/java/com/rbkmoney/anapi/v2/AnapiV2Application.java index 04bfb05..f7cb15a 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/AnapiV2Application.java +++ b/src/main/java/com/rbkmoney/anapi/v2/AnapiV2Application.java @@ -2,7 +2,6 @@ package com.rbkmoney.anapi.v2; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.web.servlet.ServletComponentScan; @ServletComponentScan diff --git a/src/main/java/com/rbkmoney/anapi/v2/controller/ErrorControllerAdvice.java b/src/main/java/com/rbkmoney/anapi/v2/controller/ErrorControllerAdvice.java index 9e47e7f..47a7d14 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/controller/ErrorControllerAdvice.java +++ b/src/main/java/com/rbkmoney/anapi/v2/controller/ErrorControllerAdvice.java @@ -1,11 +1,13 @@ package com.rbkmoney.anapi.v2.controller; +import com.rbkmoney.anapi.v2.exception.AuthorizationException; import com.rbkmoney.anapi.v2.exception.BadRequestException; import com.rbkmoney.anapi.v2.exception.DeadlineException; import com.rbkmoney.openapi.anapi_v2.model.DefaultLogicError; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; +import org.springframework.security.access.AccessDeniedException; import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; @@ -63,6 +65,18 @@ public class ErrorControllerAdvice { .message(e.getMessage()); } + @ExceptionHandler({AccessDeniedException.class}) + @ResponseStatus(HttpStatus.FORBIDDEN) + public void handleAccessDeniedException(AccessDeniedException e) { + log.warn("<- Res [403]: Request denied access", e); + } + + @ExceptionHandler({AuthorizationException.class}) + @ResponseStatus(HttpStatus.FORBIDDEN) + public void handleAccessDeniedException(AuthorizationException e) { + log.warn("<- Res [403]: Request denied access", e); + } + @ExceptionHandler(Exception.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public void handleException(Exception e) { diff --git a/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java b/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java index 08fe055..4f5cbb5 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java +++ b/src/main/java/com/rbkmoney/anapi/v2/controller/SearchController.java @@ -74,6 +74,7 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Min(1L) @Valid Long paymentAmountTo, @Valid List excludedShops, @Valid String continuationToken) { + log.info("-> Req: xRequestID={}", xRequestID); checkDeadline(xRequestDeadline, xRequestID); shopIDs = accessService .getAccessibleShops("searchPayments", partyID, merge(shopID, shopIDs), paymentInstitutionRealm); @@ -105,6 +106,7 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp excludedShops, continuationToken); InlineResponse20010 response = searchService.findPayments(query); + log.info("<- Res [200]: xRequestID={}", xRequestID); return ResponseEntity.ok(response); } @@ -118,7 +120,8 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Size(min = 1, max = 40) @Valid String shopID, @Valid List shopIDs, @Valid String paymentInstitutionRealm, - @Min(0L) @Valid Integer offset, + //Not used by magista + @Min(0L) @Valid @Deprecated Integer offset, @Size(min = 1, max = 40) @Valid String invoiceID, @Size(min = 1, max = 40) @Valid String paymentID, @Size(min = 1, max = 40) @Valid String chargebackID, @@ -126,6 +129,7 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Valid List chargebackStages, @Valid List chargebackCategories, @Valid String continuationToken) { + log.info("-> Req: xRequestID={}", xRequestID); checkDeadline(xRequestDeadline, xRequestID); shopIDs = accessService .getAccessibleShops("searchChargebacks", partyID, merge(shopID, shopIDs), paymentInstitutionRealm); @@ -143,6 +147,7 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp continuationToken); InlineResponse2008 response = searchService .findChargebacks(query); + log.info("<- Res [200]: xRequestID={}", xRequestID); return ResponseEntity.ok(response); } @@ -162,8 +167,10 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Size(min = 1, max = 40) @Valid String externalID, @Min(1L) @Valid Long invoiceAmountFrom, @Min(1L) @Valid Long invoiceAmountTo, - @Valid List excludedShops, + //Not used by magista + @Valid @Deprecated List excludedShops, @Valid String continuationToken) { + log.info("-> Req: xRequestID={}", xRequestID); checkDeadline(xRequestDeadline, xRequestID); shopIDs = accessService .getAccessibleShops("searchInvoices", partyID, merge(shopID, shopIDs), paymentInstitutionRealm); @@ -180,6 +187,7 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp invoiceAmountTo, continuationToken); InlineResponse2009 response = searchService.findInvoices(query); + log.info("<- Res [200]: xRequestID={}", xRequestID); return ResponseEntity.ok(response); } @@ -193,11 +201,14 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Size(min = 1, max = 40) @Valid String shopID, @Valid List shopIDs, @Valid String paymentInstitutionRealm, - @Min(0L) @Valid Integer offset, + //Not used by magista + @Min(0L) @Valid @Deprecated Integer offset, @Size(min = 1, max = 40) @Valid String payoutID, @Valid String payoutToolType, - @Valid List excludedShops, + //Not used by magista + @Valid @Deprecated List excludedShops, @Valid String continuationToken) { + log.info("-> Req: xRequestID={}", xRequestID); checkDeadline(xRequestDeadline, xRequestID); shopIDs = accessService .getAccessibleShops("searchPayouts", partyID, merge(shopID, shopIDs), paymentInstitutionRealm); @@ -210,6 +221,7 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp payoutToolType, continuationToken); InlineResponse20011 response = searchService.findPayouts(query); + log.info("<- Res [200]: xRequestID={}", xRequestID); return ResponseEntity.ok(response); } @@ -223,15 +235,18 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp @Size(min = 1, max = 40) @Valid String shopID, @Valid List shopIDs, @Valid String paymentInstitutionRealm, - @Min(0L) @Valid Integer offset, + //Not used by magista + @Min(0L) @Valid @Deprecated Integer offset, @Valid List invoiceIDs, @Size(min = 1, max = 40) @Valid String invoiceID, @Size(min = 1, max = 40) @Valid String paymentID, @Size(min = 1, max = 40) @Valid String refundID, @Size(min = 1, max = 40) @Valid String externalID, @Valid String refundStatus, - @Valid List excludedShops, + //Not used by magista + @Valid @Deprecated List excludedShops, @Valid String continuationToken) { + log.info("-> Req: xRequestID={}", xRequestID); checkDeadline(xRequestDeadline, xRequestID); shopIDs = accessService .getAccessibleShops("searchRefunds", partyID, merge(shopID, shopIDs), paymentInstitutionRealm); @@ -248,6 +263,7 @@ public class SearchController implements PaymentsApi, ChargebacksApi, InvoicesAp refundStatus, continuationToken); InlineResponse20012 response = searchService.findRefunds(query); + log.info("<- Res [200]: xRequestID={}", xRequestID); return ResponseEntity.ok(response); } } diff --git a/src/main/java/com/rbkmoney/anapi/v2/security/AccessService.java b/src/main/java/com/rbkmoney/anapi/v2/security/AccessService.java index cba7cef..354790e 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/security/AccessService.java +++ b/src/main/java/com/rbkmoney/anapi/v2/security/AccessService.java @@ -61,8 +61,6 @@ public class AccessService { } return new ArrayList<>(shopIds); - - } private AnapiBouncerContext buildAnapiBouncerContext(String operationId, String partyId, List shopIds) { diff --git a/src/main/java/com/rbkmoney/anapi/v2/service/VortigonService.java b/src/main/java/com/rbkmoney/anapi/v2/service/VortigonService.java index 4902a8b..7d75d79 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/service/VortigonService.java +++ b/src/main/java/com/rbkmoney/anapi/v2/service/VortigonService.java @@ -17,15 +17,15 @@ public class VortigonService { @SneakyThrows public List getShopIds(String partyId, String realm) { - return vortigonClient.getShopsIds(partyId, mapToRealm(realm)); + return vortigonClient.getShopsIds(partyId, mapRealm(realm)); } - private PaymentInstitutionRealm mapToRealm(String realm) { - return switch (realm) { - case "live" -> PaymentInstitutionRealm.live; - case "test" -> PaymentInstitutionRealm.test; - default -> throw new BadRequestException( + private PaymentInstitutionRealm mapRealm(String realm) { + try { + return PaymentInstitutionRealm.valueOf(realm); + } catch (IllegalArgumentException e) { + throw new BadRequestException( String.format("Realm %s cannot be processed", realm)); - }; + } } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 16e0794..c4c3f2a 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -27,10 +27,10 @@ management: service: magista: - url: http://magista:8022/stat + url: http://localhost:8022/change_it networkTimeout: 5000 vortigon: - url: http://hellgate:8022/v1/processing/partymgmt + url: http://localhost:8022/change_it networkTimeout: 5000 orgManagement: url: http://localhost:8022/change_it diff --git a/src/test/java/com/rbkmoney/anapi/v2/auth/utils/JwtTokenBuilder.java b/src/test/java/com/rbkmoney/anapi/v2/auth/utils/JwtTokenBuilder.java index 7564d7e..bf04921 100644 --- a/src/test/java/com/rbkmoney/anapi/v2/auth/utils/JwtTokenBuilder.java +++ b/src/test/java/com/rbkmoney/anapi/v2/auth/utils/JwtTokenBuilder.java @@ -10,9 +10,6 @@ import java.security.PrivateKey; import java.time.Instant; import java.util.UUID; -/** - * @since 04.07.17 - **/ public class JwtTokenBuilder { public static final String DEFAULT_USERNAME = "Darth Vader"; @@ -38,18 +35,6 @@ public class JwtTokenBuilder { this.privateKey = privateKey; } - public String getUserId() { - return userId; - } - - public String getUsername() { - return username; - } - - public String getEmail() { - return email; - } - public String generateJwtWithRoles(String issuer, String... roles) { long iat = Instant.now().getEpochSecond(); long exp = iat + 60 * 10; diff --git a/src/test/java/com/rbkmoney/anapi/v2/config/AbstractKeycloakOpenIdAsWiremockConfig.java b/src/test/java/com/rbkmoney/anapi/v2/config/AbstractKeycloakOpenIdAsWiremockConfig.java index f5a0953..3d58873 100644 --- a/src/test/java/com/rbkmoney/anapi/v2/config/AbstractKeycloakOpenIdAsWiremockConfig.java +++ b/src/test/java/com/rbkmoney/anapi/v2/config/AbstractKeycloakOpenIdAsWiremockConfig.java @@ -29,10 +29,6 @@ public abstract class AbstractKeycloakOpenIdAsWiremockConfig { keycloakOpenIdStub.givenStub(); } - protected String generateJwt(long iat, long exp, String... roles) { - return keycloakOpenIdStub.generateJwt(iat, exp, roles); - } - protected String generateInvoicesReadJwt() { return keycloakOpenIdStub.generateJwt("invoices:read"); } From 43f041227ae41edf4333300c65df8e8574b18398 Mon Sep 17 00:00:00 2001 From: echerniak Date: Wed, 6 Oct 2021 20:29:01 +0300 Subject: [PATCH 33/37] Dependency update --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b269dce..8227619 100644 --- a/pom.xml +++ b/pom.xml @@ -69,7 +69,7 @@ com.rbkmoney swag-anapi-v2 - 1.43-33abd08-server + 1.47-23587c8-server com.rbkmoney From 4b047db9d24cf7df17d11eced4b873030894857b Mon Sep 17 00:00:00 2001 From: echerniak Date: Tue, 12 Oct 2021 08:02:33 +0300 Subject: [PATCH 34/37] Magista proto updated --- pom.xml | 2 +- ...PaymentToPaymentSearchResultConverter.java | 39 +++++++++++++------ ...entToPaymentSearchResultConverterTest.java | 35 ++++++++++++++--- .../anapi/v2/testutil/MagistaUtil.java | 7 ++-- 4 files changed, 61 insertions(+), 22 deletions(-) diff --git a/pom.xml b/pom.xml index 8227619..846a575 100644 --- a/pom.xml +++ b/pom.xml @@ -74,7 +74,7 @@ com.rbkmoney magista-proto - 1.18-e470bc7 + 1.19-50148e1 com.rbkmoney diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverter.java index 83dfa21..e4fbf0a 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverter.java @@ -1,7 +1,7 @@ package com.rbkmoney.anapi.v2.converter.search.response; +import com.rbkmoney.damsel.domain.InvoicePaymentStatus; import com.rbkmoney.geck.common.util.TypeUtil; -import com.rbkmoney.magista.InvoicePaymentStatus; import com.rbkmoney.magista.StatPayment; import com.rbkmoney.openapi.anapi_v2.model.*; import org.springframework.stereotype.Component; @@ -28,6 +28,9 @@ public class StatPaymentToPaymentSearchResultConverter { .countryGeoID(payment.getLocationInfo().getCountryGeoId()) : null) .status(mapStatus(payment.getStatus())) + .error(payment.getStatus().isSetFailed() + ? new PaymentError().code(payment.getStatus().getFailed().getFailure().getFailure().getCode()) + : null) .statusChangedAt(payment.getStatusChangedAt() != null ? TypeUtil.stringToInstant(payment.getStatusChangedAt()).atOffset(ZoneOffset.UTC) : null) .id(payment.getId()) @@ -62,17 +65,29 @@ public class StatPaymentToPaymentSearchResultConverter { } protected PaymentSearchResult.StatusEnum mapStatus(InvoicePaymentStatus status) { - return switch (status) { - case pending -> PENDING; - case processed -> PROCESSED; - case captured -> CAPTURED; - case cancelled -> CANCELLED; - case refunded -> REFUNDED; - case failed -> FAILED; - case charged_back -> CHARGEDBACK; - default -> throw new IllegalArgumentException( - String.format("Payment status %s cannot be processed", status)); + if (status.isSetPending()) { + return PENDING; + } + if (status.isSetProcessed()) { + return PROCESSED; + } + if (status.isSetCaptured()) { + return CAPTURED; + } + if (status.isSetCancelled()) { + return CANCELLED; + } + if (status.isSetRefunded()) { + return REFUNDED; + } + if (status.isSetFailed()) { + return FAILED; + } + if (status.isSetChargedBack()) { + return CHARGEDBACK; + } + throw new IllegalArgumentException( + String.format("Payment status %s cannot be processed", status)); - }; } } diff --git a/src/test/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverterTest.java b/src/test/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverterTest.java index 2f85bca..846c22c 100644 --- a/src/test/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverterTest.java +++ b/src/test/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverterTest.java @@ -1,9 +1,12 @@ package com.rbkmoney.anapi.v2.converter.search.response; -import com.rbkmoney.damsel.domain.AdditionalTransactionInfo; -import com.rbkmoney.damsel.domain.PaymentResourcePayer; -import com.rbkmoney.damsel.domain.RecurrentPayer; +import com.rbkmoney.damsel.domain.InvoicePaymentStatus; +import com.rbkmoney.damsel.domain.*; import com.rbkmoney.geck.common.util.TypeUtil; +import com.rbkmoney.magista.CustomerPayer; +import com.rbkmoney.magista.InvoicePaymentFlow; +import com.rbkmoney.magista.InvoicePaymentFlowHold; +import com.rbkmoney.magista.Payer; import com.rbkmoney.magista.*; import com.rbkmoney.openapi.anapi_v2.model.PaymentFlow; import com.rbkmoney.openapi.anapi_v2.model.PaymentSearchResult; @@ -14,6 +17,7 @@ import java.time.OffsetDateTime; import static com.rbkmoney.anapi.v2.testutil.MagistaUtil.createSearchPaymentAllResponse; import static com.rbkmoney.anapi.v2.testutil.RandomUtil.randomString; import static com.rbkmoney.openapi.anapi_v2.model.Payer.PayerTypeEnum.*; +import static com.rbkmoney.openapi.anapi_v2.model.PaymentSearchResult.StatusEnum.*; import static org.junit.jupiter.api.Assertions.*; class StatPaymentToPaymentSearchResultConverterTest { @@ -66,8 +70,27 @@ class StatPaymentToPaymentSearchResultConverterTest { @Test void mapStatus() { - for (InvoicePaymentStatus status : InvoicePaymentStatus.values()) { - assertNotNull(converter.mapStatus(status)); - } + var status = InvoicePaymentStatus.pending(new InvoicePaymentPending()); + assertEquals(PENDING, converter.mapStatus(status)); + + status = InvoicePaymentStatus.processed(new InvoicePaymentProcessed()); + assertEquals(PROCESSED, converter.mapStatus(status)); + + status = InvoicePaymentStatus.captured(new InvoicePaymentCaptured()); + assertEquals(CAPTURED, converter.mapStatus(status)); + + status = InvoicePaymentStatus.cancelled(new InvoicePaymentCancelled()); + assertEquals(CANCELLED, converter.mapStatus(status)); + + status = InvoicePaymentStatus.refunded(new InvoicePaymentRefunded()); + assertEquals(REFUNDED, converter.mapStatus(status)); + + status = InvoicePaymentStatus.failed(new InvoicePaymentFailed()); + assertEquals(FAILED, converter.mapStatus(status)); + + status = InvoicePaymentStatus.charged_back(new InvoicePaymentChargedBack()); + assertEquals(CHARGEDBACK, converter.mapStatus(status)); + + assertThrows(IllegalArgumentException.class, () -> converter.mapStatus(new InvoicePaymentStatus())); } } \ No newline at end of file diff --git a/src/test/java/com/rbkmoney/anapi/v2/testutil/MagistaUtil.java b/src/test/java/com/rbkmoney/anapi/v2/testutil/MagistaUtil.java index fcc9481..00b0d14 100644 --- a/src/test/java/com/rbkmoney/anapi/v2/testutil/MagistaUtil.java +++ b/src/test/java/com/rbkmoney/anapi/v2/testutil/MagistaUtil.java @@ -5,6 +5,7 @@ import com.rbkmoney.bouncer.decisions.Judgement; import com.rbkmoney.bouncer.decisions.Resolution; import com.rbkmoney.bouncer.decisions.ResolutionAllowed; import com.rbkmoney.damsel.domain.InvoicePaymentRefundStatus; +import com.rbkmoney.damsel.domain.InvoicePaymentStatus; import com.rbkmoney.damsel.domain.InvoiceStatus; import com.rbkmoney.damsel.domain.*; import com.rbkmoney.damsel.geo_ip.LocationInfo; @@ -14,7 +15,6 @@ import com.rbkmoney.geck.serializer.kit.mock.MockTBaseProcessor; import com.rbkmoney.geck.serializer.kit.tbase.TBaseHandler; import com.rbkmoney.magista.InvoicePaymentFlow; import com.rbkmoney.magista.InvoicePaymentFlowInstant; -import com.rbkmoney.magista.InvoicePaymentStatus; import com.rbkmoney.magista.*; import lombok.SneakyThrows; import lombok.experimental.UtilityClass; @@ -48,6 +48,8 @@ public class MagistaUtil { public static StatPaymentResponse createSearchPaymentAllResponse() { var payment = fillRequiredTBaseObject(new StatPayment(), StatPayment.class); + var status = new InvoicePaymentStatus(); + status.setPending(new InvoicePaymentPending()); var cart = fillRequiredTBaseObject(new InvoiceCart(), InvoiceCart.class); var line = fillRequiredTBaseObject(new InvoiceLine(), InvoiceLine.class); var instant = fillRequiredTBaseObject(new InvoicePaymentFlowInstant(), InvoicePaymentFlowInstant.class); @@ -56,8 +58,7 @@ public class MagistaUtil { return response.setPayments( List.of(payment - .setStatus(InvoicePaymentStatus - .pending) + .setStatus(status) .setCart(cart.setLines(List.of(line))) .setFlow(InvoicePaymentFlow .instant(instant)) From c31b3bb4ce94595c4e27b55611abdcd49e23d4b5 Mon Sep 17 00:00:00 2001 From: echerniak Date: Wed, 13 Oct 2021 10:41:09 +0300 Subject: [PATCH 35/37] Magista-proto updated, shops list creation changed --- pom.xml | 2 +- src/main/java/com/rbkmoney/anapi/v2/security/AccessService.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 846a575..98eed81 100644 --- a/pom.xml +++ b/pom.xml @@ -74,7 +74,7 @@ com.rbkmoney magista-proto - 1.19-50148e1 + 1.20-f95c236 com.rbkmoney diff --git a/src/main/java/com/rbkmoney/anapi/v2/security/AccessService.java b/src/main/java/com/rbkmoney/anapi/v2/security/AccessService.java index 354790e..41d2f64 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/security/AccessService.java +++ b/src/main/java/com/rbkmoney/anapi/v2/security/AccessService.java @@ -33,7 +33,7 @@ public class AccessService { List shopIds = vortigonService.getShopIds(partyId, Objects.requireNonNullElse(realm, "live")); - if (!shopIds.isEmpty()) { + if (!requestShopIds.isEmpty()) { shopIds = requestShopIds.stream() .filter(shopIds::contains) .collect(Collectors.toList()); From 57621627b896dbcf3ad4b1d25c0d397a1f0e20e6 Mon Sep 17 00:00:00 2001 From: echerniak Date: Thu, 14 Oct 2021 13:24:44 +0300 Subject: [PATCH 36/37] Feedback fixes --- .../response/StatChargebackToChargebackConverter.java | 4 ++-- .../search/response/StatInvoiceToInvoiceConverter.java | 2 +- .../response/StatPaymentToPaymentSearchResultConverter.java | 6 +++--- .../search/response/StatPayoutToPayoutConverter.java | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatChargebackToChargebackConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatChargebackToChargebackConverter.java index 4ffa3db..a0cca86 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatChargebackToChargebackConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatChargebackToChargebackConverter.java @@ -20,11 +20,11 @@ public class StatChargebackToChargebackConverter { .createdAt(TypeUtil.stringToInstant(chargeback.getCreatedAt()).atOffset(ZoneOffset.UTC)) .chargebackId(chargeback.getChargebackId()) .fee(chargeback.getFee()) - .chargebackReason(chargeback.getChargebackReason() != null + .chargebackReason(chargeback.isSetChargebackReason() ? new ChargebackReason() .category(mapCategory(chargeback.getChargebackReason().getCategory())) .code(chargeback.getChargebackReason().getCode()) : null) - .content(chargeback.getContent() != null + .content(chargeback.isSetContent() ? new Content().data(chargeback.getContent().getData()) .type(chargeback.getContent().getType()) : null) .bodyCurrency(chargeback.getCurrencyCode().getSymbolicCode()); diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatInvoiceToInvoiceConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatInvoiceToInvoiceConverter.java index 1389bee..6c4f567 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatInvoiceToInvoiceConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatInvoiceToInvoiceConverter.java @@ -23,7 +23,7 @@ public class StatInvoiceToInvoiceConverter { .createdAt(TypeUtil.stringToInstant(invoice.getCreatedAt()).atOffset(ZoneOffset.UTC)) .currency(invoice.getCurrencySymbolicCode()) .externalID(invoice.getExternalId()) - .cart(invoice.getCart() != null + .cart(invoice.isSetCart() ? invoice.getCart().getLines().stream().map(invoiceLine -> new InvoiceLine() .cost(invoiceLine.getQuantity() * invoiceLine.getPrice().getAmount()) .price(invoiceLine.getPrice().getAmount()) diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverter.java index e4fbf0a..8a230ea 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverter.java @@ -23,7 +23,7 @@ public class StatPaymentToPaymentSearchResultConverter { .flow(new PaymentFlow() .type(payment.getFlow().isSetHold() ? PaymentFlow.TypeEnum.PAYMENTFLOWHOLD : PaymentFlow.TypeEnum.PAYMENTFLOWINSTANT)) - .geoLocationInfo(payment.getLocationInfo() != null ? new GeoLocationInfo() + .geoLocationInfo(payment.isSetLocationInfo() ? new GeoLocationInfo() .cityGeoID(payment.getLocationInfo().getCityGeoId()) .countryGeoID(payment.getLocationInfo().getCountryGeoId()) : null) @@ -31,7 +31,7 @@ public class StatPaymentToPaymentSearchResultConverter { .error(payment.getStatus().isSetFailed() ? new PaymentError().code(payment.getStatus().getFailed().getFailure().getFailure().getCode()) : null) - .statusChangedAt(payment.getStatusChangedAt() != null + .statusChangedAt(payment.isSetStatusChangedAt() ? TypeUtil.stringToInstant(payment.getStatusChangedAt()).atOffset(ZoneOffset.UTC) : null) .id(payment.getId()) .invoiceID(payment.getInvoiceId()) @@ -39,7 +39,7 @@ public class StatPaymentToPaymentSearchResultConverter { .payer(mapPayer(payment.getPayer())) .shopID(payment.getShopId()) .shortID(payment.getShortId()) - .transactionInfo(payment.getAdditionalTransactionInfo() != null + .transactionInfo(payment.isSetAdditionalTransactionInfo() ? new TransactionInfo() .approvalCode(payment.getAdditionalTransactionInfo().getApprovalCode()) .rrn(payment.getAdditionalTransactionInfo().getRrn()) diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPayoutToPayoutConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPayoutToPayoutConverter.java index a5e93b1..1273d97 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPayoutToPayoutConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPayoutToPayoutConverter.java @@ -67,7 +67,7 @@ public class StatPayoutToPayoutConverter { return new PayoutToolDetailsInternationalBankAccount() .iban(account.getIban()) .number(account.getNumber()) - .bankDetails(account.getBank() != null + .bankDetails(account.isSetBank() ? new InternationalBankDetails() .name(account.getBank().getName()) .bic(account.getBank().getBic()) @@ -120,7 +120,7 @@ public class StatPayoutToPayoutConverter { : null) .iban(account.getIban()) .number(account.getNumber()) - .correspondentBankAccount(account.getCorrespondentAccount() != null + .correspondentBankAccount(account.isSetCorrespondentAccount() ? mapInternationalCorrespondentBankAccount(account.getCorrespondentAccount()) : null); } From c19e77f2d238e866dfcdacb0820a62e753e4f5e5 Mon Sep 17 00:00:00 2001 From: echerniak Date: Thu, 14 Oct 2021 15:48:42 +0300 Subject: [PATCH 37/37] Feedback edits --- .../StatChargebackToChargebackConverter.java | 30 ++--- .../StatInvoiceToInvoiceConverter.java | 41 +++---- ...PaymentToPaymentSearchResultConverter.java | 62 ++++------ .../response/StatPayoutToPayoutConverter.java | 112 +++++++++--------- ...atRefundToRefundSearchResultConverter.java | 24 ++-- 5 files changed, 123 insertions(+), 146 deletions(-) diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatChargebackToChargebackConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatChargebackToChargebackConverter.java index a0cca86..7fc60cf 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatChargebackToChargebackConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatChargebackToChargebackConverter.java @@ -31,23 +31,19 @@ public class StatChargebackToChargebackConverter { } protected ChargebackCategory mapCategory(InvoicePaymentChargebackCategory chargebackCategory) { - if (chargebackCategory.isSetAuthorisation()) { - return ChargebackCategory.AUTHORISATION; + try { + var field = InvoicePaymentChargebackCategory._Fields.findByName( + chargebackCategory.getSetField().getFieldName()); + return switch (field) { + case FRAUD -> ChargebackCategory.FRAUD; + case DISPUTE -> ChargebackCategory.DISPUTE; + case AUTHORISATION -> ChargebackCategory.AUTHORISATION; + case PROCESSING_ERROR -> ChargebackCategory.PROCESSING_ERROR; + default -> throw new IllegalArgumentException(); + }; + } catch (Exception e) { + throw new IllegalArgumentException( + String.format("Chargeback category %s cannot be processed", chargebackCategory)); } - - if (chargebackCategory.isSetDispute()) { - return ChargebackCategory.DISPUTE; - } - - if (chargebackCategory.isSetFraud()) { - return ChargebackCategory.FRAUD; - } - - if (chargebackCategory.isSetProcessingError()) { - return ChargebackCategory.PROCESSING_ERROR; - } - - throw new IllegalArgumentException( - String.format("Chargeback category %s cannot be processed", chargebackCategory)); } } diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatInvoiceToInvoiceConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatInvoiceToInvoiceConverter.java index 6c4f567..153a55b 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatInvoiceToInvoiceConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatInvoiceToInvoiceConverter.java @@ -41,30 +41,25 @@ public class StatInvoiceToInvoiceConverter { } protected void mapStatusInfo(Invoice invoice, InvoiceStatus status) { - if (status.isSetFulfilled()) { - invoice.setStatus(Invoice.StatusEnum.FULFILLED); - invoice.setReason(status.getFulfilled().getDetails()); - return; + try { + var field = InvoiceStatus._Fields.findByName(status.getSetField().getFieldName()); + switch (field) { + case FULFILLED -> { + invoice.setReason(status.getFulfilled().getDetails()); + invoice.setStatus(Invoice.StatusEnum.FULFILLED); + } + case CANCELLED -> { + invoice.setReason(status.getCancelled().getDetails()); + invoice.setStatus(Invoice.StatusEnum.CANCELLED); + } + case PAID -> invoice.setStatus(Invoice.StatusEnum.PAID); + case UNPAID -> invoice.setStatus(Invoice.StatusEnum.UNPAID); + default -> throw new IllegalArgumentException(); + } + } catch (Exception e) { + throw new IllegalArgumentException( + String.format("Invoice status %s cannot be processed", status)); } - - if (status.isSetPaid()) { - invoice.setStatus(Invoice.StatusEnum.PAID); - return; - } - - if (status.isSetUnpaid()) { - invoice.setStatus(Invoice.StatusEnum.UNPAID); - return; - } - - if (status.isSetCancelled()) { - invoice.setStatus(Invoice.StatusEnum.CANCELLED); - invoice.setReason(status.getCancelled().getDetails()); - return; - } - - throw new IllegalArgumentException( - String.format("Invoice status %s cannot be processed", status)); } protected InvoiceLineTaxMode mapTaxMode(Map metadata) { diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverter.java index 8a230ea..751a604 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPaymentToPaymentSearchResultConverter.java @@ -47,47 +47,37 @@ public class StatPaymentToPaymentSearchResultConverter { } protected Payer mapPayer(com.rbkmoney.magista.Payer payer) { - - if (payer.isSetCustomer()) { - return new Payer().payerType(Payer.PayerTypeEnum.CUSTOMERPAYER); + try { + var field = com.rbkmoney.magista.Payer._Fields.findByName(payer.getSetField().getFieldName()); + return switch (field) { + case CUSTOMER -> new Payer().payerType(Payer.PayerTypeEnum.CUSTOMERPAYER); + case PAYMENT_RESOURCE -> new Payer().payerType(Payer.PayerTypeEnum.PAYMENTRESOURCEPAYER); + case RECURRENT -> new Payer().payerType(Payer.PayerTypeEnum.RECURRENTPAYER); + default -> throw new IllegalArgumentException(); + }; + } catch (Exception e) { + throw new IllegalArgumentException( + String.format("Payer %s cannot be processed", payer)); } - - if (payer.isSetPaymentResource()) { - return new Payer().payerType(Payer.PayerTypeEnum.PAYMENTRESOURCEPAYER); - } - - if (payer.isSetRecurrent()) { - return new Payer().payerType(Payer.PayerTypeEnum.RECURRENTPAYER); - } - - throw new IllegalArgumentException( - String.format("Payer %s cannot be processed", payer)); } protected PaymentSearchResult.StatusEnum mapStatus(InvoicePaymentStatus status) { - if (status.isSetPending()) { - return PENDING; + try { + var field = InvoicePaymentStatus._Fields.findByName(status.getSetField().getFieldName()); + return switch (field) { + case PENDING -> PENDING; + case PROCESSED -> PROCESSED; + case CAPTURED -> CAPTURED; + case CANCELLED -> CANCELLED; + case REFUNDED -> REFUNDED; + case FAILED -> FAILED; + case CHARGED_BACK -> CHARGEDBACK; + default -> throw new IllegalArgumentException(); + }; + } catch (Exception e) { + throw new IllegalArgumentException( + String.format("Payment status %s cannot be processed", status)); } - if (status.isSetProcessed()) { - return PROCESSED; - } - if (status.isSetCaptured()) { - return CAPTURED; - } - if (status.isSetCancelled()) { - return CANCELLED; - } - if (status.isSetRefunded()) { - return REFUNDED; - } - if (status.isSetFailed()) { - return FAILED; - } - if (status.isSetChargedBack()) { - return CHARGEDBACK; - } - throw new IllegalArgumentException( - String.format("Payment status %s cannot be processed", status)); } } diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPayoutToPayoutConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPayoutToPayoutConverter.java index 1273d97..354ab3b 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPayoutToPayoutConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatPayoutToPayoutConverter.java @@ -31,69 +31,67 @@ public class StatPayoutToPayoutConverter { } protected String mapStatus(PayoutStatus status) { - if (status.isSetCancelled()) { - return "Cancelled"; + try { + var field = PayoutStatus._Fields.findByName(status.getSetField().getFieldName()); + return switch (field) { + case UNPAID -> "Unpaid"; + case PAID -> "Paid"; + case CANCELLED -> "Cancelled"; + case CONFIRMED -> "Confirmed"; + default -> throw new IllegalArgumentException(); + }; + } catch (Exception e) { + throw new IllegalArgumentException( + String.format("Payout status %s cannot be processed", status)); } - - if (status.isSetPaid()) { - return "Paid"; - } - - if (status.isSetConfirmed()) { - return "Confirmed"; - } - - if (status.isSetUnpaid()) { - return "Unpaid"; - } - - throw new IllegalArgumentException( - String.format("Payout status %s cannot be processed", status)); } protected PayoutToolDetails mapPayoutToolDetails(PayoutToolInfo payoutToolInfo) { - if (payoutToolInfo.isSetRussianBankAccount()) { - var account = payoutToolInfo.getRussianBankAccount(); - return new PayoutToolDetailsBankAccount() - .account(account.getAccount()) - .bankBik(account.getBankBik()) - .bankName(account.getBankName()) - .bankPostAccount(account.getBankPostAccount()) - .detailsType("PayoutToolDetailsBankAccount"); + try { + var field = PayoutToolInfo._Fields.findByName(payoutToolInfo.getSetField().getFieldName()); + switch (field) { + case RUSSIAN_BANK_ACCOUNT -> { + var account = payoutToolInfo.getRussianBankAccount(); + return new PayoutToolDetailsBankAccount() + .account(account.getAccount()) + .bankBik(account.getBankBik()) + .bankName(account.getBankName()) + .bankPostAccount(account.getBankPostAccount()) + .detailsType("PayoutToolDetailsBankAccount"); + } + case INTERNATIONAL_BANK_ACCOUNT -> { + var account = payoutToolInfo.getInternationalBankAccount(); + return new PayoutToolDetailsInternationalBankAccount() + .iban(account.getIban()) + .number(account.getNumber()) + .bankDetails(account.isSetBank() + ? new InternationalBankDetails() + .name(account.getBank().getName()) + .bic(account.getBank().getBic()) + .countryCode(mapCountryCode(account.getBank().getCountry())) + .address(account.getBank().getAddress()) + .abartn(account.getBank().getAbaRtn()) + : null) + .correspondentBankAccount( + mapInternationalCorrespondentBankAccount(account.getCorrespondentAccount())) + .detailsType("PayoutToolDetailsInternationalBankAccount"); + } + case WALLET_INFO -> { + return new PayoutToolDetailsWalletInfo() + .walletID(payoutToolInfo.getWalletInfo().getWalletId()) + .detailsType("PayoutToolDetailsWalletInfo"); + } + case PAYMENT_INSTITUTION_ACCOUNT -> { + return new PayoutToolDetailsPaymentInstitutionAccount() + .detailsType("PayoutToolDetailsPaymentInstitutionAccount"); + } + default -> throw new IllegalArgumentException(); + } + } catch (Exception e) { + throw new IllegalArgumentException( + String.format("PayoutToolInfo %s cannot be processed", payoutToolInfo)); } - if (payoutToolInfo.isSetInternationalBankAccount()) { - var account = payoutToolInfo.getInternationalBankAccount(); - return new PayoutToolDetailsInternationalBankAccount() - .iban(account.getIban()) - .number(account.getNumber()) - .bankDetails(account.isSetBank() - ? new InternationalBankDetails() - .name(account.getBank().getName()) - .bic(account.getBank().getBic()) - .countryCode(mapCountryCode(account.getBank().getCountry())) - .address(account.getBank().getAddress()) - .abartn(account.getBank().getAbaRtn()) - : null) - .correspondentBankAccount( - mapInternationalCorrespondentBankAccount(account.getCorrespondentAccount())) - .detailsType("PayoutToolDetailsInternationalBankAccount"); - } - - if (payoutToolInfo.isSetPaymentInstitutionAccount()) { - return new PayoutToolDetailsPaymentInstitutionAccount() - .detailsType("PayoutToolDetailsPaymentInstitutionAccount"); - } - - if (payoutToolInfo.isSetWalletInfo()) { - return new PayoutToolDetailsWalletInfo() - .walletID(payoutToolInfo.getWalletInfo().getWalletId()) - .detailsType("PayoutToolDetailsWalletInfo"); - } - - throw new IllegalArgumentException( - String.format("PayoutToolInfo %s cannot be processed", payoutToolInfo)); - } protected String mapCountryCode(@Nullable CountryCode countryCode) { diff --git a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatRefundToRefundSearchResultConverter.java b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatRefundToRefundSearchResultConverter.java index 030c7af..0861403 100644 --- a/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatRefundToRefundSearchResultConverter.java +++ b/src/main/java/com/rbkmoney/anapi/v2/converter/search/response/StatRefundToRefundSearchResultConverter.java @@ -39,19 +39,17 @@ public class StatRefundToRefundSearchResultConverter { } protected RefundSearchResult.StatusEnum mapStatus(InvoicePaymentRefundStatus status) { - if (status.isSetPending()) { - return RefundSearchResult.StatusEnum.PENDING; + try { + var field = InvoicePaymentRefundStatus._Fields.findByName(status.getSetField().getFieldName()); + return switch (field) { + case PENDING -> RefundSearchResult.StatusEnum.PENDING; + case SUCCEEDED -> RefundSearchResult.StatusEnum.SUCCEEDED; + case FAILED -> RefundSearchResult.StatusEnum.FAILED; + default -> throw new IllegalArgumentException(); + }; + } catch (Exception e) { + throw new IllegalArgumentException( + String.format("Refund status %s cannot be processed", status)); } - - if (status.isSetFailed()) { - return RefundSearchResult.StatusEnum.FAILED; - } - - if (status.isSetSucceeded()) { - return RefundSearchResult.StatusEnum.SUCCEEDED; - } - - throw new IllegalArgumentException( - String.format("Refund status %s cannot be processed", status)); } }