diff --git a/README.md b/README.md
index 2d0dc00..0fc997b 100644
--- a/README.md
+++ b/README.md
@@ -48,7 +48,7 @@
Если при финальном статусе платежа `captured` создавать на провайдере диспут является не желательной ситуацией, можно
установить опцию в терминале `DISPUTE_FLOW_CAPTURED_BLOCKED` и пулять
состояние
-в топик\тг-провайдер-бот\filebeat на ручной разбор (`ManualParsing` module)
+в топик\тг-провайдер-бот\filebeat на ручной разбор (`AdminManagement` module)
Не все провайдеры на данный момент поддерживают работу с диспутами по `API`.
Предполагается такой способ действия при этой ситуации:
@@ -111,7 +111,7 @@
- если это captured платеж и выставлена опция `DISPUTE_FLOW_CAPTURED_BLOCKED` , то тоже отправляет на ручной разбор
Далее, через внутрений трифт-интерфейс саппорт получает способ манипулировать диспутом для его
-обработки (`ManualParsingDisputesService`)
+обработки (`AdminManagementDisputesService`)
- Перед переводом диспута в финальный статус саппорт должен будет забиндить айди созданного диспута в провайдере через
ручку `BindCreated()`. Здесь особенность, что этот метод фильтрует возможность биндить диспуты только созданные
diff --git a/pom.xml b/pom.xml
index b98d37f..ac549f1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
dev.vality
service-parent-pom
- 3.0.2
+ 3.0.4
disputes-api
@@ -47,7 +47,7 @@
dev.vality
disputes-proto
- 1.23-37a5ad1
+ 1.26-fc8e34f
dev.vality
@@ -62,6 +62,7 @@
dev.vality
damsel
+ 1.648-ad715bd
dev.vality
@@ -87,6 +88,16 @@
adapter-flow-lib
1.0.0
+
+ dev.vality.woody
+ woody-thrift
+ 2.0.8
+
+
+ dev.vality.woody
+ woody-api
+ 2.0.8
+
@@ -217,6 +228,11 @@
guava
32.0.0-jre
+
+ io.opentelemetry
+ opentelemetry-semconv
+ 1.29.0-alpha
+
diff --git a/src/main/java/dev/vality/disputes/admin/callback/CallbackNotifier.java b/src/main/java/dev/vality/disputes/admin/callback/CallbackNotifier.java
new file mode 100644
index 0000000..063f342
--- /dev/null
+++ b/src/main/java/dev/vality/disputes/admin/callback/CallbackNotifier.java
@@ -0,0 +1,17 @@
+package dev.vality.disputes.admin.callback;
+
+import dev.vality.disputes.domain.tables.pojos.Dispute;
+
+import java.util.List;
+
+public interface CallbackNotifier {
+
+ void sendDisputeAlreadyCreated(Dispute dispute);
+
+ void sendDisputePoolingExpired(Dispute dispute);
+
+ void sendDisputeReadyForCreateAdjustment(List disputes);
+
+ void sendDisputeFailedReviewRequired(Dispute dispute, String errorCode, String errorDescription);
+
+}
diff --git a/src/main/java/dev/vality/disputes/admin/callback/DisputesTgBotCallbackNotifierImpl.java b/src/main/java/dev/vality/disputes/admin/callback/DisputesTgBotCallbackNotifierImpl.java
new file mode 100644
index 0000000..6c9fdbd
--- /dev/null
+++ b/src/main/java/dev/vality/disputes/admin/callback/DisputesTgBotCallbackNotifierImpl.java
@@ -0,0 +1,52 @@
+package dev.vality.disputes.admin.callback;
+
+import dev.vality.disputes.admin.DisputeAlreadyCreated;
+import dev.vality.disputes.admin.DisputeFailedReviewRequired;
+import dev.vality.disputes.admin.DisputePoolingExpired;
+import dev.vality.disputes.admin.DisputeReadyForCreateAdjustment;
+import dev.vality.disputes.domain.tables.pojos.Dispute;
+import dev.vality.disputes.service.external.DisputesTgBotService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.UUID;
+
+@Service
+@ConditionalOnProperty(value = "service.disputes-tg-bot.admin.enabled", havingValue = "true", matchIfMissing = true)
+@RequiredArgsConstructor
+@Slf4j
+@SuppressWarnings({"LineLength"})
+public class DisputesTgBotCallbackNotifierImpl implements CallbackNotifier {
+
+ private final DisputesTgBotService disputesTgBotService;
+
+ @Override
+ public void sendDisputeAlreadyCreated(Dispute dispute) {
+ disputesTgBotService.sendDisputeAlreadyCreated(new DisputeAlreadyCreated(dispute.getId().toString()));
+ }
+
+ @Override
+ public void sendDisputePoolingExpired(Dispute dispute) {
+ disputesTgBotService.sendDisputePoolingExpired(new DisputePoolingExpired(dispute.getId().toString()));
+ }
+
+ @Override
+ public void sendDisputeReadyForCreateAdjustment(List disputes) {
+ var disputeReadyForCreateAdjustments = disputes.stream()
+ .map(Dispute::getId)
+ .map(UUID::toString)
+ .map(DisputeReadyForCreateAdjustment::new)
+ .toList();
+ disputesTgBotService.sendDisputeReadyForCreateAdjustment(disputeReadyForCreateAdjustments);
+ }
+
+ @Override
+ public void sendDisputeFailedReviewRequired(Dispute dispute, String errorCode, String errorDescription) {
+ disputesTgBotService.sendDisputeFailedReviewRequired(
+ new DisputeFailedReviewRequired(dispute.getId().toString(), errorCode)
+ .setErrorDescription(errorDescription));
+ }
+}
diff --git a/src/main/java/dev/vality/disputes/admin/callback/DummyCallbackNotifierImpl.java b/src/main/java/dev/vality/disputes/admin/callback/DummyCallbackNotifierImpl.java
new file mode 100644
index 0000000..b09691b
--- /dev/null
+++ b/src/main/java/dev/vality/disputes/admin/callback/DummyCallbackNotifierImpl.java
@@ -0,0 +1,37 @@
+package dev.vality.disputes.admin.callback;
+
+import dev.vality.disputes.domain.tables.pojos.Dispute;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+@ConditionalOnProperty(value = "service.disputes-tg-bot.admin.enabled", havingValue = "false")
+@RequiredArgsConstructor
+@Slf4j
+@SuppressWarnings({"LineLength"})
+public class DummyCallbackNotifierImpl implements CallbackNotifier {
+
+ @Override
+ public void sendDisputeAlreadyCreated(Dispute dispute) {
+ log.debug("Trying to call DummyCallbackNotifierImpl.sendDisputeAlreadyCreated() {}", dispute.getId());
+ }
+
+ @Override
+ public void sendDisputePoolingExpired(Dispute dispute) {
+ log.debug("Trying to call DummyCallbackNotifierImpl.sendDisputePoolingExpired() {}", dispute.getId());
+ }
+
+ @Override
+ public void sendDisputeReadyForCreateAdjustment(List disputes) {
+ log.debug("Trying to call DummyCallbackNotifierImpl.sendDisputeReadyForCreateAdjustment() {}", disputes.size());
+ }
+
+ @Override
+ public void sendDisputeFailedReviewRequired(Dispute dispute, String errorCode, String errorDescription) {
+ log.debug("Trying to call DummyCallbackNotifierImpl.sendDisputeFailedReviewRequired() {}", dispute.getId());
+ }
+}
diff --git a/src/main/java/dev/vality/disputes/manualparsing/ManualParsingDisputesService.java b/src/main/java/dev/vality/disputes/admin/management/AdminManagementDisputesService.java
similarity index 96%
rename from src/main/java/dev/vality/disputes/manualparsing/ManualParsingDisputesService.java
rename to src/main/java/dev/vality/disputes/admin/management/AdminManagementDisputesService.java
index 60e3bd5..240ba7b 100644
--- a/src/main/java/dev/vality/disputes/manualparsing/ManualParsingDisputesService.java
+++ b/src/main/java/dev/vality/disputes/admin/management/AdminManagementDisputesService.java
@@ -1,4 +1,4 @@
-package dev.vality.disputes.manualparsing;
+package dev.vality.disputes.admin.management;
import dev.vality.disputes.admin.*;
import dev.vality.disputes.dao.DisputeDao;
@@ -31,7 +31,7 @@ import static dev.vality.disputes.api.service.ApiDisputesService.DISPUTE_PENDING
@Service
@RequiredArgsConstructor
@SuppressWarnings({"ParameterName", "LineLength", "MissingSwitchDefault"})
-public class ManualParsingDisputesService {
+public class AdminManagementDisputesService {
private final DisputeDao disputeDao;
private final ProviderDisputeDao providerDisputeDao;
@@ -48,11 +48,12 @@ public class ManualParsingDisputesService {
return;
}
var cancelReason = cancelParams.getCancelReason().orElse(null);
+ var mapping = cancelParams.getMapping().orElse(null);
log.debug("GetForUpdateSkipLocked has been found {}", dispute);
if (DISPUTE_PENDING.contains(dispute.getStatus())) {
// используется не failed, а cancelled чтоб можно было понять, что зафейлен по внешнему вызову
- log.warn("Trying to set cancelled Dispute status {}, {}", dispute, cancelReason);
- disputeDao.update(dispute.getId(), DisputeStatus.cancelled, cancelReason);
+ log.warn("Trying to set cancelled Dispute status {}, {}, {}", dispute, mapping, cancelReason);
+ disputeDao.update(dispute.getId(), DisputeStatus.cancelled, cancelReason, mapping);
log.debug("Dispute status has been set to cancelled {}", dispute);
} else {
log.info("Request was skipped by inappropriate status {}", dispute);
@@ -133,6 +134,7 @@ public class ManualParsingDisputesService {
disputeResult.setProviderTrxId(dispute.getProviderTrxId());
disputeResult.setStatus(dispute.getStatus().name());
disputeResult.setErrorMessage(dispute.getErrorMessage());
+ disputeResult.setMapping(dispute.getMapping());
disputeResult.setAmount(String.valueOf(dispute.getAmount()));
disputeResult.setChangedAmount(Optional.ofNullable(dispute.getChangedAmount())
.map(String::valueOf)
diff --git a/src/main/java/dev/vality/disputes/manualparsing/ManualParsingHandler.java b/src/main/java/dev/vality/disputes/admin/management/AdminManagementHandler.java
similarity index 69%
rename from src/main/java/dev/vality/disputes/manualparsing/ManualParsingHandler.java
rename to src/main/java/dev/vality/disputes/admin/management/AdminManagementHandler.java
index 3122fbb..3170c45 100644
--- a/src/main/java/dev/vality/disputes/manualparsing/ManualParsingHandler.java
+++ b/src/main/java/dev/vality/disputes/admin/management/AdminManagementHandler.java
@@ -1,4 +1,4 @@
-package dev.vality.disputes.manualparsing;
+package dev.vality.disputes.admin.management;
import dev.vality.disputes.admin.*;
import lombok.RequiredArgsConstructor;
@@ -12,28 +12,28 @@ import java.util.ArrayList;
@RequiredArgsConstructor
@Slf4j
@SuppressWarnings({"ParameterName", "LineLength"})
-public class ManualParsingHandler implements ManualParsingServiceSrv.Iface {
+public class AdminManagementHandler implements AdminManagementServiceSrv.Iface {
- private final ManualParsingDisputesService manualParsingDisputesService;
+ private final AdminManagementDisputesService adminManagementDisputesService;
@Override
public void cancelPending(CancelParamsRequest cancelParamsRequest) throws TException {
for (var cancelParam : cancelParamsRequest.getCancelParams()) {
- manualParsingDisputesService.cancelPendingDispute(cancelParam);
+ adminManagementDisputesService.cancelPendingDispute(cancelParam);
}
}
@Override
public void approvePending(ApproveParamsRequest approveParamsRequest) throws TException {
for (var approveParam : approveParamsRequest.getApproveParams()) {
- manualParsingDisputesService.approvePendingDispute(approveParam);
+ adminManagementDisputesService.approvePendingDispute(approveParam);
}
}
@Override
public void bindCreated(BindParamsRequest bindParamsRequest) throws TException {
for (var bindParam : bindParamsRequest.getBindParams()) {
- manualParsingDisputesService.bindCreatedDispute(bindParam);
+ adminManagementDisputesService.bindCreatedDispute(bindParam);
}
}
@@ -41,7 +41,7 @@ public class ManualParsingHandler implements ManualParsingServiceSrv.Iface {
public DisputeResult getDisputes(DisputeParamsRequest disputeParamsRequest) throws TException {
var disputeResult = new DisputeResult(new ArrayList<>());
for (var disputeParams : disputeParamsRequest.getDisputeParams()) {
- var dispute = manualParsingDisputesService.getDispute(disputeParams, disputeParamsRequest.isWithAttachments());
+ var dispute = adminManagementDisputesService.getDispute(disputeParams, disputeParamsRequest.isWithAttachments());
if (dispute != null) {
disputeResult.getDisputes().add(dispute);
}
diff --git a/src/main/java/dev/vality/disputes/manualparsing/ManualParsingTopic.java b/src/main/java/dev/vality/disputes/admin/management/MdcTopicProducer.java
similarity index 67%
rename from src/main/java/dev/vality/disputes/manualparsing/ManualParsingTopic.java
rename to src/main/java/dev/vality/disputes/admin/management/MdcTopicProducer.java
index 1b6cb66..a065798 100644
--- a/src/main/java/dev/vality/disputes/manualparsing/ManualParsingTopic.java
+++ b/src/main/java/dev/vality/disputes/admin/management/MdcTopicProducer.java
@@ -1,8 +1,7 @@
-package dev.vality.disputes.manualparsing;
+package dev.vality.disputes.admin.management;
import dev.vality.disputes.domain.enums.DisputeStatus;
import dev.vality.disputes.domain.tables.pojos.Dispute;
-import dev.vality.disputes.provider.Attachment;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
@@ -11,26 +10,28 @@ import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
@Slf4j
@SuppressWarnings({"ParameterName", "LineLength"})
-public class ManualParsingTopic {
+public class MdcTopicProducer {
- @Value("${manual-parsing-topic.enabled}")
+ @Value("${service.mdc-topic-producer.enabled}")
private boolean enabled;
- public void sendCreated(Dispute dispute, List attachments, DisputeStatus disputeStatus) {
+ public void sendCreated(Dispute dispute, DisputeStatus disputeStatus, String errorMessage) {
if (!enabled) {
return;
}
- var contextMap = MDC.getCopyOfContextMap() == null ? new HashMap() : MDC.getCopyOfContextMap();
+ var contextMap = getContextMap();
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.name());
+ if (errorMessage != null) {
+ contextMap.put("dispute_error_message", errorMessage);
+ }
MDC.setContextMap(contextMap);
log.warn("Manual parsing case");
MDC.clear();
@@ -40,7 +41,7 @@ public class ManualParsingTopic {
if (!enabled) {
return;
}
- var contextMap = MDC.getCopyOfContextMap() == null ? new HashMap() : MDC.getCopyOfContextMap();
+ var contextMap = getContextMap();
contextMap.put("dispute_id", dispute.getId().toString());
contextMap.put("dispute_status", DisputeStatus.manual_pending.name());
MDC.setContextMap(contextMap);
@@ -52,11 +53,15 @@ public class ManualParsingTopic {
if (!enabled || disputes.isEmpty()) {
return;
}
- var contextMap = MDC.getCopyOfContextMap() == null ? new HashMap() : MDC.getCopyOfContextMap();
+ var contextMap = getContextMap();
contextMap.put("dispute_ids", disputes.stream().map(Dispute::getId).map(String::valueOf).collect(Collectors.joining(", ")));
contextMap.put("dispute_status", DisputeStatus.create_adjustment.name());
MDC.setContextMap(contextMap);
log.warn("Ready for CreateAdjustments case");
MDC.clear();
}
+
+ private Map getContextMap() {
+ return MDC.getCopyOfContextMap() == null ? new HashMap<>() : MDC.getCopyOfContextMap();
+ }
}
diff --git a/src/main/java/dev/vality/disputes/api/CancelController.java b/src/main/java/dev/vality/disputes/api/CancelController.java
deleted file mode 100644
index 180a1f4..0000000
--- a/src/main/java/dev/vality/disputes/api/CancelController.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package dev.vality.disputes.api;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
-import dev.vality.disputes.admin.CancelParamsRequest;
-import dev.vality.disputes.admin.ManualParsingServiceSrv;
-import lombok.RequiredArgsConstructor;
-import lombok.SneakyThrows;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-@RestController
-@RequiredArgsConstructor
-@RequestMapping({"/disputes"})
-@Slf4j
-public class CancelController {
-
- private final ManualParsingServiceSrv.Iface manualParsingHandler;
- private final ObjectMapper objectMapper = new ObjectMapper().registerModule(new Jdk8Module());
-
- @PostMapping("/cancel")
- @SneakyThrows
- public void cancelPending(@RequestBody String body) {
- log.debug("cancelPending {}", body);
- manualParsingHandler.cancelPending(objectMapper.readValue(body, CancelParamsRequest.class));
- }
-}
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 694d3e8..4ececbc 100644
--- a/src/main/java/dev/vality/disputes/api/converter/Status200ResponseConverter.java
+++ b/src/main/java/dev/vality/disputes/api/converter/Status200ResponseConverter.java
@@ -12,8 +12,8 @@ public class Status200ResponseConverter {
public Status200Response convert(Dispute dispute) {
var body = new Status200Response();
body.setStatus(getStatus(dispute));
- if (!StringUtils.isBlank(dispute.getErrorMessage())) {
- body.setReason(new GeneralError(dispute.getErrorMessage()));
+ if (!StringUtils.isBlank(dispute.getMapping())) {
+ body.setReason(new GeneralError(dispute.getMapping()));
}
if (dispute.getChangedAmount() != null) {
body.setChangedAmount(dispute.getChangedAmount());
diff --git a/src/main/java/dev/vality/disputes/config/ApplicationConfig.java b/src/main/java/dev/vality/disputes/config/ApplicationConfig.java
index 3364f95..d244bb3 100644
--- a/src/main/java/dev/vality/disputes/config/ApplicationConfig.java
+++ b/src/main/java/dev/vality/disputes/config/ApplicationConfig.java
@@ -5,6 +5,8 @@ import dev.vality.bouncer.decisions.ArbiterSrv;
import dev.vality.damsel.domain_config.RepositoryClientSrv;
import dev.vality.damsel.payment_processing.InvoicingSrv;
import dev.vality.damsel.payment_processing.PartyManagementSrv;
+import dev.vality.disputes.admin.AdminCallbackServiceSrv;
+import dev.vality.disputes.provider.ProviderDisputesServiceSrv;
import dev.vality.file.storage.FileStorageSrv;
import dev.vality.token.keeper.TokenAuthenticatorSrv;
import dev.vality.woody.thrift.impl.http.THSpawnClientBuilder;
@@ -81,6 +83,26 @@ public class ApplicationConfig {
.build(PartyManagementSrv.Iface.class);
}
+ @Bean
+ public ProviderDisputesServiceSrv.Iface providerDisputesTgBotClient(
+ @Value("${service.disputes-tg-bot.provider.url}") Resource resource,
+ @Value("${service.disputes-tg-bot.provider.networkTimeout}") int networkTimeout) throws IOException {
+ return new THSpawnClientBuilder()
+ .withNetworkTimeout(networkTimeout)
+ .withAddress(resource.getURI())
+ .build(ProviderDisputesServiceSrv.Iface.class);
+ }
+
+ @Bean
+ public AdminCallbackServiceSrv.Iface adminCallbackDisputesTgBotClient(
+ @Value("${service.disputes-tg-bot.admin.url}") Resource resource,
+ @Value("${service.disputes-tg-bot.admin.networkTimeout}") int networkTimeout) throws IOException {
+ return new THSpawnClientBuilder()
+ .withNetworkTimeout(networkTimeout)
+ .withAddress(resource.getURI())
+ .build(AdminCallbackServiceSrv.Iface.class);
+ }
+
@Bean
public ExecutorService disputesThreadPool(@Value("${dispute.batchSize}") int threadPoolSize) {
final var threadFactory = new ThreadFactoryBuilder()
diff --git a/src/main/java/dev/vality/disputes/config/NetworkConfig.java b/src/main/java/dev/vality/disputes/config/NetworkConfig.java
index 06b4672..1bc8610 100644
--- a/src/main/java/dev/vality/disputes/config/NetworkConfig.java
+++ b/src/main/java/dev/vality/disputes/config/NetworkConfig.java
@@ -24,7 +24,7 @@ public class NetworkConfig {
public static final String HEALTH = "/actuator/health";
public static final String MERCHANT = "/disputes-api/v1/merchant";
- public static final String MANUAL = "/disputes-api/v1/manual-parsing";
+ public static final String ADMIN_MANAGEMENT = "/disputes-api/v1/admin-management";
public static final String CALLBACK = "/disputes-api/v1/callback";
@Bean
@@ -39,7 +39,7 @@ public class NetworkConfig {
var enabledPaths = servletPath.startsWith(restEndpoint)
|| servletPath.startsWith(HEALTH)
|| servletPath.startsWith(MERCHANT)
- || servletPath.startsWith(MANUAL)
+ || servletPath.startsWith(ADMIN_MANAGEMENT)
|| servletPath.startsWith(CALLBACK);
if ((request.getLocalPort() == restPort) && !enabledPaths) {
response.sendError(404, "Unknown address");
diff --git a/src/main/java/dev/vality/disputes/config/OtelConfig.java b/src/main/java/dev/vality/disputes/config/OtelConfig.java
new file mode 100644
index 0000000..16978e2
--- /dev/null
+++ b/src/main/java/dev/vality/disputes/config/OtelConfig.java
@@ -0,0 +1,68 @@
+package dev.vality.disputes.config;
+
+import dev.vality.disputes.config.properties.OtelProperties;
+import io.opentelemetry.api.GlobalOpenTelemetry;
+import io.opentelemetry.api.OpenTelemetry;
+import io.opentelemetry.api.common.Attributes;
+import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
+import io.opentelemetry.context.propagation.ContextPropagators;
+import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter;
+import io.opentelemetry.sdk.OpenTelemetrySdk;
+import io.opentelemetry.sdk.resources.Resource;
+import io.opentelemetry.sdk.trace.SdkTracerProvider;
+import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
+import io.opentelemetry.sdk.trace.samplers.Sampler;
+import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.time.Duration;
+
+@Slf4j
+@Configuration
+@RequiredArgsConstructor
+public class OtelConfig {
+
+ private final OtelProperties otelProperties;
+
+ @Value("${spring.application.name}")
+ private String applicationName;
+
+ @Bean
+ public OpenTelemetry openTelemetryConfig() {
+ var resource = Resource.getDefault()
+ .merge(Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, applicationName)));
+ var sdkTracerProvider = SdkTracerProvider.builder()
+ .addSpanProcessor(BatchSpanProcessor.builder(OtlpHttpSpanExporter.builder()
+ .setEndpoint(otelProperties.getResource())
+ .setTimeout(Duration.ofMillis(otelProperties.getTimeout()))
+ .build())
+ .build())
+ .setSampler(Sampler.alwaysOn())
+ .setResource(resource)
+ .build();
+ var openTelemetrySdk = OpenTelemetrySdk.builder()
+ .setTracerProvider(sdkTracerProvider)
+ .setPropagators(ContextPropagators.create(W3CTraceContextPropagator.getInstance()))
+ .build();
+ registerGlobalOpenTelemetry(openTelemetrySdk);
+ return openTelemetrySdk;
+ }
+
+ private static void registerGlobalOpenTelemetry(OpenTelemetry openTelemetry) {
+ try {
+ GlobalOpenTelemetry.set(openTelemetry);
+ } catch (Exception e) {
+ log.warn("please initialize the ObservabilitySdk before starting the application");
+ GlobalOpenTelemetry.resetForTest();
+ try {
+ GlobalOpenTelemetry.set(openTelemetry);
+ } catch (Exception ex) {
+ log.warn("unable to set GlobalOpenTelemetry", ex);
+ }
+ }
+ }
+}
diff --git a/src/main/java/dev/vality/disputes/config/properties/OtelProperties.java b/src/main/java/dev/vality/disputes/config/properties/OtelProperties.java
new file mode 100644
index 0000000..1aedd3d
--- /dev/null
+++ b/src/main/java/dev/vality/disputes/config/properties/OtelProperties.java
@@ -0,0 +1,17 @@
+package dev.vality.disputes.config.properties;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+@Getter
+@Setter
+@Component
+@ConfigurationProperties(prefix = "otel")
+public class OtelProperties {
+
+ private String resource;
+ private Long timeout;
+
+}
diff --git a/src/main/java/dev/vality/disputes/constant/ModerationPrefix.java b/src/main/java/dev/vality/disputes/constant/ModerationPrefix.java
new file mode 100644
index 0000000..081eaa2
--- /dev/null
+++ b/src/main/java/dev/vality/disputes/constant/ModerationPrefix.java
@@ -0,0 +1,7 @@
+package dev.vality.disputes.constant;
+
+public class ModerationPrefix {
+
+ public static final String DISPUTES_UNKNOWN_MAPPING = "disputes_unknown_mapping";
+
+}
diff --git a/src/main/java/dev/vality/disputes/dao/DisputeDao.java b/src/main/java/dev/vality/disputes/dao/DisputeDao.java
index 6c5bd39..bff8e24 100644
--- a/src/main/java/dev/vality/disputes/dao/DisputeDao.java
+++ b/src/main/java/dev/vality/disputes/dao/DisputeDao.java
@@ -113,28 +113,32 @@ public class DisputeDao extends AbstractGenericDao {
}
public UUID update(UUID disputeId, DisputeStatus status) {
- return update(disputeId, status, null, null, null, null);
+ return update(disputeId, status, null, null, null, null, null);
}
public UUID update(UUID disputeId, DisputeStatus status, LocalDateTime nextCheckAfter) {
- return update(disputeId, status, nextCheckAfter, null, null, null);
+ return update(disputeId, status, nextCheckAfter, null, null, null, null);
}
public UUID update(UUID disputeId, DisputeStatus status, String errorMessage) {
- return update(disputeId, status, null, errorMessage, null, null);
+ return update(disputeId, status, null, errorMessage, null, null, null);
+ }
+
+ public UUID update(UUID disputeId, DisputeStatus status, String errorMessage, String mapping) {
+ return update(disputeId, status, null, errorMessage, null, null, mapping);
}
public UUID update(UUID disputeId, DisputeStatus status, Long changedAmount) {
- return update(disputeId, status, null, null, changedAmount, null);
+ return update(disputeId, status, null, null, changedAmount, null, null);
}
public UUID update(UUID disputeId, DisputeStatus status, Long changedAmount,
Boolean skipCallHgForCreateAdjustment) {
- return update(disputeId, status, null, null, changedAmount, skipCallHgForCreateAdjustment);
+ return update(disputeId, status, null, null, changedAmount, skipCallHgForCreateAdjustment, null);
}
private UUID update(UUID disputeId, DisputeStatus status, LocalDateTime nextCheckAfter, String errorMessage,
- Long changedAmount, Boolean skipCallHgForCreateAdjustment) {
+ Long changedAmount, Boolean skipCallHgForCreateAdjustment, String mapping) {
var set = getDslContext().update(DISPUTE)
.set(DISPUTE.STATUS, status);
if (nextCheckAfter != null) {
@@ -143,6 +147,9 @@ public class DisputeDao extends AbstractGenericDao {
if (errorMessage != null) {
set = set.set(DISPUTE.ERROR_MESSAGE, errorMessage);
}
+ if (mapping != null) {
+ set = set.set(DISPUTE.MAPPING, mapping);
+ }
if (changedAmount != null) {
set = set.set(DISPUTE.CHANGED_AMOUNT, changedAmount);
}
diff --git a/src/main/java/dev/vality/disputes/merchant/MerchantDisputesHandler.java b/src/main/java/dev/vality/disputes/merchant/MerchantDisputesHandler.java
index 0fd9068..1b6f40f 100644
--- a/src/main/java/dev/vality/disputes/merchant/MerchantDisputesHandler.java
+++ b/src/main/java/dev/vality/disputes/merchant/MerchantDisputesHandler.java
@@ -31,11 +31,12 @@ public class MerchantDisputesHandler implements MerchantDisputesServiceSrv.Iface
}
@Override
- public DisputeStatusResult checkDisputeStatus(DisputeContext disputeContext) throws DisputeNotFound, TException {
+ public DisputeStatusResult checkDisputeStatus(DisputeContext disputeContext) throws TException {
var response = disputesApiDelegate.status(getRequestID(), disputeContext.getDisputeId(), false).getBody();
return switch (response.getStatus()) {
case PENDING -> DisputeStatusResult.statusPending(new DisputeStatusPendingResult());
- case FAILED -> DisputeStatusResult.statusFail(new DisputeStatusFailResult(getErrorMessage(response)));
+ case FAILED ->
+ DisputeStatusResult.statusFail(new DisputeStatusFailResult().setMapping(getMapping(response)));
case SUCCEEDED -> DisputeStatusResult.statusSuccess(new DisputeStatusSuccessResult());
};
}
@@ -44,7 +45,7 @@ public class MerchantDisputesHandler implements MerchantDisputesServiceSrv.Iface
return UUID.randomUUID().toString();
}
- private String getErrorMessage(Status200Response response) {
+ private String getMapping(Status200Response response) {
return Optional.ofNullable(response.getReason())
.map(GeneralError::getMessage)
.orElse(null);
diff --git a/src/main/java/dev/vality/disputes/schedule/TaskReadyForCreateAdjustmentsService.java b/src/main/java/dev/vality/disputes/schedule/TaskReadyForCreateAdjustmentsService.java
index b890e39..f9cb83a 100644
--- a/src/main/java/dev/vality/disputes/schedule/TaskReadyForCreateAdjustmentsService.java
+++ b/src/main/java/dev/vality/disputes/schedule/TaskReadyForCreateAdjustmentsService.java
@@ -1,6 +1,7 @@
package dev.vality.disputes.schedule;
-import dev.vality.disputes.manualparsing.ManualParsingTopic;
+import dev.vality.disputes.admin.callback.CallbackNotifier;
+import dev.vality.disputes.admin.management.MdcTopicProducer;
import dev.vality.disputes.schedule.service.CreateAdjustmentsService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -16,13 +17,15 @@ import org.springframework.stereotype.Service;
public class TaskReadyForCreateAdjustmentsService {
private final CreateAdjustmentsService createAdjustmentsService;
- private final ManualParsingTopic manualParsingTopic;
+ private final CallbackNotifier callbackNotifier;
+ private final MdcTopicProducer mdcTopicProducer;
@Scheduled(fixedDelayString = "${dispute.fixedDelayReadyForCreateAdjustments}", initialDelayString = "${dispute.initialDelayReadyForCreateAdjustments}")
public void processPending() {
log.debug("Processing ReadyForCreateAdjustments get started");
var disputes = createAdjustmentsService.getReadyDisputesForCreateAdjustment();
- manualParsingTopic.sendReadyForCreateAdjustments(disputes);
+ mdcTopicProducer.sendReadyForCreateAdjustments(disputes);
+ callbackNotifier.sendDisputeReadyForCreateAdjustment(disputes);
log.info("ReadyForCreateAdjustments were processed");
}
}
diff --git a/src/main/java/dev/vality/disputes/schedule/catcher/WRuntimeExceptionCatcher.java b/src/main/java/dev/vality/disputes/schedule/catcher/WRuntimeExceptionCatcher.java
new file mode 100644
index 0000000..d8690dc
--- /dev/null
+++ b/src/main/java/dev/vality/disputes/schedule/catcher/WRuntimeExceptionCatcher.java
@@ -0,0 +1,46 @@
+package dev.vality.disputes.schedule.catcher;
+
+import dev.vality.disputes.schedule.model.ProviderData;
+import dev.vality.disputes.schedule.service.ExternalGatewayChecker;
+import dev.vality.woody.api.flow.error.WRuntimeException;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.function.Consumer;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+@SuppressWarnings({"ParameterName", "LineLength", "MissingSwitchDefault"})
+public class WRuntimeExceptionCatcher {
+
+ private final ExternalGatewayChecker externalGatewayChecker;
+
+ public void catchProvidersDisputesApiNotExist(ProviderData providerData, Runnable runnable, Runnable defaultRemoteClientRunnable) {
+ try {
+ runnable.run();
+ } catch (WRuntimeException e) {
+ if (externalGatewayChecker.isProvidersDisputesApiNotExist(providerData, e)) {
+ // отправлять на ручной разбор, если API диспутов на провайдере не реализовано
+ // (тогда при тесте соединения вернется 404)
+ log.warn("Trying to call defaultRemoteClient.createDispute(), externalGatewayChecker", e);
+ defaultRemoteClientRunnable.run();
+ return;
+ }
+ throw e;
+ }
+ }
+
+ public void catchUnexpectedResultMapping(Runnable runnable, Consumer unexpectedResultMappingHandler) {
+ try {
+ runnable.run();
+ } catch (WRuntimeException e) {
+ if (externalGatewayChecker.isProvidersDisputesUnexpectedResultMapping(e)) {
+ unexpectedResultMappingHandler.accept(e);
+ return;
+ }
+ throw e;
+ }
+ }
+}
diff --git a/src/main/java/dev/vality/disputes/schedule/client/DefaultRemoteClient.java b/src/main/java/dev/vality/disputes/schedule/client/DefaultRemoteClient.java
new file mode 100644
index 0000000..35c1c91
--- /dev/null
+++ b/src/main/java/dev/vality/disputes/schedule/client/DefaultRemoteClient.java
@@ -0,0 +1,16 @@
+package dev.vality.disputes.schedule.client;
+
+import dev.vality.disputes.domain.tables.pojos.Dispute;
+import dev.vality.disputes.provider.Attachment;
+import dev.vality.disputes.provider.DisputeCreatedResult;
+import dev.vality.disputes.schedule.model.ProviderData;
+
+import java.util.List;
+
+public interface DefaultRemoteClient {
+
+ Boolean routeUrlEquals(ProviderData providerData);
+
+ DisputeCreatedResult createDispute(Dispute dispute, List attachments, ProviderData providerData);
+
+}
diff --git a/src/main/java/dev/vality/disputes/schedule/client/DisputesTgBotRemoteClientImpl.java b/src/main/java/dev/vality/disputes/schedule/client/DisputesTgBotRemoteClientImpl.java
new file mode 100644
index 0000000..1128d79
--- /dev/null
+++ b/src/main/java/dev/vality/disputes/schedule/client/DisputesTgBotRemoteClientImpl.java
@@ -0,0 +1,46 @@
+package dev.vality.disputes.schedule.client;
+
+import dev.vality.disputes.domain.tables.pojos.Dispute;
+import dev.vality.disputes.provider.Attachment;
+import dev.vality.disputes.provider.DisputeCreatedResult;
+import dev.vality.disputes.schedule.converter.DisputeParamsConverter;
+import dev.vality.disputes.schedule.model.ProviderData;
+import dev.vality.disputes.service.external.DisputesTgBotService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Slf4j
+@Service
+@ConditionalOnProperty(value = "service.disputes-tg-bot.provider.enabled", havingValue = "true", matchIfMissing = true)
+@RequiredArgsConstructor
+@SuppressWarnings({"ParameterName", "LineLength"})
+public class DisputesTgBotRemoteClientImpl implements DefaultRemoteClient {
+
+ private final DisputesTgBotService disputesTgBotService;
+ private final DisputeParamsConverter disputeParamsConverter;
+
+ @Value("${service.disputes-tg-bot.provider.url}")
+ private String routeUrl;
+
+ @Override
+ public Boolean routeUrlEquals(ProviderData providerData) {
+ return StringUtils.equalsIgnoreCase(providerData.getRouteUrl(), routeUrl);
+ }
+
+ @Override
+ public DisputeCreatedResult createDispute(Dispute dispute, List attachments, ProviderData providerData) {
+ log.debug("Trying to build disputeParams {}", dispute.getId());
+ var disputeParams = disputeParamsConverter.convert(dispute, attachments, providerData.getOptions());
+ providerData.setRouteUrl(routeUrl);
+ log.debug("Trying to disputesTgBotService.createDispute() call {}", dispute.getId());
+ var result = disputesTgBotService.createDispute(disputeParams);
+ log.info("disputesTgBotService.createDispute() has been called {} {}", dispute.getId(), result);
+ return result;
+ }
+}
diff --git a/src/main/java/dev/vality/disputes/schedule/client/DummyRemoteClientImpl.java b/src/main/java/dev/vality/disputes/schedule/client/DummyRemoteClientImpl.java
new file mode 100644
index 0000000..a514239
--- /dev/null
+++ b/src/main/java/dev/vality/disputes/schedule/client/DummyRemoteClientImpl.java
@@ -0,0 +1,37 @@
+package dev.vality.disputes.schedule.client;
+
+import dev.vality.disputes.domain.tables.pojos.Dispute;
+import dev.vality.disputes.provider.Attachment;
+import dev.vality.disputes.provider.DisputeCreatedResult;
+import dev.vality.disputes.provider.DisputeCreatedSuccessResult;
+import dev.vality.disputes.schedule.model.ProviderData;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.UUID;
+
+@Slf4j
+@Service
+@ConditionalOnProperty(value = "service.disputes-tg-bot.provider.enabled", havingValue = "false")
+@RequiredArgsConstructor
+@SuppressWarnings({"ParameterName", "LineLength"})
+public class DummyRemoteClientImpl implements DefaultRemoteClient {
+
+ private final String routeUrl = "tg-bot";
+
+ @Override
+ public Boolean routeUrlEquals(ProviderData providerData) {
+ return StringUtils.equalsIgnoreCase(providerData.getRouteUrl(), routeUrl);
+ }
+
+ @Override
+ public DisputeCreatedResult createDispute(Dispute dispute, List attachments, ProviderData providerData) {
+ log.debug("Trying to call DummyRemoteClientImpl.createDispute() {}", dispute.getId());
+ providerData.setRouteUrl(routeUrl);
+ return DisputeCreatedResult.successResult(new DisputeCreatedSuccessResult(UUID.randomUUID().toString()));
+ }
+}
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 a463192..76cf036 100644
--- a/src/main/java/dev/vality/disputes/schedule/client/RemoteClient.java
+++ b/src/main/java/dev/vality/disputes/schedule/client/RemoteClient.java
@@ -9,6 +9,7 @@ import dev.vality.disputes.schedule.converter.DisputeContextConverter;
import dev.vality.disputes.schedule.converter.DisputeParamsConverter;
import dev.vality.disputes.schedule.model.ProviderData;
import dev.vality.disputes.schedule.service.ProviderIfaceBuilder;
+import dev.vality.disputes.schedule.service.ProviderRouting;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
@@ -22,17 +23,18 @@ import java.util.List;
@SuppressWarnings({"ParameterName", "LineLength"})
public class RemoteClient {
+ private final ProviderRouting providerRouting;
private final ProviderIfaceBuilder providerIfaceBuilder;
- private final DisputeContextConverter disputeContextConverter;
private final DisputeParamsConverter disputeParamsConverter;
+ private final DisputeContextConverter disputeContextConverter;
@SneakyThrows
public DisputeCreatedResult createDispute(Dispute dispute, List attachments, ProviderData providerData) {
- log.debug("Trying to call dominant for RemoteClient {}", dispute.getId());
+ providerRouting.initRouteUrl(providerData);
+ log.debug("Trying to call ProviderIfaceBuilder {}", dispute.getId());
+ var remoteClient = providerIfaceBuilder.buildTHSpawnClient(providerData.getRouteUrl());
log.debug("Trying to build disputeParams {}", dispute.getId());
var disputeParams = disputeParamsConverter.convert(dispute, attachments, providerData.getOptions());
- log.debug("Trying to call ProviderIfaceBuilder {}", dispute.getId());
- var remoteClient = providerIfaceBuilder.buildTHSpawnClient(providerData);
log.debug("Trying to routed remote provider's createDispute() call {}", dispute.getId());
var result = remoteClient.createDispute(disputeParams);
log.info("Routed remote provider's createDispute() has been called {} {}", dispute.getId(), result);
@@ -41,11 +43,11 @@ public class RemoteClient {
@SneakyThrows
public DisputeStatusResult checkDisputeStatus(Dispute dispute, ProviderDispute providerDispute, ProviderData providerData) {
- log.debug("Trying to call dominant for RemoteClient {}", dispute.getId());
+ providerRouting.initRouteUrl(providerData);
+ log.debug("Trying to call ProviderIfaceBuilder {}", dispute.getId());
+ var remoteClient = providerIfaceBuilder.buildTHSpawnClient(providerData.getRouteUrl());
log.debug("Trying to build disputeContext {}", dispute.getId());
var disputeContext = disputeContextConverter.convert(dispute, providerDispute, providerData.getOptions());
- log.debug("Trying to call ProviderIfaceBuilder {}", dispute.getId());
- var remoteClient = providerIfaceBuilder.buildTHSpawnClient(providerData);
log.debug("Trying to routed remote provider's checkDisputeStatus() call {}", dispute.getId());
var result = remoteClient.checkDisputeStatus(disputeContext);
log.info("Routed remote provider's checkDisputeStatus() has been called {} {}", dispute.getId(), result);
diff --git a/src/main/java/dev/vality/disputes/schedule/handler/DisputeCreateResultHandler.java b/src/main/java/dev/vality/disputes/schedule/handler/DisputeCreateResultHandler.java
new file mode 100644
index 0000000..9fa3ffc
--- /dev/null
+++ b/src/main/java/dev/vality/disputes/schedule/handler/DisputeCreateResultHandler.java
@@ -0,0 +1,83 @@
+package dev.vality.disputes.schedule.handler;
+
+import dev.vality.disputes.admin.callback.CallbackNotifier;
+import dev.vality.disputes.admin.management.MdcTopicProducer;
+import dev.vality.disputes.dao.DisputeDao;
+import dev.vality.disputes.dao.ProviderDisputeDao;
+import dev.vality.disputes.domain.enums.DisputeStatus;
+import dev.vality.disputes.domain.tables.pojos.Dispute;
+import dev.vality.disputes.domain.tables.pojos.ProviderDispute;
+import dev.vality.disputes.polling.ExponentialBackOffPollingServiceWrapper;
+import dev.vality.disputes.provider.DisputeCreatedResult;
+import dev.vality.disputes.schedule.client.DefaultRemoteClient;
+import dev.vality.disputes.schedule.model.ProviderData;
+import dev.vality.disputes.utils.ErrorFormatter;
+import dev.vality.woody.api.flow.error.WRuntimeException;
+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 static dev.vality.disputes.constant.ModerationPrefix.DISPUTES_UNKNOWN_MAPPING;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+@SuppressWarnings({"ParameterName", "LineLength", "MissingSwitchDefault"})
+public class DisputeCreateResultHandler {
+
+ private final DisputeDao disputeDao;
+ private final ExponentialBackOffPollingServiceWrapper exponentialBackOffPollingService;
+ private final DefaultRemoteClient defaultRemoteClient;
+ private final ProviderDisputeDao providerDisputeDao;
+ private final CallbackNotifier callbackNotifier;
+ private final MdcTopicProducer mdcTopicProducer;
+
+ @Transactional(propagation = Propagation.REQUIRED)
+ public void handleSuccessResult(Dispute dispute, DisputeCreatedResult result, ProviderData providerData) {
+ var nextCheckAfter = exponentialBackOffPollingService.prepareNextPollingInterval(dispute, providerData.getOptions());
+ providerDisputeDao.save(new ProviderDispute(result.getSuccessResult().getProviderDisputeId(), dispute.getId()));
+ log.info("Trying to set pending Dispute status {}, {}", dispute, result);
+ var isDefaultRouteUrl = defaultRemoteClient.routeUrlEquals(providerData);
+ disputeDao.update(dispute.getId(), !isDefaultRouteUrl ? DisputeStatus.pending : DisputeStatus.manual_pending, nextCheckAfter);
+ log.debug("Dispute status has been set to pending {}", dispute.getId());
+ }
+
+ @Transactional(propagation = Propagation.REQUIRED)
+ public void handleFailResult(Dispute dispute, DisputeCreatedResult result) {
+ var failure = result.getFailResult().getFailure();
+ var errorMessage = ErrorFormatter.getErrorMessage(failure);
+ if (errorMessage.startsWith(DISPUTES_UNKNOWN_MAPPING)) {
+ handleUnexpectedResultMapping(dispute, failure.getCode(), failure.getReason());
+ } else {
+ log.warn("Trying to set failed Dispute status {}, {}", dispute.getId(), errorMessage);
+ disputeDao.update(dispute.getId(), DisputeStatus.failed, errorMessage, failure.getCode());
+ log.debug("Dispute status has been set to failed {}", dispute.getId());
+ }
+ }
+
+ @Transactional(propagation = Propagation.REQUIRED)
+ public void handleAlreadyExistResult(Dispute dispute) {
+ callbackNotifier.sendDisputeAlreadyCreated(dispute);
+ mdcTopicProducer.sendCreated(dispute, DisputeStatus.already_exist_created, "dispute already exist");
+ log.info("Trying to set {} Dispute status {}", DisputeStatus.already_exist_created, dispute);
+ disputeDao.update(dispute.getId(), DisputeStatus.already_exist_created);
+ log.debug("Dispute status has been set to {} {}", DisputeStatus.already_exist_created, dispute.getId());
+ }
+
+ @Transactional(propagation = Propagation.REQUIRED)
+ public void handleUnexpectedResultMapping(Dispute dispute, WRuntimeException e) {
+ var errorMessage = e.getErrorDefinition().getErrorReason();
+ handleUnexpectedResultMapping(dispute, errorMessage, null);
+ }
+
+ private void handleUnexpectedResultMapping(Dispute dispute, String errorCode, String errorDescription) {
+ callbackNotifier.sendDisputeFailedReviewRequired(dispute, errorCode, errorDescription);
+ var errorMessage = ErrorFormatter.getErrorMessage(errorCode, errorDescription);
+ mdcTopicProducer.sendCreated(dispute, DisputeStatus.manual_created, errorMessage);
+ log.warn("Trying to set manual_created Dispute status {}, {}", dispute.getId(), errorMessage);
+ disputeDao.update(dispute.getId(), DisputeStatus.manual_created, errorMessage);
+ log.debug("Dispute status has been set to manual_created {}", dispute.getId());
+ }
+}
diff --git a/src/main/java/dev/vality/disputes/schedule/handler/DisputeStatusResultHandler.java b/src/main/java/dev/vality/disputes/schedule/handler/DisputeStatusResultHandler.java
index 1d681de..a5a40c3 100644
--- a/src/main/java/dev/vality/disputes/schedule/handler/DisputeStatusResultHandler.java
+++ b/src/main/java/dev/vality/disputes/schedule/handler/DisputeStatusResultHandler.java
@@ -1,19 +1,26 @@
package dev.vality.disputes.schedule.handler;
+import dev.vality.disputes.admin.callback.CallbackNotifier;
+import dev.vality.disputes.admin.management.MdcTopicProducer;
+import dev.vality.disputes.constant.ErrorReason;
import dev.vality.disputes.dao.DisputeDao;
import dev.vality.disputes.domain.enums.DisputeStatus;
import dev.vality.disputes.domain.tables.pojos.Dispute;
import dev.vality.disputes.polling.ExponentialBackOffPollingServiceWrapper;
import dev.vality.disputes.provider.DisputeStatusResult;
import dev.vality.disputes.utils.ErrorFormatter;
+import dev.vality.woody.api.flow.error.WRuntimeException;
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.List;
import java.util.Map;
+import static dev.vality.disputes.constant.ModerationPrefix.DISPUTES_UNKNOWN_MAPPING;
+
@Slf4j
@Service
@RequiredArgsConstructor
@@ -22,6 +29,8 @@ public class DisputeStatusResultHandler {
private final DisputeDao disputeDao;
private final ExponentialBackOffPollingServiceWrapper exponentialBackOffPollingService;
+ private final CallbackNotifier callbackNotifier;
+ private final MdcTopicProducer mdcTopicProducer;
@Transactional(propagation = Propagation.REQUIRED)
public void handleStatusPending(Dispute dispute, DisputeStatusResult result, Map options) {
@@ -36,17 +45,48 @@ public class DisputeStatusResultHandler {
@Transactional(propagation = Propagation.REQUIRED)
public void handleStatusFail(Dispute dispute, DisputeStatusResult result) {
- var errorMessage = ErrorFormatter.getErrorMessage(result.getStatusFail().getFailure());
- 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.getId());
+ var failure = result.getStatusFail().getFailure();
+ var errorMessage = ErrorFormatter.getErrorMessage(failure);
+ if (errorMessage.startsWith(DISPUTES_UNKNOWN_MAPPING)) {
+ handleUnexpectedResultMapping(dispute, failure.getCode(), failure.getReason());
+ } else {
+ log.warn("Trying to set failed Dispute status {}, {}", dispute.getId(), errorMessage);
+ disputeDao.update(dispute.getId(), DisputeStatus.failed, errorMessage, failure.getCode());
+ log.debug("Dispute status has been set to failed {}", dispute.getId());
+ }
}
@Transactional(propagation = Propagation.REQUIRED)
public void handleStatusSuccess(Dispute dispute, DisputeStatusResult result) {
+ callbackNotifier.sendDisputeReadyForCreateAdjustment(List.of(dispute));
+ mdcTopicProducer.sendReadyForCreateAdjustments(List.of(dispute));
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.getId());
}
+
+ @Transactional(propagation = Propagation.REQUIRED)
+ public void handlePoolingExpired(Dispute dispute) {
+ callbackNotifier.sendDisputePoolingExpired(dispute);
+ mdcTopicProducer.sendPoolingExpired(dispute);
+ log.warn("Trying to set manual_pending Dispute status with POOLING_EXPIRED error reason {}", dispute.getId());
+ disputeDao.update(dispute.getId(), DisputeStatus.manual_pending, ErrorReason.POOLING_EXPIRED);
+ log.debug("Dispute status has been set to manual_pending {}", dispute.getId());
+ }
+
+ @Transactional(propagation = Propagation.REQUIRED)
+ public void handleUnexpectedResultMapping(Dispute dispute, WRuntimeException e) {
+ var errorMessage = e.getErrorDefinition().getErrorReason();
+ handleUnexpectedResultMapping(dispute, errorMessage, null);
+ }
+
+ private void handleUnexpectedResultMapping(Dispute dispute, String errorCode, String errorDescription) {
+ callbackNotifier.sendDisputeFailedReviewRequired(dispute, errorCode, errorDescription);
+ var errorMessage = ErrorFormatter.getErrorMessage(errorCode, errorDescription);
+ mdcTopicProducer.sendCreated(dispute, DisputeStatus.manual_pending, errorMessage);
+ log.warn("Trying to set manual_pending Dispute status {}, {}", dispute.getId(), errorMessage);
+ disputeDao.update(dispute.getId(), DisputeStatus.manual_pending, errorMessage);
+ log.debug("Dispute status has been set to manual_pending {}", dispute.getId());
+ }
}
diff --git a/src/main/java/dev/vality/disputes/schedule/model/ProviderData.java b/src/main/java/dev/vality/disputes/schedule/model/ProviderData.java
index 2c21e56..028e463 100644
--- a/src/main/java/dev/vality/disputes/schedule/model/ProviderData.java
+++ b/src/main/java/dev/vality/disputes/schedule/model/ProviderData.java
@@ -11,5 +11,6 @@ public class ProviderData {
private Map options;
private String defaultProviderUrl;
+ private String routeUrl;
}
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 cd957f5..484ba33 100644
--- a/src/main/java/dev/vality/disputes/schedule/service/CreatedDisputesService.java
+++ b/src/main/java/dev/vality/disputes/schedule/service/CreatedDisputesService.java
@@ -3,18 +3,15 @@ package dev.vality.disputes.schedule.service;
import dev.vality.damsel.payment_processing.InvoicePayment;
import dev.vality.disputes.constant.ErrorReason;
import dev.vality.disputes.dao.DisputeDao;
-import dev.vality.disputes.dao.ProviderDisputeDao;
import dev.vality.disputes.domain.enums.DisputeStatus;
import dev.vality.disputes.domain.tables.pojos.Dispute;
-import dev.vality.disputes.domain.tables.pojos.ProviderDispute;
-import dev.vality.disputes.manualparsing.ManualParsingTopic;
-import dev.vality.disputes.polling.ExponentialBackOffPollingServiceWrapper;
-import dev.vality.disputes.provider.Attachment;
import dev.vality.disputes.provider.DisputeCreatedResult;
+import dev.vality.disputes.schedule.catcher.WRuntimeExceptionCatcher;
+import dev.vality.disputes.schedule.client.DefaultRemoteClient;
import dev.vality.disputes.schedule.client.RemoteClient;
+import dev.vality.disputes.schedule.handler.DisputeCreateResultHandler;
+import dev.vality.disputes.schedule.model.ProviderData;
import dev.vality.disputes.service.external.InvoicingService;
-import dev.vality.disputes.utils.ErrorFormatter;
-import dev.vality.woody.api.flow.error.WRuntimeException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -31,18 +28,17 @@ import static dev.vality.disputes.constant.TerminalOptionsField.DISPUTE_FLOW_PRO
@Slf4j
@Service
@RequiredArgsConstructor
-@SuppressWarnings({"ParameterName", "LineLength", "MissingSwitchDefault"})
+@SuppressWarnings({"MemberName", "ParameterName", "LineLength", "MissingSwitchDefault"})
public class CreatedDisputesService {
private final RemoteClient remoteClient;
private final DisputeDao disputeDao;
- private final ProviderDisputeDao providerDisputeDao;
private final CreatedAttachmentsService createdAttachmentsService;
private final InvoicingService invoicingService;
- private final ExponentialBackOffPollingServiceWrapper exponentialBackOffPollingService;
private final ProviderDataService providerDataService;
- private final ExternalGatewayChecker externalGatewayChecker;
- private final ManualParsingTopic manualParsingTopic;
+ private final DefaultRemoteClient defaultRemoteClient;
+ private final DisputeCreateResultHandler disputeCreateResultHandler;
+ private final WRuntimeExceptionCatcher wRuntimeExceptionCatcher;
@Transactional(propagation = Propagation.REQUIRED)
public List getCreatedDisputesForUpdateSkipLocked(int batchSize) {
@@ -87,54 +83,40 @@ public class CreatedDisputesService {
|| isNotProvidersDisputesApiExist(options)) {
// отправлять на ручной разбор, если выставлена опция
// DISPUTE_FLOW_CAPTURED_BLOCKED или не выставлена DISPUTE_FLOW_PROVIDERS_API_EXIST
- log.warn("finishTaskWithManualParsingFlowActivation, options capt={}, apiExist={}", isCapturedBlockedForDispute(options), isNotProvidersDisputesApiExist(options));
- finishTaskWithManualParsingFlowActivation(dispute, attachments, DisputeStatus.manual_created);
+ log.warn("Trying to call defaultRemoteClient.createDispute(), options capt={}, apiExist={}", isCapturedBlockedForDispute(options), isNotProvidersDisputesApiExist(options));
+ wRuntimeExceptionCatcher.catchUnexpectedResultMapping(
+ () -> {
+ var result = defaultRemoteClient.createDispute(dispute, attachments, providerData);
+ finishTask(dispute, result, providerData);
+ },
+ e -> disputeCreateResultHandler.handleUnexpectedResultMapping(dispute, e));
return;
}
- try {
- var result = remoteClient.createDispute(dispute, attachments, providerData);
- finishTask(dispute, attachments, result, options);
- } catch (WRuntimeException e) {
- if (externalGatewayChecker.isNotProvidersDisputesApiExist(providerData, e)) {
- // отправлять на ручной разбор, если API диспутов на провайдере не реализовано
- // (тогда при тесте соединения вернется 404)
- log.warn("finishTaskWithManualParsingFlowActivation with externalGatewayChecker", e);
- finishTaskWithManualParsingFlowActivation(dispute, attachments, DisputeStatus.manual_created);
- return;
- }
- throw e;
- }
+ wRuntimeExceptionCatcher.catchUnexpectedResultMapping(
+ () -> wRuntimeExceptionCatcher.catchProvidersDisputesApiNotExist(
+ providerData,
+ () -> {
+ var result = remoteClient.createDispute(dispute, attachments, providerData);
+ finishTask(dispute, result, providerData);
+ },
+ () -> wRuntimeExceptionCatcher.catchUnexpectedResultMapping(
+ () -> {
+ var result = defaultRemoteClient.createDispute(dispute, attachments, providerData);
+ finishTask(dispute, result, providerData);
+ },
+ e -> disputeCreateResultHandler.handleUnexpectedResultMapping(dispute, e))),
+ e -> disputeCreateResultHandler.handleUnexpectedResultMapping(dispute, e));
}
@Transactional(propagation = Propagation.REQUIRED)
- void finishTask(Dispute dispute, List attachments, DisputeCreatedResult result, Map options) {
+ void finishTask(Dispute dispute, DisputeCreatedResult result, ProviderData providerData) {
switch (result.getSetField()) {
- case SUCCESS_RESULT -> {
- var nextCheckAfter = exponentialBackOffPollingService.prepareNextPollingInterval(dispute, options);
- 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.getId());
- }
- case FAIL_RESULT -> {
- var errorMessage = ErrorFormatter.getErrorMessage(result.getFailResult().getFailure());
- 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.getId());
- }
- case ALREADY_EXIST_RESULT ->
- finishTaskWithManualParsingFlowActivation(dispute, attachments, DisputeStatus.already_exist_created);
+ case SUCCESS_RESULT -> disputeCreateResultHandler.handleSuccessResult(dispute, result, providerData);
+ case FAIL_RESULT -> disputeCreateResultHandler.handleFailResult(dispute, result);
+ case ALREADY_EXIST_RESULT -> disputeCreateResultHandler.handleAlreadyExistResult(dispute);
}
}
- @Transactional(propagation = Propagation.REQUIRED)
- 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());
- }
-
private boolean isCapturedBlockedForDispute(Map options) {
return options.containsKey(DISPUTE_FLOW_CAPTURED_BLOCKED);
}
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 7c43c12..ee0c8d0 100644
--- a/src/main/java/dev/vality/disputes/schedule/service/ExternalGatewayChecker.java
+++ b/src/main/java/dev/vality/disputes/schedule/service/ExternalGatewayChecker.java
@@ -22,23 +22,32 @@ public class ExternalGatewayChecker {
private final CloseableHttpClient httpClient;
private final ProviderRouting providerRouting;
- public boolean isNotProvidersDisputesApiExist(ProviderData providerData, WRuntimeException e) {
+ public boolean isProvidersDisputesUnexpectedResultMapping(WRuntimeException e) {
return e.getErrorDefinition() != null
&& e.getErrorDefinition().getGenerationSource() == WErrorSource.EXTERNAL
&& e.getErrorDefinition().getErrorType() == WErrorType.UNEXPECTED_ERROR
&& e.getErrorDefinition().getErrorSource() == WErrorSource.INTERNAL
- && isNotFoundProvidersDisputesApi(providerData);
+ && e.getErrorDefinition().getErrorReason() != null
+ && e.getErrorDefinition().getErrorReason().contains("Unexpected result, code = ");
+ }
+
+ public boolean isProvidersDisputesApiNotExist(ProviderData providerData, WRuntimeException e) {
+ return e.getErrorDefinition() != null
+ && e.getErrorDefinition().getGenerationSource() == WErrorSource.EXTERNAL
+ && e.getErrorDefinition().getErrorType() == WErrorType.UNEXPECTED_ERROR
+ && e.getErrorDefinition().getErrorSource() == WErrorSource.INTERNAL
+ && isProvidersDisputesApiNotFound(providerData);
}
@SneakyThrows
- private Boolean isNotFoundProvidersDisputesApi(ProviderData providerData) {
+ private Boolean isProvidersDisputesApiNotFound(ProviderData providerData) {
return httpClient.execute(new HttpGet(getRouteUrl(providerData)), isNotFoundResponse());
}
private String getRouteUrl(ProviderData providerData) {
- var routeUrl = providerRouting.getRouteUrl(providerData);
- log.debug("Check adapter connection, routeUrl={}", routeUrl);
- return routeUrl;
+ providerRouting.initRouteUrl(providerData);
+ log.debug("Check adapter connection, routeUrl={}", providerData.getRouteUrl());
+ return providerData.getRouteUrl();
}
private HttpClientResponseHandler isNotFoundResponse() {
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 c15b0e9..6ed4c7c 100644
--- a/src/main/java/dev/vality/disputes/schedule/service/PendingDisputesService.java
+++ b/src/main/java/dev/vality/disputes/schedule/service/PendingDisputesService.java
@@ -1,14 +1,13 @@
package dev.vality.disputes.schedule.service;
-import dev.vality.disputes.constant.ErrorReason;
import dev.vality.disputes.dao.DisputeDao;
import dev.vality.disputes.dao.ProviderDisputeDao;
import dev.vality.disputes.domain.enums.DisputeStatus;
import dev.vality.disputes.domain.tables.pojos.Dispute;
-import dev.vality.disputes.manualparsing.ManualParsingTopic;
import dev.vality.disputes.polling.ExponentialBackOffPollingServiceWrapper;
import dev.vality.disputes.polling.PollingInfoService;
import dev.vality.disputes.provider.DisputeStatusResult;
+import dev.vality.disputes.schedule.catcher.WRuntimeExceptionCatcher;
import dev.vality.disputes.schedule.client.RemoteClient;
import dev.vality.disputes.schedule.handler.DisputeStatusResultHandler;
import lombok.RequiredArgsConstructor;
@@ -24,7 +23,7 @@ import java.util.Map;
@Slf4j
@Service
@RequiredArgsConstructor
-@SuppressWarnings({"ParameterName", "LineLength", "MissingSwitchDefault"})
+@SuppressWarnings({"MemberName", "ParameterName", "LineLength", "MissingSwitchDefault"})
public class PendingDisputesService {
private final RemoteClient remoteClient;
@@ -34,7 +33,7 @@ public class PendingDisputesService {
private final ExponentialBackOffPollingServiceWrapper exponentialBackOffPollingService;
private final ProviderDataService providerDataService;
private final DisputeStatusResultHandler disputeStatusResultHandler;
- private final ManualParsingTopic manualParsingTopic;
+ private final WRuntimeExceptionCatcher wRuntimeExceptionCatcher;
@Transactional(propagation = Propagation.REQUIRED)
public List getPendingDisputesForUpdateSkipLocked(int batchSize) {
@@ -65,15 +64,16 @@ public class PendingDisputesService {
return;
}
if (pollingInfoService.isDeadline(dispute)) {
- manualParsingTopic.sendPoolingExpired(dispute);
- log.error("Trying to set manual_pending Dispute status with POOLING_EXPIRED error reason {}", dispute.getId());
- disputeDao.update(dispute.getId(), DisputeStatus.manual_pending, ErrorReason.POOLING_EXPIRED);
- log.debug("Dispute status has been set to manual_pending {}", dispute.getId());
+ disputeStatusResultHandler.handlePoolingExpired(dispute);
return;
}
log.debug("ProviderDispute has been found {}", dispute.getId());
- var result = remoteClient.checkDisputeStatus(dispute, providerDispute, providerData);
- finishTask(dispute, result, providerData.getOptions());
+ wRuntimeExceptionCatcher.catchUnexpectedResultMapping(
+ () -> {
+ var result = remoteClient.checkDisputeStatus(dispute, providerDispute, providerData);
+ finishTask(dispute, result, providerData.getOptions());
+ },
+ e -> disputeStatusResultHandler.handleUnexpectedResultMapping(dispute, e));
}
@Transactional(propagation = Propagation.REQUIRED)
diff --git a/src/main/java/dev/vality/disputes/schedule/service/ProviderDataService.java b/src/main/java/dev/vality/disputes/schedule/service/ProviderDataService.java
index ad74176..3464948 100644
--- a/src/main/java/dev/vality/disputes/schedule/service/ProviderDataService.java
+++ b/src/main/java/dev/vality/disputes/schedule/service/ProviderDataService.java
@@ -38,7 +38,7 @@ public class ProviderDataService {
var terminal = dominantAsyncService.getTerminal(payment.getRoute().getTerminal());
var proxy = dominantAsyncService.getProxy(provider.get().getProxy().getRef());
return ProviderData.builder()
- .options(OptionsExtractors.mergeOptions(provider.get(), proxy.get(), terminal.get()))
+ .options(OptionsExtractors.mergeOptions(provider.get(), proxy.get(), terminal.get()))
.defaultProviderUrl(proxy.get().getUrl())
.build();
}
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 c92cfb8..2b59b84 100644
--- a/src/main/java/dev/vality/disputes/schedule/service/ProviderIfaceBuilder.java
+++ b/src/main/java/dev/vality/disputes/schedule/service/ProviderIfaceBuilder.java
@@ -2,7 +2,6 @@ package dev.vality.disputes.schedule.service;
import dev.vality.disputes.config.properties.AdaptersConnectionProperties;
import dev.vality.disputes.provider.ProviderDisputesServiceSrv;
-import dev.vality.disputes.schedule.model.ProviderData;
import dev.vality.woody.thrift.impl.http.THSpawnClientBuilder;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -18,12 +17,10 @@ import java.util.concurrent.TimeUnit;
@SuppressWarnings({"AbbreviationAsWordInName", "LineLength"})
public class ProviderIfaceBuilder {
- private final ProviderRouting providerRouting;
private final AdaptersConnectionProperties adaptersConnectionProperties;
- @Cacheable(value = "adapters", key = "#providerData.defaultProviderUrl", cacheManager = "adaptersCacheManager")
- public ProviderDisputesServiceSrv.Iface buildTHSpawnClient(ProviderData providerData) {
- var routeUrl = providerRouting.getRouteUrl(providerData);
+ @Cacheable(value = "adapters", key = "#root.args[0]", cacheManager = "adaptersCacheManager")
+ public ProviderDisputesServiceSrv.Iface buildTHSpawnClient(String routeUrl) {
log.info("Creating new client for url: {}", routeUrl);
return new THSpawnClientBuilder()
.withNetworkTimeout((int) TimeUnit.SECONDS.toMillis(adaptersConnectionProperties.getTimeoutSec()))
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 6627094..82a5288 100644
--- a/src/main/java/dev/vality/disputes/schedule/service/ProviderRouting.java
+++ b/src/main/java/dev/vality/disputes/schedule/service/ProviderRouting.java
@@ -18,12 +18,12 @@ public class ProviderRouting {
private static final String DISPUTES_URL_POSTFIX_DEFAULT = "disputes";
private static final String OPTION_DISPUTES_URL_FIELD_NAME = "disputes_url";
- public String getRouteUrl(ProviderData providerData) {
+ public void initRouteUrl(ProviderData providerData) {
var url = providerData.getOptions().get(OPTION_DISPUTES_URL_FIELD_NAME);
if (ObjectUtils.isEmpty(url)) {
url = createDefaultRouteUrl(providerData.getDefaultProviderUrl());
}
- return url;
+ providerData.setRouteUrl(url);
}
private String createDefaultRouteUrl(String defaultProviderUrl) {
diff --git a/src/main/java/dev/vality/disputes/service/external/DisputesTgBotService.java b/src/main/java/dev/vality/disputes/service/external/DisputesTgBotService.java
new file mode 100644
index 0000000..2019d77
--- /dev/null
+++ b/src/main/java/dev/vality/disputes/service/external/DisputesTgBotService.java
@@ -0,0 +1,24 @@
+package dev.vality.disputes.service.external;
+
+import dev.vality.disputes.admin.DisputeAlreadyCreated;
+import dev.vality.disputes.admin.DisputeFailedReviewRequired;
+import dev.vality.disputes.admin.DisputePoolingExpired;
+import dev.vality.disputes.admin.DisputeReadyForCreateAdjustment;
+import dev.vality.disputes.provider.DisputeCreatedResult;
+import dev.vality.disputes.provider.DisputeParams;
+
+import java.util.List;
+
+public interface DisputesTgBotService {
+
+ DisputeCreatedResult createDispute(DisputeParams disputeParams);
+
+ void sendDisputeAlreadyCreated(DisputeAlreadyCreated disputeAlreadyCreated);
+
+ void sendDisputePoolingExpired(DisputePoolingExpired disputePoolingExpired);
+
+ void sendDisputeReadyForCreateAdjustment(List disputeReadyForCreateAdjustments);
+
+ void sendDisputeFailedReviewRequired(DisputeFailedReviewRequired disputeFailedReviewRequired);
+
+}
diff --git a/src/main/java/dev/vality/disputes/service/external/impl/DisputesTgBotServiceImpl.java b/src/main/java/dev/vality/disputes/service/external/impl/DisputesTgBotServiceImpl.java
new file mode 100644
index 0000000..d4c4bd7
--- /dev/null
+++ b/src/main/java/dev/vality/disputes/service/external/impl/DisputesTgBotServiceImpl.java
@@ -0,0 +1,75 @@
+package dev.vality.disputes.service.external.impl;
+
+import dev.vality.disputes.admin.*;
+import dev.vality.disputes.provider.DisputeCreatedResult;
+import dev.vality.disputes.provider.DisputeParams;
+import dev.vality.disputes.provider.ProviderDisputesServiceSrv;
+import dev.vality.disputes.service.external.DisputesTgBotService;
+import lombok.RequiredArgsConstructor;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Service
+@RequiredArgsConstructor
+@Slf4j
+@SuppressWarnings({"LineLength"})
+public class DisputesTgBotServiceImpl implements DisputesTgBotService {
+
+ public final ProviderDisputesServiceSrv.Iface providerDisputesTgBotClient;
+ public final AdminCallbackServiceSrv.Iface adminCallbackDisputesTgBotClient;
+
+ @Override
+ @SneakyThrows
+ public DisputeCreatedResult createDispute(DisputeParams disputeParams) {
+ log.debug("Trying to call providerDisputesTgBotClient.createDispute() {} {}", disputeParams.getDisputeId(), disputeParams.getTransactionContext().getInvoiceId());
+ var invoice = providerDisputesTgBotClient.createDispute(disputeParams);
+ log.debug("providerDisputesTgBotClient.createDispute() has been called {} {}", disputeParams.getDisputeId(), disputeParams.getTransactionContext().getInvoiceId());
+ return invoice;
+ }
+
+ @Override
+ @SneakyThrows
+ public void sendDisputeAlreadyCreated(DisputeAlreadyCreated disputeAlreadyCreated) {
+ log.debug("Trying to call adminCallbackDisputesTgBotClient.sendDisputeAlreadyCreated() {}", disputeAlreadyCreated.getId());
+ adminCallbackDisputesTgBotClient.notify(
+ new NotificationParamsRequest(List.of(Notification.disputeAlreadyCreated(disputeAlreadyCreated))));
+ log.debug("adminCallbackDisputesTgBotClient.sendDisputeAlreadyCreated() has been called {}", disputeAlreadyCreated.getId());
+ }
+
+ @Override
+ @SneakyThrows
+ public void sendDisputePoolingExpired(DisputePoolingExpired disputePoolingExpired) {
+ log.debug("Trying to call adminCallbackDisputesTgBotClient.sendDisputePoolingExpired() {}", disputePoolingExpired.getId());
+ adminCallbackDisputesTgBotClient.notify(
+ new NotificationParamsRequest(List.of(Notification.disputePoolingExpired(disputePoolingExpired))));
+ log.debug("adminCallbackDisputesTgBotClient.sendDisputePoolingExpired() has been called {}", disputePoolingExpired.getId());
+ }
+
+ @Override
+ @SneakyThrows
+ public void sendDisputeReadyForCreateAdjustment(List disputeReadyForCreateAdjustments) {
+ var ids = disputeReadyForCreateAdjustments.stream()
+ .map(DisputeReadyForCreateAdjustment::getId)
+ .map(String::valueOf)
+ .collect(Collectors.joining(", "));
+ log.debug("Trying to call adminCallbackDisputesTgBotClient.sendDisputeReadyForCreateAdjustment() {}", ids);
+ var notifications = disputeReadyForCreateAdjustments.stream()
+ .map(Notification::disputeReadyForCreateAdjustment)
+ .collect(Collectors.toList());
+ adminCallbackDisputesTgBotClient.notify(new NotificationParamsRequest(notifications));
+ log.debug("adminCallbackDisputesTgBotClient.sendDisputeReadyForCreateAdjustment() has been called {}", ids);
+ }
+
+ @Override
+ @SneakyThrows
+ public void sendDisputeFailedReviewRequired(DisputeFailedReviewRequired disputeFailedReviewRequired) {
+ log.debug("Trying to call adminCallbackDisputesTgBotClient.sendDisputeFailedReviewRequired() {}", disputeFailedReviewRequired.getId());
+ adminCallbackDisputesTgBotClient.notify(
+ new NotificationParamsRequest(List.of(Notification.disputeFailedReviewRequired(disputeFailedReviewRequired))));
+ log.debug("adminCallbackDisputesTgBotClient.sendDisputeFailedReviewRequired() has been called {}", disputeFailedReviewRequired.getId());
+ }
+}
diff --git a/src/main/java/dev/vality/disputes/servlet/ManualParsingServlet.java b/src/main/java/dev/vality/disputes/servlet/AdminManagementServlet.java
similarity index 66%
rename from src/main/java/dev/vality/disputes/servlet/ManualParsingServlet.java
rename to src/main/java/dev/vality/disputes/servlet/AdminManagementServlet.java
index 502770c..352cdd9 100644
--- a/src/main/java/dev/vality/disputes/servlet/ManualParsingServlet.java
+++ b/src/main/java/dev/vality/disputes/servlet/AdminManagementServlet.java
@@ -1,6 +1,6 @@
package dev.vality.disputes.servlet;
-import dev.vality.disputes.admin.ManualParsingServiceSrv;
+import dev.vality.disputes.admin.AdminManagementServiceSrv;
import dev.vality.woody.thrift.impl.http.THServiceBuilder;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebServlet;
@@ -8,11 +8,11 @@ import org.springframework.beans.factory.annotation.Autowired;
import java.io.IOException;
-@WebServlet("/disputes-api/v1/manual-parsing")
-public class ManualParsingServlet extends GenericServlet {
+@WebServlet("/disputes-api/v1/admin-management")
+public class AdminManagementServlet extends GenericServlet {
@Autowired
- private ManualParsingServiceSrv.Iface manualParsingHandler;
+ private AdminManagementServiceSrv.Iface adminManagementHandler;
private Servlet servlet;
@@ -20,7 +20,7 @@ public class ManualParsingServlet extends GenericServlet {
public void init(ServletConfig config) throws ServletException {
super.init(config);
servlet = new THServiceBuilder()
- .build(ManualParsingServiceSrv.Iface.class, manualParsingHandler);
+ .build(AdminManagementServiceSrv.Iface.class, adminManagementHandler);
}
@Override
diff --git a/src/main/java/dev/vality/disputes/utils/ErrorFormatter.java b/src/main/java/dev/vality/disputes/utils/ErrorFormatter.java
index 878734a..7096664 100644
--- a/src/main/java/dev/vality/disputes/utils/ErrorFormatter.java
+++ b/src/main/java/dev/vality/disputes/utils/ErrorFormatter.java
@@ -12,4 +12,11 @@ public class ErrorFormatter {
}
return TErrorUtil.toStringVal(failure);
}
+
+ public static String getErrorMessage(String errorCode, String errorDescription) {
+ if (!StringUtils.isBlank(errorDescription)) {
+ return errorCode + ": " + errorDescription;
+ }
+ return errorCode;
+ }
}
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 7557c87..e61cc7e 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -64,6 +64,17 @@ service:
shops:
poolSize: 10
ttlSec: 86400
+ disputes-tg-bot:
+ provider:
+ url: http://localhost:8022/change_it
+ networkTimeout: 5000
+ enabled: false
+ admin:
+ url: http://localhost:8022/change_it
+ networkTimeout: 5000
+ enabled: false
+ mdc-topic-producer:
+ enabled: true
adapters:
connection:
timeoutSec: 30
@@ -106,18 +117,15 @@ dispute:
isScheduleCreatedEnabled: true
isSchedulePendingEnabled: true
isScheduleCreateAdjustmentsEnabled: true
- isScheduleReadyForCreateAdjustmentsEnabled: true
+ isScheduleReadyForCreateAdjustmentsEnabled: false
time:
config:
max-time-polling-min: 600
-manual-parsing-topic:
- enabled: true
-
testcontainers:
postgresql:
- tag: '11.4'
+ tag: '14.12'
http-client:
requestTimeout: 60000
@@ -125,3 +133,7 @@ http-client:
connectionTimeout: 10000
maxTotalPooling: 200
defaultMaxPerRoute: 200
+
+otel:
+ resource: http://localhost:4318/v1/traces
+ timeout: 60000
diff --git a/src/main/resources/db/migration/V3__add_mapping.sql b/src/main/resources/db/migration/V3__add_mapping.sql
new file mode 100644
index 0000000..f5cd381
--- /dev/null
+++ b/src/main/resources/db/migration/V3__add_mapping.sql
@@ -0,0 +1,2 @@
+ALTER TABLE dspt.dispute
+ ADD COLUMN "mapping" CHARACTER VARYING;
diff --git a/src/test/java/dev/vality/disputes/manualparsing/DebugManualParsingController.java b/src/test/java/dev/vality/disputes/admin/management/DebugAdminManagementController.java
similarity index 83%
rename from src/test/java/dev/vality/disputes/manualparsing/DebugManualParsingController.java
rename to src/test/java/dev/vality/disputes/admin/management/DebugAdminManagementController.java
index a0f8200..355be84 100644
--- a/src/test/java/dev/vality/disputes/manualparsing/DebugManualParsingController.java
+++ b/src/test/java/dev/vality/disputes/admin/management/DebugAdminManagementController.java
@@ -1,4 +1,4 @@
-package dev.vality.disputes.manualparsing;
+package dev.vality.disputes.admin.management;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
@@ -16,10 +16,8 @@ import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.ArrayList;
@@ -28,43 +26,49 @@ import java.util.List;
@RestController
@RequiredArgsConstructor
-@RequestMapping({"/debug/disputes-api/manual-parsing"})
+@RequestMapping({"/debug/disputes-api/admin-management"})
@Slf4j
-public class DebugManualParsingController {
+public class DebugAdminManagementController {
- private final ManualParsingServiceSrv.Iface manualParsingHandler;
+ private final AdminManagementServiceSrv.Iface adminManagementHandler;
private final ObjectMapper objectMapper = new ObjectMapper().registerModule(new Jdk8Module());
@PostMapping("/cancel")
@SneakyThrows
public void cancelPending(@RequestBody String body) {
log.debug("cancelPending {}", body);
- manualParsingHandler.cancelPending(objectMapper.readValue(body, CancelParamsRequest.class));
+ adminManagementHandler.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));
+ adminManagementHandler.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));
+ adminManagementHandler.bindCreated(objectMapper.readValue(body, BindParamsRequest.class));
}
@PostMapping("/get")
@SneakyThrows
public DisputeResult getDisputes(@RequestBody String body) {
log.debug("getDispute {}", body);
- var dispute = manualParsingHandler.getDisputes(objectMapper.readValue(body, DisputeParamsRequest.class));
+ var dispute = adminManagementHandler.getDisputes(objectMapper.readValue(body, DisputeParamsRequest.class));
return objectMapper.convertValue(dispute, new TypeReference<>() {
});
}
+ @GetMapping("/disputes")
+ @ResponseStatus(HttpStatus.NOT_FOUND)
+ public void defaultRouteUrl() {
+ log.info("hi");
+ }
+
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
diff --git a/src/test/java/dev/vality/disputes/manualparsing/DebugManualParsingControllerTest.java b/src/test/java/dev/vality/disputes/admin/management/DebugAdminManagementControllerTest.java
similarity index 79%
rename from src/test/java/dev/vality/disputes/manualparsing/DebugManualParsingControllerTest.java
rename to src/test/java/dev/vality/disputes/admin/management/DebugAdminManagementControllerTest.java
index 0489b6e..40c9ad2 100644
--- a/src/test/java/dev/vality/disputes/manualparsing/DebugManualParsingControllerTest.java
+++ b/src/test/java/dev/vality/disputes/admin/management/DebugAdminManagementControllerTest.java
@@ -1,9 +1,9 @@
-package dev.vality.disputes.manualparsing;
+package dev.vality.disputes.admin.management;
+import dev.vality.disputes.admin.AdminManagementServiceSrv;
import dev.vality.disputes.admin.Attachment;
import dev.vality.disputes.admin.Dispute;
import dev.vality.disputes.admin.DisputeResult;
-import dev.vality.disputes.admin.ManualParsingServiceSrv;
import dev.vality.disputes.config.SpringBootUTest;
import lombok.SneakyThrows;
import org.junit.jupiter.api.Test;
@@ -19,17 +19,17 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
@SpringBootUTest
-public class DebugManualParsingControllerTest {
+public class DebugAdminManagementControllerTest {
@MockBean
- private ManualParsingServiceSrv.Iface manualParsingHandler;
+ private AdminManagementServiceSrv.Iface adminManagementHandler;
@Autowired
- private DebugManualParsingController debugManualParsingController;
+ private DebugAdminManagementController debugAdminManagementController;
@Test
@SneakyThrows
public void checkSerialization() {
- debugManualParsingController.approvePending("""
+ debugAdminManagementController.approvePending("""
{
"approveParams": [
{
@@ -39,7 +39,7 @@ public class DebugManualParsingControllerTest {
]
}
""");
- debugManualParsingController.cancelPending("""
+ debugAdminManagementController.cancelPending("""
{
"cancelParams": [
{
@@ -49,7 +49,7 @@ public class DebugManualParsingControllerTest {
]
}
""");
- debugManualParsingController.cancelPending("""
+ debugAdminManagementController.cancelPending("""
{
"cancelParams": [
{
@@ -59,7 +59,7 @@ public class DebugManualParsingControllerTest {
]
}
""");
- debugManualParsingController.bindCreated("""
+ debugAdminManagementController.bindCreated("""
{
"bindParams": [
{
@@ -77,9 +77,9 @@ public class DebugManualParsingControllerTest {
randomed.setDisputes(List.of(
randomThrift(Dispute.class).setAttachments(List.of(new Attachment().setData(b))),
randomThrift(Dispute.class).setAttachments(List.of(new Attachment().setData(a)))));
- given(manualParsingHandler.getDisputes(any()))
+ given(adminManagementHandler.getDisputes(any()))
.willReturn(randomed);
- var disputes = debugManualParsingController.getDisputes("""
+ var disputes = debugAdminManagementController.getDisputes("""
{
"disputeParams": [
{
diff --git a/src/test/java/dev/vality/disputes/manualparsing/DebugManualParsingHandlerTest.java b/src/test/java/dev/vality/disputes/admin/management/DebugAdminManagementHandlerTest.java
similarity index 78%
rename from src/test/java/dev/vality/disputes/manualparsing/DebugManualParsingHandlerTest.java
rename to src/test/java/dev/vality/disputes/admin/management/DebugAdminManagementHandlerTest.java
index b76358d..bbabae1 100644
--- a/src/test/java/dev/vality/disputes/manualparsing/DebugManualParsingHandlerTest.java
+++ b/src/test/java/dev/vality/disputes/admin/management/DebugAdminManagementHandlerTest.java
@@ -1,4 +1,4 @@
-package dev.vality.disputes.manualparsing;
+package dev.vality.disputes.admin.management;
import dev.vality.disputes.config.WireMockSpringBootITest;
import dev.vality.disputes.dao.DisputeDao;
@@ -20,7 +20,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
@WireMockSpringBootITest
@Import({PendingDisputesTestService.class})
-public class DebugManualParsingHandlerTest {
+public class DebugAdminManagementHandlerTest {
@Autowired
private DisputeDao disputeDao;
@@ -31,19 +31,19 @@ public class DebugManualParsingHandlerTest {
@Autowired
private PendingDisputesTestService pendingDisputesTestService;
@Autowired
- private DebugManualParsingController debugManualParsingController;
+ private DebugAdminManagementController debugAdminManagementController;
@Test
public void testCancelCreateAdjustment() {
var disputeId = pendingDisputesTestService.callPendingDisputeRemotely();
- debugManualParsingController.cancelPending(getCancelRequest(disputeId.toString()));
+ debugAdminManagementController.cancelPending(getCancelRequest(disputeId.toString()));
assertEquals(DisputeStatus.cancelled, disputeDao.get(disputeId).get().getStatus());
}
@Test
public void testCancelPending() {
var disputeId = createdDisputesTestService.callCreateDisputeRemotely();
- debugManualParsingController.cancelPending(getCancelRequest(disputeId.toString()));
+ debugAdminManagementController.cancelPending(getCancelRequest(disputeId.toString()));
assertEquals(DisputeStatus.cancelled, disputeDao.get(disputeId).get().getStatus());
}
@@ -51,14 +51,14 @@ public class DebugManualParsingHandlerTest {
public void testCancelFailed() {
var disputeId = pendingDisputesTestService.callPendingDisputeRemotely();
disputeDao.update(disputeId, DisputeStatus.failed);
- debugManualParsingController.cancelPending(getCancelRequest(disputeId.toString()));
+ debugAdminManagementController.cancelPending(getCancelRequest(disputeId.toString()));
assertEquals(DisputeStatus.failed, disputeDao.get(disputeId).get().getStatus());
}
@Test
public void testApproveCreateAdjustmentWithCallHg() {
var disputeId = pendingDisputesTestService.callPendingDisputeRemotely();
- debugManualParsingController.approvePending(getApproveRequest(disputeId.toString(), false));
+ debugAdminManagementController.approvePending(getApproveRequest(disputeId.toString(), false));
assertEquals(DisputeStatus.create_adjustment, disputeDao.get(disputeId).get().getStatus());
disputeDao.update(disputeId, DisputeStatus.failed);
}
@@ -66,7 +66,7 @@ public class DebugManualParsingHandlerTest {
@Test
public void testApproveCreateAdjustmentWithSkipHg() {
var disputeId = pendingDisputesTestService.callPendingDisputeRemotely();
- debugManualParsingController.approvePending(getApproveRequest(disputeId.toString(), true));
+ debugAdminManagementController.approvePending(getApproveRequest(disputeId.toString(), true));
assertEquals(DisputeStatus.succeeded, disputeDao.get(disputeId).get().getStatus());
disputeDao.update(disputeId, DisputeStatus.failed);
}
@@ -74,7 +74,7 @@ public class DebugManualParsingHandlerTest {
@Test
public void testApprovePendingWithSkipHg() {
var disputeId = createdDisputesTestService.callCreateDisputeRemotely();
- debugManualParsingController.approvePending(getApproveRequest(disputeId.toString(), true));
+ debugAdminManagementController.approvePending(getApproveRequest(disputeId.toString(), true));
assertEquals(DisputeStatus.succeeded, disputeDao.get(disputeId).get().getStatus());
disputeDao.update(disputeId, DisputeStatus.failed);
}
@@ -83,7 +83,7 @@ public class DebugManualParsingHandlerTest {
public void testApproveFailed() {
var disputeId = pendingDisputesTestService.callPendingDisputeRemotely();
disputeDao.update(disputeId, DisputeStatus.failed);
- debugManualParsingController.approvePending(getApproveRequest(disputeId.toString(), true));
+ debugAdminManagementController.approvePending(getApproveRequest(disputeId.toString(), true));
assertEquals(DisputeStatus.failed, disputeDao.get(disputeId).get().getStatus());
}
@@ -91,7 +91,7 @@ public class DebugManualParsingHandlerTest {
public void testBindCreatedCreateAdjustment() {
var disputeId = pendingDisputesTestService.callPendingDisputeRemotely();
var providerDisputeId = generateId();
- debugManualParsingController.bindCreated(getBindCreatedRequest(disputeId.toString(), providerDisputeId));
+ debugAdminManagementController.bindCreated(getBindCreatedRequest(disputeId.toString(), providerDisputeId));
assertEquals(DisputeStatus.create_adjustment, disputeDao.get(disputeId).get().getStatus());
disputeDao.update(disputeId, DisputeStatus.failed);
}
@@ -100,7 +100,7 @@ public class DebugManualParsingHandlerTest {
public void testBindCreatedPending() {
var disputeId = createdDisputesTestService.callCreateDisputeRemotely();
var providerDisputeId = generateId();
- debugManualParsingController.bindCreated(getBindCreatedRequest(disputeId.toString(), providerDisputeId));
+ debugAdminManagementController.bindCreated(getBindCreatedRequest(disputeId.toString(), providerDisputeId));
assertEquals(DisputeStatus.pending, disputeDao.get(disputeId).get().getStatus());
disputeDao.update(disputeId, DisputeStatus.failed);
}
@@ -112,7 +112,7 @@ public class DebugManualParsingHandlerTest {
var providerDisputeId = generateId();
var disputeId = disputeApiTestService.createDisputeViaApi(invoiceId, paymentId).getDisputeId();
disputeDao.update(UUID.fromString(disputeId), DisputeStatus.manual_created);
- debugManualParsingController.bindCreated(getBindCreatedRequest(disputeId, providerDisputeId));
+ debugAdminManagementController.bindCreated(getBindCreatedRequest(disputeId, providerDisputeId));
assertEquals(DisputeStatus.manual_pending, disputeDao.get(UUID.fromString(disputeId)).get().getStatus());
disputeDao.update(UUID.fromString(disputeId), DisputeStatus.failed);
}
@@ -124,7 +124,7 @@ public class DebugManualParsingHandlerTest {
var providerDisputeId = generateId();
var disputeId = disputeApiTestService.createDisputeViaApi(invoiceId, paymentId).getDisputeId();
disputeDao.update(UUID.fromString(disputeId), DisputeStatus.already_exist_created);
- debugManualParsingController.bindCreated(getBindCreatedRequest(disputeId, providerDisputeId));
+ debugAdminManagementController.bindCreated(getBindCreatedRequest(disputeId, providerDisputeId));
assertEquals(DisputeStatus.pending, disputeDao.get(UUID.fromString(disputeId)).get().getStatus());
disputeDao.update(UUID.fromString(disputeId), DisputeStatus.failed);
}
@@ -134,7 +134,7 @@ public class DebugManualParsingHandlerTest {
public void testGetDispute() {
WiremockUtils.mockS3AttachmentDownload();
var disputeId = pendingDisputesTestService.callPendingDisputeRemotely();
- var disputes = debugManualParsingController.getDisputes(getGetDisputeRequest(disputeId.toString(), true));
+ var disputes = debugAdminManagementController.getDisputes(getGetDisputeRequest(disputeId.toString(), true));
assertEquals(1, disputes.getDisputes().size());
disputeDao.update(disputeId, DisputeStatus.failed);
}
diff --git a/src/test/java/dev/vality/disputes/api/ServletTest.java b/src/test/java/dev/vality/disputes/api/ServletTest.java
index 0793b60..ca38b64 100644
--- a/src/test/java/dev/vality/disputes/api/ServletTest.java
+++ b/src/test/java/dev/vality/disputes/api/ServletTest.java
@@ -1,8 +1,8 @@
package dev.vality.disputes.api;
import dev.vality.damsel.payment_processing.InvoicingSrv;
+import dev.vality.disputes.admin.AdminManagementServiceSrv;
import dev.vality.disputes.admin.CancelParamsRequest;
-import dev.vality.disputes.admin.ManualParsingServiceSrv;
import dev.vality.disputes.callback.DisputeCallbackParams;
import dev.vality.disputes.callback.ProviderDisputesCallbackServiceSrv;
import dev.vality.disputes.config.WireMockSpringBootITest;
@@ -53,11 +53,11 @@ public class ServletTest {
@Test
@SneakyThrows
- public void manualServletTest() {
+ public void adminManagementServletTest() {
var iface = new THSpawnClientBuilder()
- .withAddress(new URI("http://127.0.0.1:" + serverPort + MANUAL))
+ .withAddress(new URI("http://127.0.0.1:" + serverPort + ADMIN_MANAGEMENT))
.withNetworkTimeout(5000)
- .build(ManualParsingServiceSrv.Iface.class);
+ .build(AdminManagementServiceSrv.Iface.class);
var request = DamselUtil.fillRequiredTBaseObject(
new CancelParamsRequest(),
CancelParamsRequest.class
diff --git a/src/test/java/dev/vality/disputes/schedule/service/CreatedDisputesServiceTest.java b/src/test/java/dev/vality/disputes/schedule/service/CreatedDisputesServiceTest.java
index f68db3b..389598c 100644
--- a/src/test/java/dev/vality/disputes/schedule/service/CreatedDisputesServiceTest.java
+++ b/src/test/java/dev/vality/disputes/schedule/service/CreatedDisputesServiceTest.java
@@ -17,12 +17,15 @@ import dev.vality.file.storage.FileStorageSrv;
import lombok.SneakyThrows;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.context.annotation.Import;
import java.util.UUID;
+import static dev.vality.disputes.constant.ModerationPrefix.DISPUTES_UNKNOWN_MAPPING;
import static dev.vality.disputes.util.MockUtil.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -49,6 +52,8 @@ public class CreatedDisputesServiceTest {
private WiremockAddressesHolder wiremockAddressesHolder;
@Autowired
private CreatedDisputesTestService createdDisputesTestService;
+ @LocalServerPort
+ private int serverPort;
@Test
@SneakyThrows
@@ -92,7 +97,7 @@ public class CreatedDisputesServiceTest {
@Test
@SneakyThrows
- public void testManualCreatedWhenIsNotProvidersDisputesApiExist() {
+ public void testManualPendingWhenIsNotProvidersDisputesApiExist() {
var invoiceId = "20McecNnWoy";
var paymentId = "1";
var disputeId = UUID.fromString(disputeApiTestService.createDisputeViaApi(invoiceId, paymentId).getDisputeId());
@@ -105,7 +110,7 @@ public class CreatedDisputesServiceTest {
when(dominantService.getProxy(any())).thenReturn(createProxy().get());
var dispute = disputeDao.get(disputeId);
createdDisputesService.callCreateDisputeRemotely(dispute.get());
- assertEquals(DisputeStatus.manual_created, disputeDao.get(disputeId).get().getStatus());
+ assertEquals(DisputeStatus.manual_pending, disputeDao.get(disputeId).get().getStatus());
disputeDao.update(disputeId, DisputeStatus.failed);
}
@@ -138,6 +143,83 @@ public class CreatedDisputesServiceTest {
assertEquals(DisputeStatus.failed, disputeDao.get(disputeId).get().getStatus());
}
+ @Test
+ @SneakyThrows
+ public void testManualCreatedWhenDisputeCreatedFailResultWithDisputesUnknownMapping() {
+ var invoiceId = "20McecNnWoy";
+ var paymentId = "1";
+ var disputeId = UUID.fromString(disputeApiTestService.createDisputeViaApi(invoiceId, paymentId).getDisputeId());
+ var invoicePayment = MockUtil.createInvoicePayment(paymentId);
+ invoicePayment.getPayment().setStatus(InvoicePaymentStatus.captured(new InvoicePaymentCaptured()));
+ when(invoicingClient.getPayment(any(), any())).thenReturn(invoicePayment);
+ when(fileStorageClient.generateDownloadUrl(any(), any())).thenReturn(wiremockAddressesHolder.getDownloadUrl());
+ var terminal = createTerminal().get();
+ terminal.getOptions().putAll(getOptions());
+ when(dominantService.getTerminal(any())).thenReturn(terminal);
+ when(dominantService.getProvider(any())).thenReturn(createProvider().get());
+ when(dominantService.getProxy(any())).thenReturn(createProxy().get());
+ var providerMock = mock(ProviderDisputesServiceSrv.Client.class);
+ var disputeCreatedFailResult = createDisputeCreatedFailResult();
+ disputeCreatedFailResult.getFailResult().getFailure().setCode(DISPUTES_UNKNOWN_MAPPING);
+ when(providerMock.createDispute(any())).thenReturn(disputeCreatedFailResult);
+ when(providerIfaceBuilder.buildTHSpawnClient(any())).thenReturn(providerMock);
+ var dispute = disputeDao.get(disputeId);
+ createdDisputesService.callCreateDisputeRemotely(dispute.get());
+ assertEquals(DisputeStatus.manual_created, disputeDao.get(disputeId).get().getStatus());
+ assertTrue(disputeDao.get(disputeId).get().getErrorMessage().contains(DISPUTES_UNKNOWN_MAPPING));
+ disputeDao.update(disputeId, DisputeStatus.failed);
+ }
+
+ @Test
+ @SneakyThrows
+ public void testManualCreatedWhenUnexpectedResultMapping() {
+ var invoiceId = "20McecNnWoy";
+ var paymentId = "1";
+ var disputeId = UUID.fromString(disputeApiTestService.createDisputeViaApi(invoiceId, paymentId).getDisputeId());
+ var invoicePayment = MockUtil.createInvoicePayment(paymentId);
+ invoicePayment.getPayment().setStatus(InvoicePaymentStatus.captured(new InvoicePaymentCaptured()));
+ when(invoicingClient.getPayment(any(), any())).thenReturn(invoicePayment);
+ when(fileStorageClient.generateDownloadUrl(any(), any())).thenReturn(wiremockAddressesHolder.getDownloadUrl());
+ var terminal = createTerminal().get();
+ terminal.getOptions().putAll(getOptions());
+ when(dominantService.getTerminal(any())).thenReturn(terminal);
+ when(dominantService.getProvider(any())).thenReturn(createProvider().get());
+ // routeUrl = "http://127.0.0.1:8023/disputes" == exist api
+ when(dominantService.getProxy(any())).thenReturn(createProxyWithRealAddress(serverPort).get());
+ var providerMock = mock(ProviderDisputesServiceSrv.Client.class);
+ when(providerMock.createDispute(any())).thenThrow(getUnexpectedResultWException());
+ when(providerIfaceBuilder.buildTHSpawnClient(any())).thenReturn(providerMock);
+ var dispute = disputeDao.get(disputeId);
+ createdDisputesService.callCreateDisputeRemotely(dispute.get());
+ assertEquals(DisputeStatus.manual_created, disputeDao.get(disputeId).get().getStatus());
+ assertTrue(disputeDao.get(disputeId).get().getErrorMessage().contains("Unexpected result"));
+ disputeDao.update(disputeId, DisputeStatus.failed);
+ }
+
+ @Test
+ @SneakyThrows
+ public void testManualPendingWhenUnexpectedResult() {
+ var invoiceId = "20McecNnWoy";
+ var paymentId = "1";
+ var disputeId = UUID.fromString(disputeApiTestService.createDisputeViaApi(invoiceId, paymentId).getDisputeId());
+ var invoicePayment = MockUtil.createInvoicePayment(paymentId);
+ invoicePayment.getPayment().setStatus(InvoicePaymentStatus.captured(new InvoicePaymentCaptured()));
+ when(invoicingClient.getPayment(any(), any())).thenReturn(invoicePayment);
+ when(fileStorageClient.generateDownloadUrl(any(), any())).thenReturn(wiremockAddressesHolder.getDownloadUrl());
+ var terminal = createTerminal().get();
+ terminal.getOptions().putAll(getOptions());
+ when(dominantService.getTerminal(any())).thenReturn(terminal);
+ when(dominantService.getProvider(any())).thenReturn(createProvider().get());
+ when(dominantService.getProxy(any())).thenReturn(createProxyNotFoundCase(serverPort).get());
+ var providerMock = mock(ProviderDisputesServiceSrv.Client.class);
+ when(providerMock.createDispute(any())).thenThrow(getUnexpectedResultWException());
+ when(providerIfaceBuilder.buildTHSpawnClient(any())).thenReturn(providerMock);
+ var dispute = disputeDao.get(disputeId);
+ createdDisputesService.callCreateDisputeRemotely(dispute.get());
+ assertEquals(DisputeStatus.manual_pending, disputeDao.get(disputeId).get().getStatus());
+ disputeDao.update(disputeId, DisputeStatus.failed);
+ }
+
@Test
@SneakyThrows
public void testDisputeCreatedAlreadyExistResult() {
diff --git a/src/test/java/dev/vality/disputes/schedule/service/PendingDisputesServiceTest.java b/src/test/java/dev/vality/disputes/schedule/service/PendingDisputesServiceTest.java
index f6dace7..77e80f2 100644
--- a/src/test/java/dev/vality/disputes/schedule/service/PendingDisputesServiceTest.java
+++ b/src/test/java/dev/vality/disputes/schedule/service/PendingDisputesServiceTest.java
@@ -15,8 +15,10 @@ import org.springframework.context.annotation.Import;
import java.util.UUID;
+import static dev.vality.disputes.constant.ModerationPrefix.DISPUTES_UNKNOWN_MAPPING;
import static dev.vality.disputes.util.MockUtil.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -77,6 +79,37 @@ public class PendingDisputesServiceTest {
assertEquals(DisputeStatus.failed, disputeDao.get(disputeId).get().getStatus());
}
+ @Test
+ @SneakyThrows
+ public void testManualPendingWhenStatusFailResultWithDisputesUnknownMapping() {
+ var disputeId = createdDisputesTestService.callCreateDisputeRemotely();
+ var providerMock = mock(ProviderDisputesServiceSrv.Client.class);
+ var disputeStatusFailResult = createDisputeStatusFailResult();
+ disputeStatusFailResult.getStatusFail().getFailure().setCode(DISPUTES_UNKNOWN_MAPPING);
+ when(providerMock.checkDisputeStatus(any())).thenReturn(disputeStatusFailResult);
+ when(providerIfaceBuilder.buildTHSpawnClient(any())).thenReturn(providerMock);
+ var dispute = disputeDao.get(disputeId);
+ pendingDisputesService.callPendingDisputeRemotely(dispute.get());
+ assertEquals(DisputeStatus.manual_pending, disputeDao.get(disputeId).get().getStatus());
+ assertTrue(disputeDao.get(disputeId).get().getErrorMessage().contains(DISPUTES_UNKNOWN_MAPPING));
+ disputeDao.update(disputeId, DisputeStatus.failed);
+ }
+
+ @Test
+ @SneakyThrows
+ public void testManualPendingWhenUnexpectedResultMapping() {
+ var disputeId = createdDisputesTestService.callCreateDisputeRemotely();
+ var providerMock = mock(ProviderDisputesServiceSrv.Client.class);
+ when(providerMock.checkDisputeStatus(any())).thenThrow(getUnexpectedResultWException());
+ when(providerIfaceBuilder.buildTHSpawnClient(any())).thenReturn(providerMock);
+ var dispute = disputeDao.get(disputeId);
+ pendingDisputesService.callPendingDisputeRemotely(dispute.get());
+ assertEquals(DisputeStatus.manual_pending, disputeDao.get(disputeId).get().getStatus());
+ assertTrue(disputeDao.get(disputeId).get().getErrorMessage().contains("Unexpected result"));
+ disputeDao.update(disputeId, DisputeStatus.failed);
+ }
+
+
@Test
@SneakyThrows
public void testDisputeStatusPendingResult() {
diff --git a/src/test/java/dev/vality/disputes/schedule/service/config/CallbackNotifierTestConfig.java b/src/test/java/dev/vality/disputes/schedule/service/config/CallbackNotifierTestConfig.java
new file mode 100644
index 0000000..45d6b36
--- /dev/null
+++ b/src/test/java/dev/vality/disputes/schedule/service/config/CallbackNotifierTestConfig.java
@@ -0,0 +1,13 @@
+package dev.vality.disputes.schedule.service.config;
+
+import dev.vality.disputes.admin.AdminCallbackServiceSrv;
+import org.springframework.boot.test.context.TestConfiguration;
+import org.springframework.boot.test.mock.mockito.MockBean;
+
+@TestConfiguration
+public class CallbackNotifierTestConfig {
+
+ @MockBean
+ private AdminCallbackServiceSrv.Iface adminCallbackDisputesTgBotClient;
+
+}
diff --git a/src/test/java/dev/vality/disputes/schedule/service/config/CreatedDisputesTestService.java b/src/test/java/dev/vality/disputes/schedule/service/config/CreatedDisputesTestService.java
index f1bb00a..45d9f2a 100644
--- a/src/test/java/dev/vality/disputes/schedule/service/config/CreatedDisputesTestService.java
+++ b/src/test/java/dev/vality/disputes/schedule/service/config/CreatedDisputesTestService.java
@@ -27,7 +27,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@TestComponent
-@Import({DisputeApiTestService.class, RemoteClientTestConfig.class})
+@Import({DisputeApiTestService.class, RemoteClientTestConfig.class, DefaultRemoteClientTestConfig.class, CallbackNotifierTestConfig.class})
@SuppressWarnings({"ParameterName", "LineLength"})
public class CreatedDisputesTestService {
diff --git a/src/test/java/dev/vality/disputes/schedule/service/config/DefaultRemoteClientTestConfig.java b/src/test/java/dev/vality/disputes/schedule/service/config/DefaultRemoteClientTestConfig.java
new file mode 100644
index 0000000..9dde8bc
--- /dev/null
+++ b/src/test/java/dev/vality/disputes/schedule/service/config/DefaultRemoteClientTestConfig.java
@@ -0,0 +1,13 @@
+package dev.vality.disputes.schedule.service.config;
+
+import dev.vality.disputes.provider.ProviderDisputesServiceSrv;
+import org.springframework.boot.test.context.TestConfiguration;
+import org.springframework.boot.test.mock.mockito.MockBean;
+
+@TestConfiguration
+public class DefaultRemoteClientTestConfig {
+
+ @MockBean
+ private ProviderDisputesServiceSrv.Iface providerDisputesTgBotClient;
+
+}
diff --git a/src/test/java/dev/vality/disputes/util/MockUtil.java b/src/test/java/dev/vality/disputes/util/MockUtil.java
index abf4967..652ce86 100644
--- a/src/test/java/dev/vality/disputes/util/MockUtil.java
+++ b/src/test/java/dev/vality/disputes/util/MockUtil.java
@@ -14,6 +14,10 @@ import dev.vality.file.storage.NewFileResult;
import dev.vality.geck.common.util.TypeUtil;
import dev.vality.token.keeper.AuthData;
import dev.vality.token.keeper.AuthDataStatus;
+import dev.vality.woody.api.flow.error.WErrorDefinition;
+import dev.vality.woody.api.flow.error.WErrorSource;
+import dev.vality.woody.api.flow.error.WErrorType;
+import dev.vality.woody.api.flow.error.WRuntimeException;
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;
import org.apache.thrift.TSerializer;
@@ -81,8 +85,16 @@ public class MockUtil {
.setProxy(new Proxy().setRef(new ProxyRef().setId(1))));
}
+ public static CompletableFuture createProxyNotFoundCase(Integer port) {
+ return createProxy("http://127.0.0.1:" + port + "/debug/disputes-api/admin-management");
+ }
+
+ public static CompletableFuture createProxyWithRealAddress(Integer port) {
+ return createProxy("http://127.0.0.1:" + port);
+ }
+
public static CompletableFuture createProxy() {
- return createProxy("http://ya.ru");
+ return createProxy("http://127.0.0.1:8023");
}
public static CompletableFuture createProxy(String url) {
@@ -173,4 +185,13 @@ public class MockUtil {
failure.setSub(new SubFailure("some_suberror"));
return failure;
}
+
+ public static WRuntimeException getUnexpectedResultWException() {
+ var errorDefinition = new WErrorDefinition(WErrorSource.EXTERNAL);
+ errorDefinition.setErrorReason("Unexpected result, code = resp_status_error, description = " +
+ "Tek seferde en fazla 4,000.00 işem yapılabilir.");
+ errorDefinition.setErrorType(WErrorType.UNEXPECTED_ERROR);
+ errorDefinition.setErrorSource(WErrorSource.INTERNAL);
+ return new WRuntimeException(errorDefinition);
+ }
}