mirror of
https://github.com/valitydev/disputes-api.git
synced 2024-11-06 00:55:23 +00:00
add disputes-tg-bot usage (#23)
* rename AdminManagementServlet * add disputes-tg-bot usage * add dummy for tg bot usages * add mapping * remove texeption catcher * bumps deps, remove debug endpoint * rename * rename * disable isScheduleReadyForCreateAdjustmentsEnabled * fix tests * up pg versino * refactor ProviderRouting * add handleUnexpectedResultMapping tests cases * checkstyle * maven-site issue * bump workflow * bump workflow * bump workflow * review fixes
This commit is contained in:
parent
0f39485afc
commit
df25417b6b
@ -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()`. Здесь особенность, что этот метод фильтрует возможность биндить диспуты только созданные
|
||||
|
20
pom.xml
20
pom.xml
@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>dev.vality</groupId>
|
||||
<artifactId>service-parent-pom</artifactId>
|
||||
<version>3.0.2</version>
|
||||
<version>3.0.4</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>disputes-api</artifactId>
|
||||
@ -47,7 +47,7 @@
|
||||
<dependency>
|
||||
<groupId>dev.vality</groupId>
|
||||
<artifactId>disputes-proto</artifactId>
|
||||
<version>1.23-37a5ad1</version>
|
||||
<version>1.26-fc8e34f</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.vality</groupId>
|
||||
@ -62,6 +62,7 @@
|
||||
<dependency>
|
||||
<groupId>dev.vality</groupId>
|
||||
<artifactId>damsel</artifactId>
|
||||
<version>1.648-ad715bd</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.vality</groupId>
|
||||
@ -87,6 +88,16 @@
|
||||
<artifactId>adapter-flow-lib</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.vality.woody</groupId>
|
||||
<artifactId>woody-thrift</artifactId>
|
||||
<version>2.0.8</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.vality.woody</groupId>
|
||||
<artifactId>woody-api</artifactId>
|
||||
<version>2.0.8</version>
|
||||
</dependency>
|
||||
|
||||
<!--spring-->
|
||||
<dependency>
|
||||
@ -217,6 +228,11 @@
|
||||
<artifactId>guava</artifactId>
|
||||
<version>32.0.0-jre</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.opentelemetry</groupId>
|
||||
<artifactId>opentelemetry-semconv</artifactId>
|
||||
<version>1.29.0-alpha</version>
|
||||
</dependency>
|
||||
|
||||
<!--test-->
|
||||
<dependency>
|
||||
|
@ -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<Dispute> disputes);
|
||||
|
||||
void sendDisputeFailedReviewRequired(Dispute dispute, String errorCode, String errorDescription);
|
||||
|
||||
}
|
@ -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<Dispute> 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));
|
||||
}
|
||||
}
|
@ -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<Dispute> 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());
|
||||
}
|
||||
}
|
@ -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)
|
@ -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);
|
||||
}
|
@ -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<Attachment> attachments, DisputeStatus disputeStatus) {
|
||||
public void sendCreated(Dispute dispute, DisputeStatus disputeStatus, String errorMessage) {
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
var contextMap = MDC.getCopyOfContextMap() == null ? new HashMap<String, String>() : 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<String, String>() : 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<String, String>() : 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<String, String> getContextMap() {
|
||||
return MDC.getCopyOfContextMap() == null ? new HashMap<>() : MDC.getCopyOfContextMap();
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
@ -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());
|
||||
|
@ -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()
|
||||
|
@ -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");
|
||||
|
68
src/main/java/dev/vality/disputes/config/OtelConfig.java
Normal file
68
src/main/java/dev/vality/disputes/config/OtelConfig.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package dev.vality.disputes.constant;
|
||||
|
||||
public class ModerationPrefix {
|
||||
|
||||
public static final String DISPUTES_UNKNOWN_MAPPING = "disputes_unknown_mapping";
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
@ -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<WRuntimeException> unexpectedResultMappingHandler) {
|
||||
try {
|
||||
runnable.run();
|
||||
} catch (WRuntimeException e) {
|
||||
if (externalGatewayChecker.isProvidersDisputesUnexpectedResultMapping(e)) {
|
||||
unexpectedResultMappingHandler.accept(e);
|
||||
return;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
@ -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<Attachment> attachments, ProviderData providerData);
|
||||
|
||||
}
|
@ -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<Attachment> 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;
|
||||
}
|
||||
}
|
@ -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<Attachment> attachments, ProviderData providerData) {
|
||||
log.debug("Trying to call DummyRemoteClientImpl.createDispute() {}", dispute.getId());
|
||||
providerData.setRouteUrl(routeUrl);
|
||||
return DisputeCreatedResult.successResult(new DisputeCreatedSuccessResult(UUID.randomUUID().toString()));
|
||||
}
|
||||
}
|
@ -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<Attachment> 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);
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
@ -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<String, String> 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());
|
||||
}
|
||||
}
|
||||
|
@ -11,5 +11,6 @@ public class ProviderData {
|
||||
|
||||
private Map<String, String> options;
|
||||
private String defaultProviderUrl;
|
||||
private String routeUrl;
|
||||
|
||||
}
|
||||
|
@ -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<Dispute> 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<Attachment> attachments, DisputeCreatedResult result, Map<String, String> 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<Attachment> 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<String, String> options) {
|
||||
return options.containsKey(DISPUTE_FLOW_CAPTURED_BLOCKED);
|
||||
}
|
||||
|
@ -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<Boolean> isNotFoundResponse() {
|
||||
|
@ -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<Dispute> 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)
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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()))
|
||||
|
@ -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) {
|
||||
|
24
src/main/java/dev/vality/disputes/service/external/DisputesTgBotService.java
vendored
Normal file
24
src/main/java/dev/vality/disputes/service/external/DisputesTgBotService.java
vendored
Normal file
@ -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<DisputeReadyForCreateAdjustment> disputeReadyForCreateAdjustments);
|
||||
|
||||
void sendDisputeFailedReviewRequired(DisputeFailedReviewRequired disputeFailedReviewRequired);
|
||||
|
||||
}
|
75
src/main/java/dev/vality/disputes/service/external/impl/DisputesTgBotServiceImpl.java
vendored
Normal file
75
src/main/java/dev/vality/disputes/service/external/impl/DisputesTgBotServiceImpl.java
vendored
Normal file
@ -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<DisputeReadyForCreateAdjustment> 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());
|
||||
}
|
||||
}
|
@ -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
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
2
src/main/resources/db/migration/V3__add_mapping.sql
Normal file
2
src/main/resources/db/migration/V3__add_mapping.sql
Normal file
@ -0,0 +1,2 @@
|
||||
ALTER TABLE dspt.dispute
|
||||
ADD COLUMN "mapping" CHARACTER VARYING;
|
@ -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)
|
@ -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": [
|
||||
{
|
@ -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);
|
||||
}
|
@ -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
|
||||
|
@ -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() {
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
|
||||
}
|
@ -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 {
|
||||
|
||||
|
@ -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;
|
||||
|
||||
}
|
@ -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<ProxyDefinition> createProxyNotFoundCase(Integer port) {
|
||||
return createProxy("http://127.0.0.1:" + port + "/debug/disputes-api/admin-management");
|
||||
}
|
||||
|
||||
public static CompletableFuture<ProxyDefinition> createProxyWithRealAddress(Integer port) {
|
||||
return createProxy("http://127.0.0.1:" + port);
|
||||
}
|
||||
|
||||
public static CompletableFuture<ProxyDefinition> createProxy() {
|
||||
return createProxy("http://ya.ru");
|
||||
return createProxy("http://127.0.0.1:8023");
|
||||
}
|
||||
|
||||
public static CompletableFuture<ProxyDefinition> 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);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user