diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index 5cbf745..06c4260 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -4,6 +4,8 @@ on:
push:
branches:
- 'master'
+ - 'main'
+ - 'epic/**'
jobs:
build-and-deploy:
diff --git a/README.md b/README.md
index f7177ed..2d0dc00 100644
--- a/README.md
+++ b/README.md
@@ -115,15 +115,15 @@
- Перед переводом диспута в финальный статус саппорт должен будет забиндить айди созданного диспута в провайдере через
ручку `BindCreated()`. Здесь особенность, что этот метод фильтрует возможность биндить диспуты только созданные
- вручную (из `manual_parsing_created`)
+ вручную (из `manual_created`)
Далее, в режиме ручного разбора есть опция финализации диспута в фейл (`CancelPending()`) либо в
успех (`ApprovePending()`). Здесь особенность, что в фейл можно перевести любой диспут имеющий не финальный статус, а в
успех можно перевести, только если гарантировано создан внешний диспут у провайдера (
-из `pending`,`manual_parsing_binded_pending`)
+из `pending`,`manual_pending`)
- Из за того, что для ручных диспутов добавлены отдельные
- статусы `manual_parsing_binded_pending` ,`manual_parsing_created` не происходит ситуации, что такие диспуты попадут в
+ статусы `manual_pending` ,`manual_created` не происходит ситуации, что такие диспуты попадут в
таску `PendingDisputesService` которая автоматически вызывает апи провайдера для проверки статуса
# Схема аппрува корректировок
diff --git a/pom.xml b/pom.xml
index 677b135..250b0ab 100644
--- a/pom.xml
+++ b/pom.xml
@@ -47,7 +47,7 @@
dev.vality
provider-disputes-proto
- 1.16-61eff7c
+ 1.18-6602d79
dev.vality
@@ -89,10 +89,6 @@
-
- org.springframework.boot
- spring-boot-starter-aop
-
org.springframework.boot
spring-boot-starter-jdbc
@@ -193,6 +189,10 @@
3.0.2
provided
+
+ com.fasterxml.jackson.core
+ jackson-databind
+
org.openapitools
jackson-databind-nullable
diff --git a/src/main/java/dev/vality/disputes/DisputesApiApplication.java b/src/main/java/dev/vality/disputes/DisputesApiApplication.java
index 6dbf7c0..ae298a3 100644
--- a/src/main/java/dev/vality/disputes/DisputesApiApplication.java
+++ b/src/main/java/dev/vality/disputes/DisputesApiApplication.java
@@ -4,8 +4,10 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.annotation.EnableScheduling;
@EnableAsync
+@EnableScheduling
@ServletComponentScan
@SpringBootApplication(scanBasePackages = {"dev.vality.disputes", "dev.vality.swag"})
public class DisputesApiApplication extends SpringApplication {
diff --git a/src/main/java/dev/vality/disputes/api/converter/Status200ResponseConverter.java b/src/main/java/dev/vality/disputes/api/converter/Status200ResponseConverter.java
index 5eb0754..694d3e8 100644
--- a/src/main/java/dev/vality/disputes/api/converter/Status200ResponseConverter.java
+++ b/src/main/java/dev/vality/disputes/api/converter/Status200ResponseConverter.java
@@ -23,7 +23,7 @@ public class Status200ResponseConverter {
private Status200Response.StatusEnum getStatus(Dispute dispute) {
return switch (dispute.getStatus()) {
- case created, pending, manual_created, manual_pending, create_adjustment ->
+ case created, pending, manual_created, manual_pending, create_adjustment, already_exist_created ->
Status200Response.StatusEnum.PENDING;
case succeeded -> Status200Response.StatusEnum.SUCCEEDED;
case cancelled, failed -> Status200Response.StatusEnum.FAILED;
diff --git a/src/main/java/dev/vality/disputes/api/model/PaymentParams.java b/src/main/java/dev/vality/disputes/api/model/PaymentParams.java
index cafca86..ca9e3aa 100644
--- a/src/main/java/dev/vality/disputes/api/model/PaymentParams.java
+++ b/src/main/java/dev/vality/disputes/api/model/PaymentParams.java
@@ -1,15 +1,13 @@
package dev.vality.disputes.api.model;
import lombok.Builder;
-import lombok.Getter;
-import lombok.Setter;
+import lombok.Data;
import lombok.ToString;
import java.util.Map;
@Builder
-@Getter
-@Setter
+@Data
public class PaymentParams {
private String invoiceId;
diff --git a/src/main/java/dev/vality/disputes/api/service/ApiAttachmentsService.java b/src/main/java/dev/vality/disputes/api/service/ApiAttachmentsService.java
index 64c19f2..386c2f1 100644
--- a/src/main/java/dev/vality/disputes/api/service/ApiAttachmentsService.java
+++ b/src/main/java/dev/vality/disputes/api/service/ApiAttachmentsService.java
@@ -4,7 +4,6 @@ import dev.vality.disputes.dao.FileMetaDao;
import dev.vality.disputes.domain.tables.pojos.FileMeta;
import dev.vality.disputes.service.external.FileStorageService;
import dev.vality.swag.disputes.model.CreateRequest;
-import dev.vality.swag.disputes.model.CreateRequestAttachmentsInner;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
@@ -24,13 +23,13 @@ public class ApiAttachmentsService {
@Transactional(propagation = Propagation.REQUIRED)
public void createAttachments(CreateRequest req, Long disputeId) {
log.debug("Trying to save Attachments {}", disputeId);
- for (CreateRequestAttachmentsInner attachment : req.getAttachments()) {
+ for (var attachment : req.getAttachments()) {
// validate
MediaType.valueOf(attachment.getMimeType());
// http 500
var fileId = fileStorageService.saveFile(attachment.getData());
var fileMeta = new FileMeta(fileId, disputeId, attachment.getMimeType());
- log.debug("Trying to save Attachment {}", fileMeta.getFileId());
+ log.debug("Trying to save Attachment {}", fileMeta);
// http 500
fileMetaDao.save(fileMeta);
}
diff --git a/src/main/java/dev/vality/disputes/api/service/ApiDisputesService.java b/src/main/java/dev/vality/disputes/api/service/ApiDisputesService.java
index 68c72a4..a200593 100644
--- a/src/main/java/dev/vality/disputes/api/service/ApiDisputesService.java
+++ b/src/main/java/dev/vality/disputes/api/service/ApiDisputesService.java
@@ -74,6 +74,7 @@ public class ApiDisputesService {
DisputeStatus.pending,
DisputeStatus.manual_created,
DisputeStatus.manual_pending,
- DisputeStatus.create_adjustment);
+ DisputeStatus.create_adjustment,
+ DisputeStatus.already_exist_created);
}
}
diff --git a/src/main/java/dev/vality/disputes/api/service/PaymentParamsBuilder.java b/src/main/java/dev/vality/disputes/api/service/PaymentParamsBuilder.java
index defedce..9cbf28b 100644
--- a/src/main/java/dev/vality/disputes/api/service/PaymentParamsBuilder.java
+++ b/src/main/java/dev/vality/disputes/api/service/PaymentParamsBuilder.java
@@ -5,20 +5,21 @@ import dev.vality.damsel.domain.TransactionInfo;
import dev.vality.damsel.payment_processing.InvoicePayment;
import dev.vality.disputes.api.model.PaymentParams;
import dev.vality.disputes.security.AccessData;
-import dev.vality.disputes.service.external.DominantService;
+import dev.vality.disputes.service.external.impl.dominant.DominantAsyncService;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
@Slf4j
@Service
@RequiredArgsConstructor
public class PaymentParamsBuilder {
- private final DominantService dominantService;
+ private final DominantAsyncService dominantAsyncService;
@SneakyThrows
public PaymentParams buildGeneralPaymentContext(AccessData accessData) {
@@ -26,28 +27,42 @@ public class PaymentParamsBuilder {
log.debug("Start building PaymentParams id={}", invoice.getInvoice().getId());
var payment = accessData.getPayment();
// http 500
- var terminal = dominantService.getTerminal(payment.getRoute().getTerminal());
+ var terminal = dominantAsyncService.getTerminal(payment.getRoute().getTerminal());
var currency = Optional.of(payment)
.filter(p -> p.getPayment().isSetCost())
.map(p -> p.getPayment().getCost())
// http 500
- .map(cost -> dominantService.getCurrency(cost.getCurrency()));
+ .map(cost -> dominantAsyncService.getCurrency(cost.getCurrency()));
var paymentParams = PaymentParams.builder()
.invoiceId(invoice.getInvoice().getId())
.paymentId(payment.getPayment().getId())
.terminalId(payment.getRoute().getTerminal().getId())
.providerId(payment.getRoute().getProvider().getId())
.providerTrxId(getProviderTrxId(payment))
- .currencyName(currency.map(Currency::getName).orElse(null))
- .currencySymbolicCode(currency.map(Currency::getSymbolicCode).orElse(null))
- .currencyNumericCode(currency.map(Currency::getNumericCode).map(Short::intValue).orElse(null))
- .currencyExponent(currency.map(Currency::getExponent).map(Short::intValue).orElse(null))
+ .currencyName(getCurrency(currency)
+ .map(Currency::getName).orElse(null))
+ .currencySymbolicCode(getCurrency(currency)
+ .map(Currency::getSymbolicCode).orElse(null))
+ .currencyNumericCode(getCurrency(currency)
+ .map(Currency::getNumericCode).map(Short::intValue).orElse(null))
+ .currencyExponent(getCurrency(currency)
+ .map(Currency::getExponent).map(Short::intValue).orElse(null))
.options(terminal.get().getOptions())
.build();
log.debug("Finish building PaymentParams {}", paymentParams);
return paymentParams;
}
+ private Optional getCurrency(Optional> currency) {
+ return currency.map(currencyCompletableFuture -> {
+ try {
+ return currencyCompletableFuture.get();
+ } catch (Throwable e) {
+ throw new RuntimeException(e);
+ }
+ });
+ }
+
private String getProviderTrxId(InvoicePayment payment) {
return Optional.ofNullable(payment.getLastTransactionInfo())
.map(TransactionInfo::getId)
diff --git a/src/main/java/dev/vality/disputes/config/ApplicationConfig.java b/src/main/java/dev/vality/disputes/config/ApplicationConfig.java
index 46417f9..c01b3b1 100644
--- a/src/main/java/dev/vality/disputes/config/ApplicationConfig.java
+++ b/src/main/java/dev/vality/disputes/config/ApplicationConfig.java
@@ -7,8 +7,6 @@ import dev.vality.damsel.payment_processing.InvoicingSrv;
import dev.vality.file.storage.FileStorageSrv;
import dev.vality.token.keeper.TokenAuthenticatorSrv;
import dev.vality.woody.thrift.impl.http.THSpawnClientBuilder;
-import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
-import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -71,11 +69,6 @@ public class ApplicationConfig {
.build(FileStorageSrv.Iface.class);
}
- @Bean
- public CloseableHttpClient httpClient() {
- return HttpClients.createDefault();
- }
-
@Bean
public ExecutorService disputesThreadPool(@Value("${dispute.batchSize}") int threadPoolSize) {
final var threadFactory = new ThreadFactoryBuilder()
diff --git a/src/main/java/dev/vality/disputes/config/AsyncMDCConfiguration.java b/src/main/java/dev/vality/disputes/config/AsyncMDCConfiguration.java
index 34c4b86..4c767cf 100644
--- a/src/main/java/dev/vality/disputes/config/AsyncMDCConfiguration.java
+++ b/src/main/java/dev/vality/disputes/config/AsyncMDCConfiguration.java
@@ -11,11 +11,12 @@ import java.util.concurrent.Executor;
@SuppressWarnings("AbbreviationAsWordInName")
public class AsyncMDCConfiguration {
- @Bean
- public Executor asyncExecutor() {
- ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+ @Bean("dominantAsyncServiceExecutor")
+ public Executor dominantAsyncServiceExecutor() {
+ var executor = new ThreadPoolTaskExecutor();
executor.setTaskDecorator(new MDCTaskDecorator());
executor.initialize();
+ executor.setThreadNamePrefix("dominantAsyncService-thread-");
return executor;
}
}
diff --git a/src/main/java/dev/vality/disputes/config/HttpClientConfig.java b/src/main/java/dev/vality/disputes/config/HttpClientConfig.java
new file mode 100644
index 0000000..13a739c
--- /dev/null
+++ b/src/main/java/dev/vality/disputes/config/HttpClientConfig.java
@@ -0,0 +1,73 @@
+package dev.vality.disputes.config;
+
+import dev.vality.disputes.config.properties.HttpClientProperties;
+import lombok.RequiredArgsConstructor;
+import lombok.SneakyThrows;
+import org.apache.hc.client5.http.config.ConnectionConfig;
+import org.apache.hc.client5.http.config.RequestConfig;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.HttpClients;
+import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
+import org.apache.hc.client5.http.socket.ConnectionSocketFactory;
+import org.apache.hc.client5.http.socket.PlainConnectionSocketFactory;
+import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
+import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
+import org.apache.hc.client5.http.ssl.TrustAllStrategy;
+import org.apache.hc.core5.http.config.Registry;
+import org.apache.hc.core5.http.config.RegistryBuilder;
+import org.apache.hc.core5.ssl.SSLContextBuilder;
+import org.apache.hc.core5.util.Timeout;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+@RequiredArgsConstructor
+public class HttpClientConfig {
+
+ private final HttpClientProperties httpClientProperties;
+
+ @Bean
+ public CloseableHttpClient httpClient(
+ PoolingHttpClientConnectionManager manager,
+ RequestConfig requestConfig) {
+ return HttpClients.custom()
+ .setConnectionManager(manager)
+ .setDefaultRequestConfig(requestConfig)
+ .disableAutomaticRetries()
+ .setConnectionManagerShared(true)
+ .build();
+ }
+
+ @Bean
+ public PoolingHttpClientConnectionManager poolingHttpClientConnectionManager() {
+ var connectionManager = new PoolingHttpClientConnectionManager(connectionSocketFactory());
+ connectionManager.setMaxTotal(httpClientProperties.getMaxTotalPooling());
+ connectionManager.setDefaultMaxPerRoute(httpClientProperties.getDefaultMaxPerRoute());
+ connectionManager.setDefaultConnectionConfig(connectionConfig(httpClientProperties));
+ return connectionManager;
+ }
+
+ @Bean
+ public RequestConfig requestConfig() {
+ return RequestConfig.custom()
+ .setConnectionRequestTimeout(Timeout.ofMilliseconds(httpClientProperties.getPoolTimeout()))
+ .build();
+ }
+
+ @SneakyThrows
+ private Registry connectionSocketFactory() {
+ var sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustAllStrategy()).build();
+ var sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
+ return RegistryBuilder.create()
+ .register("https", sslConnectionSocketFactory)
+ .register("http", new PlainConnectionSocketFactory())
+ .build();
+ }
+
+ private ConnectionConfig connectionConfig(HttpClientProperties httpClientProperties) {
+ return ConnectionConfig.custom()
+ .setConnectTimeout(Timeout.ofMilliseconds(httpClientProperties.getConnectionTimeout()))
+ .setSocketTimeout(Timeout.ofMilliseconds(httpClientProperties.getRequestTimeout()))
+ .build();
+ }
+}
diff --git a/src/main/java/dev/vality/disputes/config/properties/HttpClientProperties.java b/src/main/java/dev/vality/disputes/config/properties/HttpClientProperties.java
new file mode 100644
index 0000000..c55a6d8
--- /dev/null
+++ b/src/main/java/dev/vality/disputes/config/properties/HttpClientProperties.java
@@ -0,0 +1,28 @@
+package dev.vality.disputes.config.properties;
+
+import jakarta.validation.constraints.NotNull;
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.validation.annotation.Validated;
+
+@Getter
+@Setter
+@Validated
+@Configuration
+@ConfigurationProperties("http-client")
+public class HttpClientProperties {
+
+ @NotNull
+ private int maxTotalPooling;
+ @NotNull
+ private int defaultMaxPerRoute;
+ @NotNull
+ private int requestTimeout;
+ @NotNull
+ private int poolTimeout;
+ @NotNull
+ private int connectionTimeout;
+
+}
diff --git a/src/main/java/dev/vality/disputes/dao/DisputeDao.java b/src/main/java/dev/vality/disputes/dao/DisputeDao.java
index fa1a39e..a31f156 100644
--- a/src/main/java/dev/vality/disputes/dao/DisputeDao.java
+++ b/src/main/java/dev/vality/disputes/dao/DisputeDao.java
@@ -62,15 +62,18 @@ public class DisputeDao extends AbstractGenericDao {
.orElse(List.of());
}
- public Dispute getForUpdateSkipLocked(long disputeId) {
+ public Optional get(long disputeId) {
+ var query = getDslContext().selectFrom(DISPUTE)
+ .where(DISPUTE.ID.eq(disputeId));
+ return Optional.ofNullable(fetchOne(query, disputeRowMapper));
+ }
+
+ public Optional getForUpdateSkipLocked(long disputeId) {
var query = getDslContext().selectFrom(DISPUTE)
.where(DISPUTE.ID.eq(disputeId))
.forUpdate()
.skipLocked();
- return Optional.ofNullable(fetchOne(query, disputeRowMapper))
- .orElseThrow(
- () -> new NotFoundException(
- String.format("Dispute not found, disputeId='%s'", disputeId)));
+ return Optional.ofNullable(fetchOne(query, disputeRowMapper));
}
public List getDisputesForUpdateSkipLocked(int limit, DisputeStatus disputeStatus) {
diff --git a/src/main/java/dev/vality/disputes/manualparsing/DebugManualParsingController.java b/src/main/java/dev/vality/disputes/manualparsing/DebugManualParsingController.java
new file mode 100644
index 0000000..e68e205
--- /dev/null
+++ b/src/main/java/dev/vality/disputes/manualparsing/DebugManualParsingController.java
@@ -0,0 +1,128 @@
+package dev.vality.disputes.manualparsing;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import dev.vality.disputes.admin.*;
+import lombok.Data;
+import lombok.RequiredArgsConstructor;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+
+import java.io.IOException;
+import java.util.Base64;
+import java.util.List;
+
+@RestController
+@RequiredArgsConstructor
+@RequestMapping({"/debug/disputes-api/manual-parsing"})
+@Slf4j
+public class DebugManualParsingController {
+
+ private final ManualParsingServiceSrv.Iface manualParsingHandler;
+ private final ObjectMapper objectMapper = new ObjectMapper();
+
+ @PostMapping("/cancel")
+ @SneakyThrows
+ public void cancelPending(@RequestBody String body) {
+ log.debug("cancelPending {}", body);
+ manualParsingHandler.cancelPending(objectMapper.readValue(body, CancelParamsRequest.class));
+ }
+
+ @PostMapping("/approve")
+ @SneakyThrows
+ public void approvePending(@RequestBody String body) {
+ log.debug("approvePending {}", body);
+ manualParsingHandler.approvePending(objectMapper.readValue(body, ApproveParamsRequest.class));
+ }
+
+ @PostMapping("/bind")
+ @SneakyThrows
+ public void bindCreated(@RequestBody String body) {
+ log.debug("bindCreated {}", body);
+ manualParsingHandler.bindCreated(objectMapper.readValue(body, BindParamsRequest.class));
+ }
+
+ @GetMapping("/get")
+ @SneakyThrows
+ public DisputeResult getDisputes(@RequestBody String body) {
+ log.debug("getDispute {}", body);
+ var dispute = manualParsingHandler.getDispute(objectMapper.readValue(body, DisputeParamsRequest.class));
+ return objectMapper.convertValue(dispute, new TypeReference<>() {
+ });
+ }
+
+ @Data
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ @JsonInclude(JsonInclude.Include.NON_NULL)
+ public static class DisputeResult {
+
+ @JsonProperty("disputes")
+ private List disputes;
+ }
+
+ @Data
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ @JsonInclude(JsonInclude.Include.NON_NULL)
+ public static class Dispute {
+
+ @JsonProperty("disputeId")
+ private String disputeId; // required
+ @JsonProperty("providerDisputeId")
+ private String providerDisputeId; // optional
+ @JsonProperty("invoiceId")
+ private String invoiceId; // required
+ @JsonProperty("paymentId")
+ private String paymentId; // required
+ @JsonProperty("providerTrxId")
+ private String providerTrxId; // required
+ @JsonProperty("status")
+ private String status; // required
+ @JsonProperty("errorMessage")
+ private String errorMessage; // optional
+ @JsonProperty("amount")
+ private String amount; // required
+ @JsonProperty("changedAmount")
+ private String changedAmount; // optional
+ @JsonProperty("skipCallHgForCreateAdjustment")
+ private boolean skipCallHgForCreateAdjustment; // required
+ @JsonProperty("attachments")
+ @JsonDeserialize(using = AttachmentDeserializer.class)
+ public List attachments;
+ }
+
+ @Data
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ @JsonInclude(JsonInclude.Include.NON_NULL)
+ public static class Attachment {
+
+ private String data;
+
+ }
+
+ public static class AttachmentDeserializer extends JsonDeserializer {
+
+ private final ObjectMapper mapper = new ObjectMapper();
+
+ @Override
+ public Attachment deserialize(JsonParser parser, DeserializationContext ctxt) throws IOException {
+ var node = (JsonNode) parser.getCodec().readTree(parser);
+ if (node.isObject()) {
+ var attachment = mapper.convertValue(node, new TypeReference() {
+ });
+ var attachmentResult = new Attachment();
+ attachmentResult.setData(Base64.getEncoder().encodeToString(attachment.getData()));
+ return attachmentResult;
+ }
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/dev/vality/disputes/manualparsing/ManualParsingDisputesService.java b/src/main/java/dev/vality/disputes/manualparsing/ManualParsingDisputesService.java
index 7421072..fd90a7f 100644
--- a/src/main/java/dev/vality/disputes/manualparsing/ManualParsingDisputesService.java
+++ b/src/main/java/dev/vality/disputes/manualparsing/ManualParsingDisputesService.java
@@ -1,19 +1,30 @@
package dev.vality.disputes.manualparsing;
-import dev.vality.disputes.admin.ApproveParams;
-import dev.vality.disputes.admin.BindParams;
-import dev.vality.disputes.admin.CancelParams;
+import dev.vality.disputes.admin.*;
import dev.vality.disputes.dao.DisputeDao;
+import dev.vality.disputes.dao.FileMetaDao;
import dev.vality.disputes.dao.ProviderDisputeDao;
import dev.vality.disputes.domain.enums.DisputeStatus;
import dev.vality.disputes.domain.tables.pojos.ProviderDispute;
+import dev.vality.disputes.service.external.FileStorageService;
import lombok.RequiredArgsConstructor;
+import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
+import org.apache.hc.client5.http.classic.methods.HttpGet;
+import org.apache.hc.client5.http.impl.classic.AbstractHttpClientResponseHandler;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.core5.http.HttpEntity;
+import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Optional;
+
import static dev.vality.disputes.api.service.ApiDisputesService.DISPUTE_PENDING;
@Slf4j
@@ -24,13 +35,20 @@ public class ManualParsingDisputesService {
private final DisputeDao disputeDao;
private final ProviderDisputeDao providerDisputeDao;
+ private final FileMetaDao fileMetaDao;
+ private final FileStorageService fileStorageService;
+ private final CloseableHttpClient httpClient;
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.REPEATABLE_READ)
public void cancelPendingDispute(CancelParams cancelParams) {
var disputeId = cancelParams.getDisputeId();
- var cancelReason = cancelParams.getCancelReason().orElse(null);
log.debug("Trying to getForUpdateSkipLocked {}", disputeId);
- var dispute = disputeDao.getForUpdateSkipLocked(Long.parseLong(disputeId));
+ var disputeOptional = disputeDao.getForUpdateSkipLocked(Long.parseLong(disputeId));
+ if (disputeOptional.isEmpty()) {
+ return;
+ }
+ var cancelReason = cancelParams.getCancelReason().orElse(null);
+ var dispute = disputeOptional.get();
log.debug("GetForUpdateSkipLocked has been found {}", dispute);
if (DISPUTE_PENDING.contains(dispute.getStatus())) {
// используется не failed, а cancelled чтоб можно было понять, что зафейлен по внешнему вызову
@@ -46,7 +64,11 @@ public class ManualParsingDisputesService {
public void approvePendingDispute(ApproveParams approveParam) {
var disputeId = approveParam.getDisputeId();
log.debug("Trying to getForUpdateSkipLocked {}", disputeId);
- var dispute = disputeDao.getForUpdateSkipLocked(Long.parseLong(disputeId));
+ var disputeOptional = disputeDao.getForUpdateSkipLocked(Long.parseLong(disputeId));
+ if (disputeOptional.isEmpty()) {
+ return;
+ }
+ var dispute = disputeOptional.get();
log.debug("GetForUpdateSkipLocked has been found {}", dispute);
var skipCallHg = approveParam.isSkipCallHgForCreateAdjustment();
var targetStatus = skipCallHg ? DisputeStatus.succeeded : DisputeStatus.create_adjustment;
@@ -70,19 +92,73 @@ public class ManualParsingDisputesService {
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.REPEATABLE_READ)
public void bindCreatedDispute(BindParams bindParam) {
var disputeId = bindParam.getDisputeId();
- var providerDisputeId = bindParam.getProviderDisputeId();
log.debug("Trying to getForUpdateSkipLocked {}", disputeId);
- var dispute = disputeDao.getForUpdateSkipLocked(Long.parseLong(disputeId));
+ var disputeOptional = disputeDao.getForUpdateSkipLocked(Long.parseLong(disputeId));
+ if (disputeOptional.isEmpty()) {
+ return;
+ }
+ var providerDisputeId = bindParam.getProviderDisputeId();
+ var dispute = disputeOptional.get();
log.debug("GetForUpdateSkipLocked has been found {}", dispute);
if (dispute.getStatus() == DisputeStatus.manual_created) {
// обрабатываем здесь только вручную созданные диспуты, у остальных предполагается,
// что providerDisputeId будет сохранен после создания диспута по API провайдера
- log.info("Trying to set manual_parsing_binded_pending Dispute status {}", dispute);
+ log.info("Trying to set manual_pending Dispute status {}", dispute);
providerDisputeDao.save(new ProviderDispute(providerDisputeId, dispute.getId()));
disputeDao.update(dispute.getId(), DisputeStatus.manual_pending);
- log.debug("Dispute status has been set to manual_parsing_binded_pending {}", dispute);
+ log.debug("Dispute status has been set to manual_pending {}", dispute);
+ } else if (dispute.getStatus() == DisputeStatus.already_exist_created) {
+ log.info("Trying to set pending Dispute status {}", dispute);
+ providerDisputeDao.save(new ProviderDispute(providerDisputeId, dispute.getId()));
+ disputeDao.update(dispute.getId(), DisputeStatus.pending);
+ log.debug("Dispute status has been set to pending {}", dispute);
} else {
log.info("Request was skipped by inappropriate status {}", dispute);
}
}
+
+ @SneakyThrows
+ public Dispute getDispute(DisputeParams disputeParams) {
+ var disputeId = disputeParams.getDisputeId();
+ var disputeOptional = disputeDao.get(Long.parseLong(disputeId));
+ if (disputeOptional.isEmpty()) {
+ log.debug("Trying to get Dispute but null {}", disputeId);
+ return null;
+ }
+ var dispute = disputeOptional.get();
+ var disputeResult = new Dispute();
+ disputeResult.setDisputeId(disputeId);
+ disputeResult.setProviderDisputeId(Optional.ofNullable(providerDisputeDao.get(dispute.getId()))
+ .map(ProviderDispute::getProviderDisputeId)
+ .orElse(null));
+ disputeResult.setInvoiceId(dispute.getInvoiceId());
+ disputeResult.setPaymentId(dispute.getPaymentId());
+ disputeResult.setProviderTrxId(dispute.getProviderTrxId());
+ disputeResult.setStatus(dispute.getStatus().name());
+ disputeResult.setErrorMessage(dispute.getErrorMessage());
+ disputeResult.setAmount(String.valueOf(dispute.getAmount()));
+ disputeResult.setChangedAmount(Optional.ofNullable(dispute.getChangedAmount())
+ .map(String::valueOf)
+ .orElse(null));
+ disputeResult.setSkipCallHgForCreateAdjustment(dispute.getSkipCallHgForCreateAdjustment());
+ log.debug("Dispute getDispute {}", disputeResult);
+ var disputeFiles = fileMetaDao.getDisputeFiles(dispute.getId());
+ if (disputeFiles == null) {
+ return disputeResult;
+ }
+ disputeResult.setAttachments(new ArrayList<>());
+ for (var disputeFile : disputeFiles) {
+ var downloadUrl = fileStorageService.generateDownloadUrl(disputeFile.getFileId());
+ var data = httpClient.execute(
+ new HttpGet(downloadUrl),
+ new AbstractHttpClientResponseHandler() {
+ @Override
+ public byte[] handleEntity(HttpEntity entity) throws IOException {
+ return EntityUtils.toByteArray(entity);
+ }
+ });
+ disputeResult.getAttachments().get().add(new Attachment(ByteBuffer.wrap(data)));
+ }
+ return disputeResult;
+ }
}
diff --git a/src/main/java/dev/vality/disputes/manualparsing/ManualParsingHandler.java b/src/main/java/dev/vality/disputes/manualparsing/ManualParsingHandler.java
index c6d58a5..36f704e 100644
--- a/src/main/java/dev/vality/disputes/manualparsing/ManualParsingHandler.java
+++ b/src/main/java/dev/vality/disputes/manualparsing/ManualParsingHandler.java
@@ -6,6 +6,8 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.thrift.TException;
import org.springframework.stereotype.Service;
+import java.util.ArrayList;
+
@Service
@RequiredArgsConstructor
@Slf4j
@@ -30,8 +32,20 @@ public class ManualParsingHandler implements ManualParsingServiceSrv.Iface {
@Override
public void bindCreated(BindParamsRequest bindParamsRequest) throws TException {
- for (BindParams bindParam : bindParamsRequest.getBindParams()) {
+ for (var bindParam : bindParamsRequest.getBindParams()) {
manualParsingDisputesService.bindCreatedDispute(bindParam);
}
}
+
+ @Override
+ public DisputeResult getDispute(DisputeParamsRequest disputeParamsRequest) throws TException {
+ var disputeResult = new DisputeResult(new ArrayList<>());
+ for (var disputeParams : disputeParamsRequest.getDisputeParams()) {
+ var dispute = manualParsingDisputesService.getDispute(disputeParams);
+ if (dispute != null) {
+ disputeResult.getDisputes().add(dispute);
+ }
+ }
+ return disputeResult;
+ }
}
diff --git a/src/main/java/dev/vality/disputes/manualparsing/ManualParsingTopic.java b/src/main/java/dev/vality/disputes/manualparsing/ManualParsingTopic.java
index b17b313..19de97f 100644
--- a/src/main/java/dev/vality/disputes/manualparsing/ManualParsingTopic.java
+++ b/src/main/java/dev/vality/disputes/manualparsing/ManualParsingTopic.java
@@ -8,8 +8,6 @@ import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Propagation;
-import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.stream.Collectors;
@@ -23,8 +21,7 @@ public class ManualParsingTopic {
@Value("${manual-parsing-topic.enabled}")
private boolean enabled;
- @Transactional(propagation = Propagation.REQUIRED)
- public void sendCreated(Dispute dispute, List attachments) {
+ public void sendCreated(Dispute dispute, List attachments, DisputeStatus disputeStatus) {
if (!enabled) {
return;
}
@@ -32,7 +29,7 @@ public class ManualParsingTopic {
contextMap.put("dispute_id", dispute.getId().toString());
var attachmentsCollect = attachments.stream().map(Attachment::toString).collect(Collectors.joining(", "));
contextMap.put("dispute_attachments", attachmentsCollect);
- contextMap.put("dispute_status", DisputeStatus.manual_created.name());
+ contextMap.put("dispute_status", disputeStatus.name());
MDC.setContextMap(contextMap);
log.warn("Manual parsing case");
MDC.clear();
diff --git a/src/main/java/dev/vality/disputes/polling/ExponentialBackOffPollingServiceWrapper.java b/src/main/java/dev/vality/disputes/polling/ExponentialBackOffPollingServiceWrapper.java
index 8bea930..ecb52da 100644
--- a/src/main/java/dev/vality/disputes/polling/ExponentialBackOffPollingServiceWrapper.java
+++ b/src/main/java/dev/vality/disputes/polling/ExponentialBackOffPollingServiceWrapper.java
@@ -6,16 +6,12 @@ import dev.vality.damsel.domain.Terminal;
import dev.vality.damsel.domain.TerminalRef;
import dev.vality.disputes.domain.tables.pojos.Dispute;
import dev.vality.disputes.service.external.DominantService;
-import lombok.SneakyThrows;
import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Propagation;
-import org.springframework.transaction.annotation.Transactional;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Map;
-import java.util.concurrent.CompletableFuture;
@Service
public class ExponentialBackOffPollingServiceWrapper {
@@ -33,20 +29,17 @@ public class ExponentialBackOffPollingServiceWrapper {
return getLocalDateTime(pollingInfo.getStartDateTimePolling().plusSeconds(seconds));
}
- @Transactional(propagation = Propagation.REQUIRED)
- @SneakyThrows
public LocalDateTime prepareNextPollingInterval(Dispute dispute) {
var pollingInfo = new PollingInfo();
var startDateTimePolling = dispute.getCreatedAt().toInstant(ZoneOffset.UTC);
pollingInfo.setStartDateTimePolling(startDateTimePolling);
pollingInfo.setMaxDateTimePolling(dispute.getPollingBefore().toInstant(ZoneOffset.UTC));
var terminal = getTerminal(dispute.getTerminalId());
- var seconds = exponentialBackOffPollingService.prepareNextPollingInterval(
- pollingInfo, terminal.get().getOptions());
+ var seconds = exponentialBackOffPollingService.prepareNextPollingInterval(pollingInfo, terminal.getOptions());
return getLocalDateTime(startDateTimePolling.plusSeconds(seconds));
}
- private CompletableFuture getTerminal(Integer terminalId) {
+ private Terminal getTerminal(Integer terminalId) {
return dominantService.getTerminal(new TerminalRef(terminalId));
}
diff --git a/src/main/java/dev/vality/disputes/schedule/TaskCreateAdjustmentsService.java b/src/main/java/dev/vality/disputes/schedule/TaskCreateAdjustmentsService.java
index 73de3f7..633a2a6 100644
--- a/src/main/java/dev/vality/disputes/schedule/TaskCreateAdjustmentsService.java
+++ b/src/main/java/dev/vality/disputes/schedule/TaskCreateAdjustmentsService.java
@@ -16,6 +16,7 @@ import java.util.stream.Collectors;
@Slf4j
@Service
@RequiredArgsConstructor
+@SuppressWarnings({"ParameterName", "LineLength", "MissingSwitchDefault"})
public class TaskCreateAdjustmentsService {
private final ExecutorService disputesThreadPool;
@@ -25,12 +26,12 @@ public class TaskCreateAdjustmentsService {
@Value("${dispute.isScheduleCreateAdjustmentsEnabled}")
private boolean isScheduleCreateAdjustmentsEnabled;
- @Scheduled(fixedDelayString = "${dispute.fixedDelayCreateAdjustments}")
+ @Scheduled(fixedDelayString = "${dispute.fixedDelayCreateAdjustments}", initialDelayString = "${dispute.initialDelayCreateAdjustments}")
public void processPending() {
if (!isScheduleCreateAdjustmentsEnabled) {
return;
}
- log.info("Processing create adjustments get started");
+ log.debug("Processing create adjustments get started");
try {
var disputes = createAdjustmentsService.getDisputesForHgCall(batchSize);
var callables = disputes.stream()
@@ -40,7 +41,7 @@ public class TaskCreateAdjustmentsService {
} catch (InterruptedException ex) {
log.error("Received InterruptedException while thread executed report", ex);
Thread.currentThread().interrupt();
- } catch (Exception ex) {
+ } catch (Throwable ex) {
log.error("Received exception while scheduler processed create adjustments", ex);
}
log.info("Create adjustments were processed");
diff --git a/src/main/java/dev/vality/disputes/schedule/TaskCreatedDisputesService.java b/src/main/java/dev/vality/disputes/schedule/TaskCreatedDisputesService.java
index 9760d7e..45351ce 100644
--- a/src/main/java/dev/vality/disputes/schedule/TaskCreatedDisputesService.java
+++ b/src/main/java/dev/vality/disputes/schedule/TaskCreatedDisputesService.java
@@ -25,12 +25,12 @@ public class TaskCreatedDisputesService {
@Value("${dispute.isScheduleCreatedEnabled}")
private boolean isScheduleCreatedEnabled;
- @Scheduled(fixedDelayString = "${dispute.fixedDelayCreated}")
+ @Scheduled(fixedDelayString = "${dispute.fixedDelayCreated}", initialDelayString = "${dispute.initialDelayCreated}")
public void processCreated() {
if (!isScheduleCreatedEnabled) {
return;
}
- log.info("Processing created disputes get started");
+ log.debug("Processing created disputes get started");
try {
var disputes = createdDisputesService.getCreatedDisputesForUpdateSkipLocked(batchSize);
var callables = disputes.stream()
@@ -40,7 +40,7 @@ public class TaskCreatedDisputesService {
} catch (InterruptedException ex) {
log.error("Received InterruptedException while thread executed report", ex);
Thread.currentThread().interrupt();
- } catch (Exception ex) {
+ } catch (Throwable ex) {
log.error("Received exception while scheduler processed created disputes", ex);
}
log.info("Created disputes were processed");
diff --git a/src/main/java/dev/vality/disputes/schedule/TaskPendingDisputesService.java b/src/main/java/dev/vality/disputes/schedule/TaskPendingDisputesService.java
index f43674f..0042f17 100644
--- a/src/main/java/dev/vality/disputes/schedule/TaskPendingDisputesService.java
+++ b/src/main/java/dev/vality/disputes/schedule/TaskPendingDisputesService.java
@@ -25,12 +25,12 @@ public class TaskPendingDisputesService {
@Value("${dispute.isSchedulePendingEnabled}")
private boolean isSchedulePendingEnabled;
- @Scheduled(fixedDelayString = "${dispute.fixedDelayPending}")
+ @Scheduled(fixedDelayString = "${dispute.fixedDelayPending}", initialDelayString = "${dispute.initialDelayPending}")
public void processPending() {
if (!isSchedulePendingEnabled) {
return;
}
- log.info("Processing pending disputes get started");
+ log.debug("Processing pending disputes get started");
try {
var disputes = pendingDisputesService.getPendingDisputesForUpdateSkipLocked(batchSize);
var callables = disputes.stream()
@@ -40,7 +40,7 @@ public class TaskPendingDisputesService {
} catch (InterruptedException ex) {
log.error("Received InterruptedException while thread executed report", ex);
Thread.currentThread().interrupt();
- } catch (Exception ex) {
+ } catch (Throwable ex) {
log.error("Received exception while scheduler processed pending disputes", ex);
}
log.info("Pending disputes were processed");
diff --git a/src/main/java/dev/vality/disputes/schedule/client/RemoteClient.java b/src/main/java/dev/vality/disputes/schedule/client/RemoteClient.java
index 1bebbf2..fa998a4 100644
--- a/src/main/java/dev/vality/disputes/schedule/client/RemoteClient.java
+++ b/src/main/java/dev/vality/disputes/schedule/client/RemoteClient.java
@@ -17,11 +17,8 @@ import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Propagation;
-import org.springframework.transaction.annotation.Transactional;
import java.util.List;
-import java.util.concurrent.CompletableFuture;
@Slf4j
@Service
@@ -34,37 +31,42 @@ public class RemoteClient {
private final DisputeContextConverter disputeContextConverter;
private final DisputeParamsConverter disputeParamsConverter;
- @Transactional(propagation = Propagation.REQUIRED)
@SneakyThrows
public DisputeCreatedResult createDispute(Dispute dispute, List attachments) {
+ log.debug("Trying to call dominant for RemoteClient {}", dispute.getId());
var terminal = getTerminal(dispute.getTerminalId());
var proxy = getProxy(dispute.getProviderId());
- var disputeParams = disputeParamsConverter.convert(dispute, attachments, terminal.get().getOptions());
- var remoteClient = providerIfaceBuilder.build(terminal.get().getOptions(), proxy.get().getUrl());
- log.info("Trying to routed remote provider's createDispute() call {}", dispute);
+ log.debug("Trying to build disputeParams {}", dispute.getId());
+ var disputeParams = disputeParamsConverter.convert(dispute, attachments, terminal.getOptions());
+ log.debug("Trying to call ProviderIfaceBuilder {}", dispute.getId());
+ var remoteClient = providerIfaceBuilder.buildTHSpawnClient(terminal.getOptions(), proxy.getUrl());
+ log.debug("Trying to routed remote provider's createDispute() call {}", dispute.getId());
var result = remoteClient.createDispute(disputeParams);
- log.debug("Routed remote provider's createDispute() has been called {}", dispute);
+ log.info("Routed remote provider's createDispute() has been called {} {}", dispute.getId(), result);
return result;
}
- @Transactional(propagation = Propagation.REQUIRED)
@SneakyThrows
public DisputeStatusResult checkDisputeStatus(Dispute dispute, ProviderDispute providerDispute) {
+ log.debug("Trying to call dominant for RemoteClient {}", dispute.getId());
var terminal = getTerminal(dispute.getTerminalId());
var proxy = getProxy(dispute.getProviderId());
- var disputeContext = disputeContextConverter.convert(dispute, providerDispute, terminal.get().getOptions());
- var remoteClient = providerIfaceBuilder.build(terminal.get().getOptions(), proxy.get().getUrl());
- log.info("Trying to routed remote provider's checkDisputeStatus() call {}", dispute);
+ log.debug("Trying to build disputeContext {}", dispute.getId());
+ var disputeContext = disputeContextConverter.convert(dispute, providerDispute, terminal.getOptions());
+ log.debug("Trying to call ProviderIfaceBuilder {}", dispute.getId());
+ var remoteClient = providerIfaceBuilder.buildTHSpawnClient(terminal.getOptions(), proxy.getUrl());
+ log.debug("Trying to routed remote provider's checkDisputeStatus() call {}", dispute.getId());
var result = remoteClient.checkDisputeStatus(disputeContext);
- log.debug("Routed remote provider's checkDisputeStatus() has been called {}", dispute);
+ log.info("Routed remote provider's checkDisputeStatus() has been called {} {}", dispute.getId(), result);
return result;
}
- private CompletableFuture getProxy(Integer providerId) {
- return dominantService.getProxy(new ProviderRef(providerId));
+ private ProxyDefinition getProxy(Integer providerId) {
+ var provider = dominantService.getProvider(new ProviderRef(providerId));
+ return dominantService.getProxy(provider.getProxy().getRef());
}
- private CompletableFuture getTerminal(Integer terminalId) {
+ private Terminal getTerminal(Integer terminalId) {
return dominantService.getTerminal(new TerminalRef(terminalId));
}
}
diff --git a/src/main/java/dev/vality/disputes/schedule/handler/CreateAdjustmentHandler.java b/src/main/java/dev/vality/disputes/schedule/handler/CreateAdjustmentHandler.java
index 84842c2..15c3095 100644
--- a/src/main/java/dev/vality/disputes/schedule/handler/CreateAdjustmentHandler.java
+++ b/src/main/java/dev/vality/disputes/schedule/handler/CreateAdjustmentHandler.java
@@ -3,8 +3,10 @@ package dev.vality.disputes.schedule.handler;
import dev.vality.disputes.domain.tables.pojos.Dispute;
import dev.vality.disputes.schedule.service.CreateAdjustmentsService;
import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
@RequiredArgsConstructor
+@Slf4j
public class CreateAdjustmentHandler {
private final CreateAdjustmentsService createAdjustmentsService;
@@ -12,10 +14,13 @@ public class CreateAdjustmentHandler {
public Long handle(Dispute dispute) {
final var currentThread = Thread.currentThread();
final var oldName = currentThread.getName();
- currentThread.setName("dispute-create-adjustment-" + dispute.getId());
+ currentThread.setName("dispute-created-adjustment-id-" + dispute.getId() + "-" + oldName);
try {
createAdjustmentsService.callHgForCreateAdjustment(dispute);
return dispute.getId();
+ } catch (Throwable ex) {
+ log.error("Received exception while scheduler processed callHgForCreateAdjustment", ex);
+ throw ex;
} finally {
currentThread.setName(oldName);
}
diff --git a/src/main/java/dev/vality/disputes/schedule/handler/CreatedDisputeHandler.java b/src/main/java/dev/vality/disputes/schedule/handler/CreatedDisputeHandler.java
index 2538d44..4f185a0 100644
--- a/src/main/java/dev/vality/disputes/schedule/handler/CreatedDisputeHandler.java
+++ b/src/main/java/dev/vality/disputes/schedule/handler/CreatedDisputeHandler.java
@@ -3,8 +3,10 @@ package dev.vality.disputes.schedule.handler;
import dev.vality.disputes.domain.tables.pojos.Dispute;
import dev.vality.disputes.schedule.service.CreatedDisputesService;
import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
@RequiredArgsConstructor
+@Slf4j
public class CreatedDisputeHandler {
private final CreatedDisputesService createdDisputesService;
@@ -12,10 +14,13 @@ public class CreatedDisputeHandler {
public Long handle(Dispute dispute) {
final var currentThread = Thread.currentThread();
final var oldName = currentThread.getName();
- currentThread.setName("dispute-created-" + dispute.getId());
+ currentThread.setName("dispute-created-id-" + dispute.getId() + "-" + oldName);
try {
createdDisputesService.callCreateDisputeRemotely(dispute);
return dispute.getId();
+ } catch (Throwable ex) {
+ log.error("Received exception while scheduler processed callCreateDisputeRemotely", ex);
+ throw ex;
} finally {
currentThread.setName(oldName);
}
diff --git a/src/main/java/dev/vality/disputes/schedule/handler/PendingDisputeHandler.java b/src/main/java/dev/vality/disputes/schedule/handler/PendingDisputeHandler.java
index 4e76d37..dabd1c6 100644
--- a/src/main/java/dev/vality/disputes/schedule/handler/PendingDisputeHandler.java
+++ b/src/main/java/dev/vality/disputes/schedule/handler/PendingDisputeHandler.java
@@ -3,8 +3,10 @@ package dev.vality.disputes.schedule.handler;
import dev.vality.disputes.domain.tables.pojos.Dispute;
import dev.vality.disputes.schedule.service.PendingDisputesService;
import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
@RequiredArgsConstructor
+@Slf4j
public class PendingDisputeHandler {
private final PendingDisputesService pendingDisputesService;
@@ -12,10 +14,13 @@ public class PendingDisputeHandler {
public Long handle(Dispute dispute) {
final var currentThread = Thread.currentThread();
final var oldName = currentThread.getName();
- currentThread.setName("dispute-pending-" + dispute.getId());
+ currentThread.setName("dispute-pending-id-" + dispute.getId() + "-" + oldName);
try {
pendingDisputesService.callPendingDisputeRemotely(dispute);
return dispute.getId();
+ } catch (Throwable ex) {
+ log.error("Received exception while scheduler processed callPendingDisputeRemotely", ex);
+ throw ex;
} finally {
currentThread.setName(oldName);
}
diff --git a/src/main/java/dev/vality/disputes/schedule/service/CreateAdjustmentsService.java b/src/main/java/dev/vality/disputes/schedule/service/CreateAdjustmentsService.java
index 910f534..ab9a1ff 100644
--- a/src/main/java/dev/vality/disputes/schedule/service/CreateAdjustmentsService.java
+++ b/src/main/java/dev/vality/disputes/schedule/service/CreateAdjustmentsService.java
@@ -49,9 +49,9 @@ public class CreateAdjustmentsService {
log.debug("GetDisputeForUpdateSkipLocked has been found {}", dispute);
var invoicePayment = getInvoicePayment(dispute);
if (invoicePayment == null || !invoicePayment.isSetRoute()) {
- log.error("Trying to set failed Dispute status with PAYMENT_NOT_FOUND error reason {}", dispute);
+ log.error("Trying to set failed Dispute status with PAYMENT_NOT_FOUND error reason {}", dispute.getId());
disputeDao.update(dispute.getId(), DisputeStatus.failed, ErrorReason.PAYMENT_NOT_FOUND);
- log.debug("Dispute status has been set to failed {}", dispute);
+ log.debug("Dispute status has been set to failed {}", dispute.getId());
return;
}
var invoicePaymentAdjustment = adjustmentExtractor.searchAdjustmentByDispute(invoicePayment, dispute);
@@ -59,16 +59,16 @@ public class CreateAdjustmentsService {
var changedAmount = adjustmentExtractor.getChangedAmount(invoicePaymentAdjustment.get(), dispute.getChangedAmount());
log.info("Trying to set succeeded Dispute status {}", dispute);
disputeDao.update(dispute.getId(), DisputeStatus.succeeded, changedAmount);
- log.debug("Dispute status has been set to succeeded {}", dispute);
+ log.debug("Dispute status has been set to succeeded {}", dispute.getId());
return;
}
try {
var params = invoicePaymentAdjustmentParamsConverter.convert(dispute);
var paymentAdjustment = createAdjustment(dispute, params);
if (paymentAdjustment == null) {
- log.error("Trying to set failed Dispute status with INVOICE_NOT_FOUND error reason {}", dispute);
+ log.error("Trying to set failed Dispute status with INVOICE_NOT_FOUND error reason {}", dispute.getId());
disputeDao.update(dispute.getId(), DisputeStatus.failed, ErrorReason.INVOICE_NOT_FOUND);
- log.debug("Dispute status has been set to failed {}", dispute);
+ log.debug("Dispute status has been set to failed {}", dispute.getId());
return;
}
} catch (InvoicingPaymentStatusPendingException e) {
@@ -77,12 +77,12 @@ public class CreateAdjustmentsService {
// и тогда диспут будет пулиться, пока платеж не зафиналится,
// и тк никакой записи в коде выше нет, то пуллинг не проблема
// а запрос в checkDisputeStatus по идемпотентности просто вернет тот же success
- log.error("Error when hg.createPaymentAdjustment() got payments status pending {}", dispute, e);
+ log.error("Error when hg.createPaymentAdjustment() got payments status pending {}", dispute.getId(), e);
return;
}
log.info("Trying to set succeeded Dispute status {}", dispute);
disputeDao.update(dispute.getId(), DisputeStatus.succeeded);
- log.debug("Dispute status has been set to succeeded {}", dispute);
+ log.debug("Dispute status has been set to succeeded {}", dispute.getId());
}
private InvoicePaymentAdjustment createAdjustment(Dispute dispute, InvoicePaymentAdjustmentParams params) {
diff --git a/src/main/java/dev/vality/disputes/schedule/service/CreatedAttachmentsService.java b/src/main/java/dev/vality/disputes/schedule/service/CreatedAttachmentsService.java
index 2c2538b..f711762 100644
--- a/src/main/java/dev/vality/disputes/schedule/service/CreatedAttachmentsService.java
+++ b/src/main/java/dev/vality/disputes/schedule/service/CreatedAttachmentsService.java
@@ -9,8 +9,6 @@ import dev.vality.disputes.service.external.FileStorageService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Propagation;
-import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
@@ -23,7 +21,6 @@ public class CreatedAttachmentsService {
private final FileMetaDao fileMetaDao;
private final FileStorageService fileStorageService;
- @Transactional(propagation = Propagation.REQUIRED)
public List getAttachments(Dispute dispute) {
log.debug("Trying to get Attachments {}", dispute);
try {
diff --git a/src/main/java/dev/vality/disputes/schedule/service/CreatedDisputesService.java b/src/main/java/dev/vality/disputes/schedule/service/CreatedDisputesService.java
index cb3bf95..bf2f219 100644
--- a/src/main/java/dev/vality/disputes/schedule/service/CreatedDisputesService.java
+++ b/src/main/java/dev/vality/disputes/schedule/service/CreatedDisputesService.java
@@ -19,7 +19,6 @@ import dev.vality.disputes.service.external.InvoicingService;
import dev.vality.geck.serializer.kit.tbase.TErrorUtil;
import dev.vality.woody.api.flow.error.WRuntimeException;
import lombok.RequiredArgsConstructor;
-import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
@@ -27,7 +26,6 @@ import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
-import java.util.concurrent.CompletableFuture;
import static dev.vality.disputes.constant.TerminalOptionsField.DISPUTE_FLOW_CAPTURED_BLOCKED;
import static dev.vality.disputes.constant.TerminalOptionsField.DISPUTE_FLOW_PROVIDERS_API_EXIST;
@@ -57,7 +55,6 @@ public class CreatedDisputesService {
}
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.REPEATABLE_READ)
- @SneakyThrows
public void callCreateDisputeRemotely(Dispute dispute) {
log.debug("Trying to getDisputeForUpdateSkipLocked {}", dispute);
var forUpdate = disputeDao.getDisputeForUpdateSkipLocked(dispute.getId());
@@ -68,39 +65,39 @@ public class CreatedDisputesService {
log.debug("GetDisputeForUpdateSkipLocked has been found {}", dispute);
var invoicePayment = getInvoicePayment(dispute);
if (invoicePayment == null || !invoicePayment.isSetRoute()) {
- log.error("Trying to set failed Dispute status with PAYMENT_NOT_FOUND error reason {}", dispute);
+ log.error("Trying to set failed Dispute status with PAYMENT_NOT_FOUND error reason {}", dispute.getId());
disputeDao.update(dispute.getId(), DisputeStatus.failed, ErrorReason.PAYMENT_NOT_FOUND);
- log.debug("Dispute status has been set to failed {}", dispute);
+ log.debug("Dispute status has been set to failed {}", dispute.getId());
return;
}
var status = invoicePayment.getPayment().getStatus();
if (!status.isSetCaptured() && !status.isSetCancelled() && !status.isSetFailed()) {
// не создаем диспут, пока платеж не финален
- log.warn("Payment has non-final status {} {}", status, dispute);
+ log.warn("Payment has non-final status {} {}", status, dispute.getId());
return;
}
var attachments = createdAttachmentsService.getAttachments(dispute);
if (attachments == null || attachments.isEmpty()) {
- log.error("Trying to set failed Dispute status with NO_ATTACHMENTS error reason {}", dispute);
+ log.error("Trying to set failed Dispute status with NO_ATTACHMENTS error reason {}", dispute.getId());
disputeDao.update(dispute.getId(), DisputeStatus.failed, ErrorReason.NO_ATTACHMENTS);
- log.debug("Dispute status has been set to failed {}", dispute);
+ log.debug("Dispute status has been set to failed {}", dispute.getId());
return;
}
if ((status.isSetCaptured() && isCapturedBlockedForDispute(dispute))
|| isNotProvidersDisputesApiExist(dispute)) {
// отправлять на ручной разбор, если выставлена опция
// DISPUTE_FLOW_CAPTURED_BLOCKED или не выставлена DISPUTE_FLOW_PROVIDERS_API_EXIST
- finishTaskWithManualParsingFlowActivation(dispute, attachments);
+ finishTaskWithManualParsingFlowActivation(dispute, attachments, DisputeStatus.manual_created);
return;
}
try {
var result = remoteClient.createDispute(dispute, attachments);
- finishTask(dispute, result);
+ finishTask(dispute, attachments, result);
} catch (WRuntimeException e) {
if (externalGatewayChecker.isNotProvidersDisputesApiExist(dispute, e)) {
// отправлять на ручной разбор, если API диспутов на провайдере не реализовано
// (тогда при тесте соединения вернется 404)
- finishTaskWithManualParsingFlowActivation(dispute, attachments);
+ finishTaskWithManualParsingFlowActivation(dispute, attachments, DisputeStatus.manual_created);
return;
}
throw e;
@@ -108,41 +105,41 @@ public class CreatedDisputesService {
}
@Transactional(propagation = Propagation.REQUIRED)
- void finishTask(Dispute dispute, DisputeCreatedResult result) {
+ void finishTask(Dispute dispute, List attachments, DisputeCreatedResult result) {
switch (result.getSetField()) {
case SUCCESS_RESULT -> {
var nextCheckAfter = exponentialBackOffPollingService.prepareNextPollingInterval(dispute);
log.info("Trying to set pending Dispute status {}, {}", dispute, result);
providerDisputeDao.save(new ProviderDispute(result.getSuccessResult().getProviderDisputeId(), dispute.getId()));
disputeDao.update(dispute.getId(), DisputeStatus.pending, nextCheckAfter);
- log.debug("Dispute status has been set to pending {}", dispute);
+ log.debug("Dispute status has been set to pending {}", dispute.getId());
}
case FAIL_RESULT -> {
var errorMessage = TErrorUtil.toStringVal(result.getFailResult().getFailure());
- log.warn("Trying to set failed Dispute status {}, {}", dispute, errorMessage);
+ log.warn("Trying to set failed Dispute status {}, {}", dispute.getId(), errorMessage);
disputeDao.update(dispute.getId(), DisputeStatus.failed, errorMessage);
- log.debug("Dispute status has been set to failed {}", dispute);
+ log.debug("Dispute status has been set to failed {}", dispute.getId());
}
+ case ALREADY_EXIST_RESULT ->
+ finishTaskWithManualParsingFlowActivation(dispute, attachments, DisputeStatus.already_exist_created);
}
}
@Transactional(propagation = Propagation.REQUIRED)
- void finishTaskWithManualParsingFlowActivation(Dispute dispute, List attachments) {
- manualParsingTopic.sendCreated(dispute, attachments);
- log.info("Trying to set manual_parsing_created Dispute status {}", dispute);
- disputeDao.update(dispute.getId(), DisputeStatus.manual_created);
- log.debug("Dispute status has been set to manual_parsing_created {}", dispute);
+ void finishTaskWithManualParsingFlowActivation(Dispute dispute, List attachments, DisputeStatus disputeStatus) {
+ manualParsingTopic.sendCreated(dispute, attachments, disputeStatus);
+ log.info("Trying to set {} Dispute status {}", disputeStatus, dispute);
+ disputeDao.update(dispute.getId(), disputeStatus);
+ log.debug("Dispute status has been set to {} {}", disputeStatus, dispute.getId());
}
- @SneakyThrows
private boolean isCapturedBlockedForDispute(Dispute dispute) {
- return getTerminal(dispute.getTerminalId()).get().getOptions()
+ return getTerminal(dispute.getTerminalId()).getOptions()
.containsKey(DISPUTE_FLOW_CAPTURED_BLOCKED);
}
- @SneakyThrows
private boolean isNotProvidersDisputesApiExist(Dispute dispute) {
- return !getTerminal(dispute.getTerminalId()).get().getOptions()
+ return !getTerminal(dispute.getTerminalId()).getOptions()
.containsKey(DISPUTE_FLOW_PROVIDERS_API_EXIST);
}
@@ -150,7 +147,7 @@ public class CreatedDisputesService {
return invoicingService.getInvoicePayment(dispute.getInvoiceId(), dispute.getPaymentId());
}
- private CompletableFuture getTerminal(Integer terminalId) {
+ private Terminal getTerminal(Integer terminalId) {
return dominantService.getTerminal(new TerminalRef(terminalId));
}
}
diff --git a/src/main/java/dev/vality/disputes/schedule/service/ExternalGatewayChecker.java b/src/main/java/dev/vality/disputes/schedule/service/ExternalGatewayChecker.java
index 9b3fb83..3c5e348 100644
--- a/src/main/java/dev/vality/disputes/schedule/service/ExternalGatewayChecker.java
+++ b/src/main/java/dev/vality/disputes/schedule/service/ExternalGatewayChecker.java
@@ -18,8 +18,6 @@ import org.apache.hc.core5.http.HttpStatus;
import org.apache.hc.core5.http.io.HttpClientResponseHandler;
import org.springframework.stereotype.Service;
-import java.util.concurrent.CompletableFuture;
-
@Slf4j
@Service
@RequiredArgsConstructor
@@ -43,20 +41,20 @@ public class ExternalGatewayChecker {
return httpClient.execute(new HttpGet(getRouteUrl(dispute)), isNotFoundResponse());
}
- @SneakyThrows
private String getRouteUrl(Dispute dispute) {
- return providerRouting.getRouteUrl(getTerminal(dispute.getTerminalId()).get().getOptions(), getProxy(dispute.getProviderId()).get().getUrl());
+ return providerRouting.getRouteUrl(getTerminal(dispute.getTerminalId()).getOptions(), getProxy(dispute.getProviderId()).getUrl());
}
private HttpClientResponseHandler isNotFoundResponse() {
return response -> response.getCode() == HttpStatus.SC_NOT_FOUND;
}
- private CompletableFuture getTerminal(Integer terminalId) {
+ private Terminal getTerminal(Integer terminalId) {
return dominantService.getTerminal(new TerminalRef(terminalId));
}
- private CompletableFuture getProxy(Integer providerId) {
- return dominantService.getProxy(new ProviderRef(providerId));
+ private ProxyDefinition getProxy(Integer providerId) {
+ var provider = dominantService.getProvider(new ProviderRef(providerId));
+ return dominantService.getProxy(provider.getProxy().getRef());
}
}
diff --git a/src/main/java/dev/vality/disputes/schedule/service/PendingDisputesService.java b/src/main/java/dev/vality/disputes/schedule/service/PendingDisputesService.java
index 7d96c0c..9736446 100644
--- a/src/main/java/dev/vality/disputes/schedule/service/PendingDisputesService.java
+++ b/src/main/java/dev/vality/disputes/schedule/service/PendingDisputesService.java
@@ -48,23 +48,23 @@ public class PendingDisputesService {
return;
}
log.debug("GetDisputeForUpdateSkipLocked has been found {}", dispute);
- log.debug("Trying to get ProviderDispute {}", dispute);
+ log.debug("Trying to get ProviderDispute {}", dispute.getId());
var providerDispute = providerDisputeDao.get(dispute.getId());
if (providerDispute == null) {
var nextCheckAfter = exponentialBackOffPollingService.prepareNextPollingInterval(dispute);
// вернуть в CreatedDisputeService и попробовать создать диспут в провайдере заново
- log.error("Trying to set created Dispute status, because createDispute() was not success {}", dispute);
+ log.error("Trying to set created Dispute status, because createDispute() was not success {}", dispute.getId());
disputeDao.update(dispute.getId(), DisputeStatus.created, nextCheckAfter);
- log.debug("Dispute status has been set to created {}", dispute);
+ log.debug("Dispute status has been set to created {}", dispute.getId());
return;
}
if (pollingInfoService.isDeadline(dispute)) {
- log.error("Trying to set failed Dispute status with POOLING_EXPIRED error reason {}", dispute);
+ log.error("Trying to set failed Dispute status with POOLING_EXPIRED error reason {}", dispute.getId());
disputeDao.update(dispute.getId(), DisputeStatus.failed, ErrorReason.POOLING_EXPIRED);
- log.debug("Dispute status has been set to failed {}", dispute);
+ log.debug("Dispute status has been set to failed {}", dispute.getId());
return;
}
- log.debug("ProviderDispute has been found {}", dispute);
+ log.debug("ProviderDispute has been found {}", dispute.getId());
var result = remoteClient.checkDisputeStatus(dispute, providerDispute);
finishTask(dispute, result);
}
@@ -76,13 +76,13 @@ public class PendingDisputesService {
var changedAmount = result.getStatusSuccess().getChangedAmount().orElse(null);
log.info("Trying to set create_adjustment Dispute status {}, {}", dispute, result);
disputeDao.update(dispute.getId(), DisputeStatus.create_adjustment, changedAmount);
- log.debug("Dispute status has been set to create_adjustment {}", dispute);
+ log.debug("Dispute status has been set to create_adjustment {}", dispute.getId());
}
case STATUS_FAIL -> {
var errorMessage = TErrorUtil.toStringVal(result.getStatusFail().getFailure());
- log.warn("Trying to set failed Dispute status {}, {}", dispute, errorMessage);
+ log.warn("Trying to set failed Dispute status {}, {}", dispute.getId(), errorMessage);
disputeDao.update(dispute.getId(), DisputeStatus.failed, errorMessage);
- log.debug("Dispute status has been set to failed {}", dispute);
+ log.debug("Dispute status has been set to failed {}", dispute.getId());
}
case STATUS_PENDING -> {
// дергаем update() чтоб обновить время вызова next_check_after,
@@ -91,7 +91,7 @@ public class PendingDisputesService {
var nextCheckAfter = exponentialBackOffPollingService.prepareNextPollingInterval(dispute);
log.info("Trying to set pending Dispute status {}, {}", dispute, result);
disputeDao.update(dispute.getId(), DisputeStatus.pending, nextCheckAfter);
- log.debug("Dispute status has been set to pending {}", dispute);
+ log.debug("Dispute status has been set to pending {}", dispute.getId());
}
}
}
diff --git a/src/main/java/dev/vality/disputes/schedule/service/ProviderIfaceBuilder.java b/src/main/java/dev/vality/disputes/schedule/service/ProviderIfaceBuilder.java
index d6e4ad6..752e9c3 100644
--- a/src/main/java/dev/vality/disputes/schedule/service/ProviderIfaceBuilder.java
+++ b/src/main/java/dev/vality/disputes/schedule/service/ProviderIfaceBuilder.java
@@ -5,6 +5,7 @@ import dev.vality.disputes.config.properties.AdaptersConnectionProperties;
import dev.vality.woody.thrift.impl.http.THSpawnClientBuilder;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import java.net.URI;
@@ -14,20 +15,19 @@ import java.util.concurrent.TimeUnit;
@Slf4j
@Service
@RequiredArgsConstructor
+@SuppressWarnings({"AbbreviationAsWordInName", "LineLength"})
public class ProviderIfaceBuilder {
private final ProviderRouting providerRouting;
private final AdaptersConnectionProperties adaptersConnectionProperties;
- public ProviderDisputesServiceSrv.Iface build(Map options, String url) {
- return build(providerRouting.getRouteUrl(options, url));
- }
-
- private ProviderDisputesServiceSrv.Iface build(String url) {
- log.info("Creating new client for url: {}", url);
+ @Cacheable(value = "adapters", key = "#root.args[1]", cacheManager = "adaptersCacheManager")
+ public ProviderDisputesServiceSrv.Iface buildTHSpawnClient(Map options, String url) {
+ var routeUrl = providerRouting.getRouteUrl(options, url);
+ log.info("Creating new client for url: {}", routeUrl);
return new THSpawnClientBuilder()
.withNetworkTimeout((int) TimeUnit.SECONDS.toMillis(adaptersConnectionProperties.getTimeoutSec()))
- .withAddress(URI.create(url))
+ .withAddress(URI.create(routeUrl))
.build(ProviderDisputesServiceSrv.Iface.class);
}
}
diff --git a/src/main/java/dev/vality/disputes/schedule/service/ProviderRouting.java b/src/main/java/dev/vality/disputes/schedule/service/ProviderRouting.java
index 9e7f69f..e81d8e7 100644
--- a/src/main/java/dev/vality/disputes/schedule/service/ProviderRouting.java
+++ b/src/main/java/dev/vality/disputes/schedule/service/ProviderRouting.java
@@ -3,12 +3,11 @@ package dev.vality.disputes.schedule.service;
import dev.vality.disputes.exception.RoutingException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
-import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.web.util.UriComponentsBuilder;
-import java.net.URL;
+import java.net.URI;
import java.util.Map;
@Slf4j
@@ -19,7 +18,6 @@ public class ProviderRouting {
private static final String DISPUTES_URL_POSTFIX_DEFAULT = "disputes";
private static final String OPTION_DISPUTES_URL_FIELD_NAME = "disputes_url";
- @Cacheable(value = "adapters", key = "defaultProviderUrl", cacheManager = "adaptersCacheManager")
public String getRouteUrl(Map options, String defaultProviderUrl) {
var url = options.get(OPTION_DISPUTES_URL_FIELD_NAME);
if (ObjectUtils.isEmpty(url)) {
@@ -31,8 +29,7 @@ public class ProviderRouting {
private String createDefaultRouteUrl(String defaultProviderUrl) {
log.debug("Creating url by appending postfix");
try {
- var validUri = new URL(defaultProviderUrl).toURI();
- return UriComponentsBuilder.fromUri(validUri)
+ return UriComponentsBuilder.fromUri(URI.create(defaultProviderUrl))
.pathSegment(DISPUTES_URL_POSTFIX_DEFAULT)
.encode()
.build()
diff --git a/src/main/java/dev/vality/disputes/security/AccessService.java b/src/main/java/dev/vality/disputes/security/AccessService.java
index d874bfa..d0f6e62 100644
--- a/src/main/java/dev/vality/disputes/security/AccessService.java
+++ b/src/main/java/dev/vality/disputes/security/AccessService.java
@@ -90,7 +90,7 @@ public class AccessService {
}
private InvoicePayment getInvoicePayment(dev.vality.damsel.payment_processing.Invoice invoice, String paymentId) {
- log.debug("Processing invoice: {}", invoice);
+ log.debug("Processing invoice: {}", invoice.getInvoice().getId());
return invoice.getPayments().stream()
.filter(invoicePayment -> paymentId.equals(invoicePayment.getPayment().getId())
&& invoicePayment.isSetRoute())
diff --git a/src/main/java/dev/vality/disputes/security/service/impl/BouncerServiceImpl.java b/src/main/java/dev/vality/disputes/security/service/impl/BouncerServiceImpl.java
index 3db3de5..1bd4c69 100644
--- a/src/main/java/dev/vality/disputes/security/service/impl/BouncerServiceImpl.java
+++ b/src/main/java/dev/vality/disputes/security/service/impl/BouncerServiceImpl.java
@@ -15,6 +15,7 @@ import org.springframework.stereotype.Service;
@Slf4j
@Service
@RequiredArgsConstructor
+@SuppressWarnings({"ParameterName", "LineLength"})
public class BouncerServiceImpl implements BouncerService {
private final BouncerProperties bouncerProperties;
@@ -23,9 +24,9 @@ public class BouncerServiceImpl implements BouncerService {
@Override
public Resolution getResolution(AccessData accessData) {
- log.debug("Check access with bouncer context: {}", accessData);
+ log.debug("Check access with bouncer context: {}{}", accessData.getInvoice().getInvoice().getId(), accessData.getPayment().getPayment().getId());
var context = bouncerContextFactory.buildContext(accessData);
- log.debug("Built thrift context: {}", context);
+ log.debug("Built thrift context: {}{}", accessData.getInvoice().getInvoice().getId(), accessData.getPayment().getPayment().getId());
try {
var judge = bouncerClient.judge(bouncerProperties.getRuleSetId(), context);
log.debug("Have judge: {}", judge);
diff --git a/src/main/java/dev/vality/disputes/service/external/DominantService.java b/src/main/java/dev/vality/disputes/service/external/DominantService.java
index ab998f3..7495af6 100644
--- a/src/main/java/dev/vality/disputes/service/external/DominantService.java
+++ b/src/main/java/dev/vality/disputes/service/external/DominantService.java
@@ -2,14 +2,14 @@ package dev.vality.disputes.service.external;
import dev.vality.damsel.domain.*;
-import java.util.concurrent.CompletableFuture;
-
public interface DominantService {
Currency getCurrency(CurrencyRef currencyRef);
- CompletableFuture getTerminal(TerminalRef terminalRef);
+ Terminal getTerminal(TerminalRef terminalRef);
- CompletableFuture getProxy(ProviderRef providerRef);
+ ProxyDefinition getProxy(ProxyRef proxyRef);
+
+ Provider getProvider(ProviderRef providerRef);
}
diff --git a/src/main/java/dev/vality/disputes/service/external/impl/DominantServiceImpl.java b/src/main/java/dev/vality/disputes/service/external/impl/DominantServiceImpl.java
index 23796d1..905fefc 100644
--- a/src/main/java/dev/vality/disputes/service/external/impl/DominantServiceImpl.java
+++ b/src/main/java/dev/vality/disputes/service/external/impl/DominantServiceImpl.java
@@ -6,11 +6,8 @@ import dev.vality.disputes.service.external.DominantService;
import dev.vality.disputes.service.external.impl.dominant.DominantCacheServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
-import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
-import java.util.concurrent.CompletableFuture;
-
@Slf4j
@Service
@RequiredArgsConstructor
@@ -23,26 +20,18 @@ public class DominantServiceImpl implements DominantService {
return dominantCacheService.getCurrency(currencyRef);
}
- @Async
@Override
- public CompletableFuture getTerminal(TerminalRef terminalRef) {
- try {
- var terminal = dominantCacheService.getTerminal(terminalRef);
- return CompletableFuture.completedFuture(terminal);
- } catch (Exception e) {
- return CompletableFuture.failedFuture(e);
- }
+ public Terminal getTerminal(TerminalRef terminalRef) {
+ return dominantCacheService.getTerminal(terminalRef);
}
- @Async
@Override
- public CompletableFuture getProxy(ProviderRef providerRef) {
- try {
- var provider = dominantCacheService.getProvider(providerRef);
- var proxy = dominantCacheService.getProxy(provider.getProxy().getRef());
- return CompletableFuture.completedFuture(proxy);
- } catch (Exception e) {
- return CompletableFuture.failedFuture(e);
- }
+ public ProxyDefinition getProxy(ProxyRef proxyRef) {
+ return dominantCacheService.getProxy(proxyRef);
+ }
+
+ @Override
+ public Provider getProvider(ProviderRef providerRef) {
+ return dominantCacheService.getProvider(providerRef);
}
}
diff --git a/src/main/java/dev/vality/disputes/service/external/impl/dominant/DominantAsyncService.java b/src/main/java/dev/vality/disputes/service/external/impl/dominant/DominantAsyncService.java
new file mode 100644
index 0000000..cc6941d
--- /dev/null
+++ b/src/main/java/dev/vality/disputes/service/external/impl/dominant/DominantAsyncService.java
@@ -0,0 +1,41 @@
+package dev.vality.disputes.service.external.impl.dominant;
+
+import dev.vality.damsel.domain.Currency;
+import dev.vality.damsel.domain.CurrencyRef;
+import dev.vality.damsel.domain.Terminal;
+import dev.vality.damsel.domain.TerminalRef;
+import dev.vality.disputes.service.external.DominantService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+
+import java.util.concurrent.CompletableFuture;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class DominantAsyncService {
+
+ private final DominantService dominantService;
+
+ @Async("dominantAsyncServiceExecutor")
+ public CompletableFuture getCurrency(CurrencyRef currencyRef) {
+ try {
+ var currency = dominantService.getCurrency(currencyRef);
+ return CompletableFuture.completedFuture(currency);
+ } catch (Exception e) {
+ return CompletableFuture.failedFuture(e);
+ }
+ }
+
+ @Async("dominantAsyncServiceExecutor")
+ public CompletableFuture getTerminal(TerminalRef terminalRef) {
+ try {
+ var terminal = dominantService.getTerminal(terminalRef);
+ return CompletableFuture.completedFuture(terminal);
+ } catch (Exception e) {
+ return CompletableFuture.failedFuture(e);
+ }
+ }
+}
diff --git a/src/main/java/dev/vality/disputes/service/external/impl/dominant/DominantCacheServiceImpl.java b/src/main/java/dev/vality/disputes/service/external/impl/dominant/DominantCacheServiceImpl.java
index 36abb8c..63fb49e 100644
--- a/src/main/java/dev/vality/disputes/service/external/impl/dominant/DominantCacheServiceImpl.java
+++ b/src/main/java/dev/vality/disputes/service/external/impl/dominant/DominantCacheServiceImpl.java
@@ -19,20 +19,19 @@ public class DominantCacheServiceImpl {
private final RepositoryClientSrv.Iface dominantClient;
@Cacheable(value = "currencies", key = "#currencyRef.symbolic_code", cacheManager = "currenciesCacheManager")
- public Currency getCurrency(CurrencyRef currencyRef) throws NotFoundException {
+ public Currency getCurrency(CurrencyRef currencyRef) {
return getCurrency(currencyRef, Reference.head(new Head()));
}
- private Currency getCurrency(CurrencyRef currencyRef, Reference revisionReference)
- throws NotFoundException {
+ private Currency getCurrency(CurrencyRef currencyRef, Reference revisionReference) {
log.debug("Trying to get currency, currencyRef='{}', revisionReference='{}'", currencyRef, revisionReference);
try {
var reference = new dev.vality.damsel.domain.Reference();
reference.setCurrency(currencyRef);
var versionedObject = checkoutObject(revisionReference, reference);
var currency = versionedObject.getObject().getCurrency().getData();
- log.debug("Currency has been found, currencyRef='{}', revisionReference='{}', currency='{}'",
- currencyRef, revisionReference, currency);
+ log.debug("Currency has been found, currencyRef='{}', revisionReference='{}'",
+ currencyRef, revisionReference);
return currency;
} catch (VersionNotFound | ObjectNotFound ex) {
throw new NotFoundException(String.format("Version not found, currencyRef='%s', revisionReference='%s'",
@@ -48,8 +47,7 @@ public class DominantCacheServiceImpl {
return getTerminal(terminalRef, Reference.head(new Head()));
}
- public Terminal getTerminal(TerminalRef terminalRef, Reference revisionReference)
- throws NotFoundException {
+ public Terminal getTerminal(TerminalRef terminalRef, Reference revisionReference) {
log.debug("Trying to get terminal from dominant, terminalRef='{}', revisionReference='{}'", terminalRef,
revisionReference);
try {
@@ -57,8 +55,8 @@ public class DominantCacheServiceImpl {
reference.setTerminal(terminalRef);
var versionedObject = checkoutObject(revisionReference, reference);
var terminal = versionedObject.getObject().getTerminal().getData();
- log.debug("Terminal has been found, terminalRef='{}', revisionReference='{}', terminal='{}'",
- terminalRef, revisionReference, terminal);
+ log.debug("Terminal has been found, terminalRef='{}', revisionReference='{}'",
+ terminalRef, revisionReference);
return terminal;
} catch (VersionNotFound | ObjectNotFound ex) {
throw new NotFoundException(String.format("Version not found, terminalRef='%s', revisionReference='%s'",
@@ -74,8 +72,7 @@ public class DominantCacheServiceImpl {
return getProvider(providerRef, Reference.head(new Head()));
}
- private Provider getProvider(ProviderRef providerRef, Reference revisionReference)
- throws NotFoundException {
+ private Provider getProvider(ProviderRef providerRef, Reference revisionReference) {
log.debug("Trying to get provider from dominant, providerRef='{}', revisionReference='{}'", providerRef,
revisionReference);
try {
@@ -83,8 +80,8 @@ public class DominantCacheServiceImpl {
reference.setProvider(providerRef);
var versionedObject = checkoutObject(revisionReference, reference);
var provider = versionedObject.getObject().getProvider().getData();
- log.debug("Provider has been found, providerRef='{}', revisionReference='{}', terminal='{}'",
- providerRef, revisionReference, provider);
+ log.debug("Provider has been found, providerRef='{}', revisionReference='{}'",
+ providerRef, revisionReference);
return provider;
} catch (VersionNotFound | ObjectNotFound ex) {
throw new NotFoundException(String.format("Version not found, providerRef='%s', revisionReference='%s'",
@@ -101,8 +98,7 @@ public class DominantCacheServiceImpl {
}
- private ProxyDefinition getProxy(ProxyRef proxyRef, Reference revisionReference)
- throws NotFoundException {
+ private ProxyDefinition getProxy(ProxyRef proxyRef, Reference revisionReference) {
log.debug("Trying to get proxy from dominant, proxyRef='{}', revisionReference='{}'", proxyRef,
revisionReference);
try {
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index bdce340..c8cde87 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -91,6 +91,9 @@ dispute:
fixedDelayCreated: 5000
fixedDelayPending: 5000
fixedDelayCreateAdjustments: 5000
+ initialDelayCreated: 5000
+ initialDelayPending: 5000
+ initialDelayCreateAdjustments: 5000
isScheduleCreatedEnabled: true
isSchedulePendingEnabled: true
isScheduleCreateAdjustmentsEnabled: true
@@ -105,3 +108,10 @@ manual-parsing-topic:
testcontainers:
postgresql:
tag: '11.4'
+
+http-client:
+ requestTimeout: 60000
+ poolTimeout: 10000
+ connectionTimeout: 10000
+ maxTotalPooling: 200
+ defaultMaxPerRoute: 200
diff --git a/src/main/resources/db/migration/V4__add_already_exist_status.sql b/src/main/resources/db/migration/V4__add_already_exist_status.sql
new file mode 100644
index 0000000..fbdbb4e
--- /dev/null
+++ b/src/main/resources/db/migration/V4__add_already_exist_status.sql
@@ -0,0 +1 @@
+ALTER TYPE dspt.dispute_status ADD VALUE 'already_exist_created';
diff --git a/src/test/java/dev/vality/disputes/dao/DisputeDaoTest.java b/src/test/java/dev/vality/disputes/dao/DisputeDaoTest.java
index 17af52b..949dfdd 100644
--- a/src/test/java/dev/vality/disputes/dao/DisputeDaoTest.java
+++ b/src/test/java/dev/vality/disputes/dao/DisputeDaoTest.java
@@ -1,26 +1,47 @@
package dev.vality.disputes.dao;
+import dev.vality.disputes.dao.config.PostgresqlSpringBootITest;
import dev.vality.disputes.domain.tables.pojos.Dispute;
-import dev.vality.testcontainers.annotations.DefaultSpringBootTest;
-import dev.vality.testcontainers.annotations.postgresql.PostgresqlTestcontainerSingleton;
+import dev.vality.disputes.exception.NotFoundException;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import static dev.vality.testcontainers.annotations.util.RandomBeans.random;
+import static dev.vality.testcontainers.annotations.util.ValuesGenerator.generateId;
+import static dev.vality.testcontainers.annotations.util.ValuesGenerator.generateLong;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
-@PostgresqlTestcontainerSingleton
-@DefaultSpringBootTest
+@PostgresqlSpringBootITest
public class DisputeDaoTest {
@Autowired
private DisputeDao disputeDao;
@Test
- public void insertAndFindTest() {
+ public void testInsertAndFind() {
var random = random(Dispute.class);
disputeDao.save(random);
assertEquals(random,
disputeDao.get(random.getId(), random.getInvoiceId(), random.getPaymentId()));
}
+
+ @Test
+ public void testNotFoundException() {
+ assertThrows(NotFoundException.class,
+ () -> disputeDao.get(generateLong(), generateId(), generateId()));
+ }
+
+ @Test
+ public void testMultiInsertAndFind() {
+ var random = random(Dispute.class);
+ random.setId(null);
+ random.setInvoiceId("setInvoiceId");
+ random.setPaymentId("setPaymentId");
+ disputeDao.save(random);
+ disputeDao.save(random);
+ disputeDao.save(random);
+ assertEquals(3,
+ disputeDao.get(random.getInvoiceId(), random.getPaymentId()).size());
+ }
}
diff --git a/src/test/java/dev/vality/disputes/dao/config/PostgresqlSpringBootITest.java b/src/test/java/dev/vality/disputes/dao/config/PostgresqlSpringBootITest.java
new file mode 100644
index 0000000..369e0ba
--- /dev/null
+++ b/src/test/java/dev/vality/disputes/dao/config/PostgresqlSpringBootITest.java
@@ -0,0 +1,19 @@
+package dev.vality.disputes.dao.config;
+
+import dev.vality.disputes.dao.config.testconfiguration.MockedUnimportantServicesConfig;
+import dev.vality.testcontainers.annotations.DefaultSpringBootTest;
+import dev.vality.testcontainers.annotations.postgresql.PostgresqlTestcontainerSingleton;
+import org.springframework.context.annotation.Import;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@PostgresqlTestcontainerSingleton
+@DefaultSpringBootTest
+@Import(MockedUnimportantServicesConfig.class)
+public @interface PostgresqlSpringBootITest {
+}
diff --git a/src/test/java/dev/vality/disputes/dao/config/testconfiguration/MockedUnimportantServicesConfig.java b/src/test/java/dev/vality/disputes/dao/config/testconfiguration/MockedUnimportantServicesConfig.java
new file mode 100644
index 0000000..9ccf13b
--- /dev/null
+++ b/src/test/java/dev/vality/disputes/dao/config/testconfiguration/MockedUnimportantServicesConfig.java
@@ -0,0 +1,21 @@
+package dev.vality.disputes.dao.config.testconfiguration;
+
+import dev.vality.disputes.schedule.TaskCreateAdjustmentsService;
+import dev.vality.disputes.schedule.TaskCreatedDisputesService;
+import dev.vality.disputes.schedule.TaskPendingDisputesService;
+import org.springframework.boot.test.context.TestConfiguration;
+import org.springframework.boot.test.mock.mockito.MockBean;
+
+@TestConfiguration
+public class MockedUnimportantServicesConfig {
+
+ @MockBean
+ private TaskCreatedDisputesService taskCreatedDisputesService;
+
+ @MockBean
+ private TaskPendingDisputesService taskPendingDisputesService;
+
+ @MockBean
+ private TaskCreateAdjustmentsService taskCreateAdjustmentsService;
+
+}