Pull from JD-599

This commit is contained in:
echerniak 2021-10-06 15:06:59 +03:00
commit b0f4c4d205
No known key found for this signature in database
GPG Key ID: 7D79B3A9CB749B36
47 changed files with 1796 additions and 402 deletions

19
pom.xml
View File

@ -51,6 +51,17 @@
<groupId>com.rbkmoney</groupId>
<artifactId>shared-resources</artifactId>
</dependency>
<dependency>
<groupId>com.rbkmoney</groupId>
<artifactId>bouncer-proto</artifactId>
<version>1.27-cc50cc4</version>
</dependency>
<dependency>
<groupId>com.rbkmoney</groupId>
<artifactId>org-management-proto</artifactId>
<version>1.3-39d8513</version>
</dependency>
<dependency>
<groupId>com.rbkmoney</groupId>
<artifactId>payout-manager-proto</artifactId>
@ -64,8 +75,14 @@
<dependency>
<groupId>com.rbkmoney</groupId>
<artifactId>magista-proto</artifactId>
<version>SNAPSHOT</version>
<version>1.18-e470bc7</version>
</dependency>
<dependency>
<groupId>com.rbkmoney</groupId>
<artifactId>vortigon-proto</artifactId>
<version>1.13-0c92608</version>
</dependency>
<dependency>
<groupId>com.rbkmoney.geck</groupId>
<artifactId>serializer</artifactId>

View File

@ -1,6 +1,13 @@
package com.rbkmoney.anapi.v2.config;
import com.rbkmoney.bouncer.decisions.ArbiterSrv;
import com.rbkmoney.damsel.vortigon.VortigonServiceSrv;
import com.rbkmoney.magista.MerchantStatisticsServiceSrv;
import com.rbkmoney.orgmanagement.AuthContextProviderSrv;
import com.rbkmoney.woody.api.trace.context.metadata.user.UserIdentityEmailExtensionKit;
import com.rbkmoney.woody.api.trace.context.metadata.user.UserIdentityIdExtensionKit;
import com.rbkmoney.woody.api.trace.context.metadata.user.UserIdentityRealmExtensionKit;
import com.rbkmoney.woody.api.trace.context.metadata.user.UserIdentityUsernameExtensionKit;
import com.rbkmoney.woody.thrift.impl.http.THSpawnClientBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
@ -8,6 +15,7 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import java.io.IOException;
import java.util.List;
@Configuration
public class ApplicationConfig {
@ -21,4 +29,42 @@ public class ApplicationConfig {
.withNetworkTimeout(networkTimeout)
.withAddress(resource.getURI()).build(MerchantStatisticsServiceSrv.Iface.class);
}
@Bean
public VortigonServiceSrv.Iface vortigonClient(
@Value("${service.vortigon.url}") Resource resource,
@Value("${service.vortigon.networkTimeout}") int networkTimeout
) throws IOException {
return new THSpawnClientBuilder()
.withNetworkTimeout(networkTimeout)
.withAddress(resource.getURI()).build(VortigonServiceSrv.Iface.class);
}
@Bean
public AuthContextProviderSrv.Iface orgManagerClient(
@Value("${service.orgManagement.url}") Resource resource,
@Value("${service.orgManagement.networkTimeout}") int networkTimeout
) throws IOException {
return new THSpawnClientBuilder()
.withNetworkTimeout(networkTimeout)
.withMetaExtensions(
List.of(
UserIdentityIdExtensionKit.INSTANCE,
UserIdentityEmailExtensionKit.INSTANCE,
UserIdentityUsernameExtensionKit.INSTANCE,
UserIdentityRealmExtensionKit.INSTANCE
)
)
.withAddress(resource.getURI()).build(AuthContextProviderSrv.Iface.class);
}
@Bean
public ArbiterSrv.Iface bouncerClient(
@Value("${service.bouncer.url}") Resource resource,
@Value("${service.bouncer.networkTimeout}") int networkTimeout
) throws IOException {
return new THSpawnClientBuilder()
.withNetworkTimeout(networkTimeout)
.withAddress(resource.getURI()).build(ArbiterSrv.Iface.class);
}
}

View File

@ -0,0 +1,28 @@
package com.rbkmoney.anapi.v2.config.properties;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.NotEmpty;
@Getter
@Setter
@Component
@Validated
@ConfigurationProperties(prefix = "service.bouncer")
public class BouncerProperties {
@NotEmpty
private String contextFragmentId;
@NotEmpty
private String deploymentId;
@NotEmpty
private String authMethod;
@NotEmpty
private String realm;
@NotEmpty
private String ruleSetId;
}

View File

@ -1,11 +1,13 @@
package com.rbkmoney.anapi.v2.controller;
import com.rbkmoney.anapi.v2.exception.AuthorizationException;
import com.rbkmoney.anapi.v2.exception.BadRequestException;
import com.rbkmoney.anapi.v2.exception.DeadlineException;
import com.rbkmoney.openapi.anapi_v2.model.DefaultLogicError;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
@ -63,6 +65,18 @@ public class ErrorControllerAdvice {
.message(e.getMessage());
}
@ExceptionHandler({AccessDeniedException.class})
@ResponseStatus(HttpStatus.FORBIDDEN)
public void handleAccessDeniedException(AccessDeniedException e) {
log.warn("<- Res [403]: Request denied access", e);
}
@ExceptionHandler({AuthorizationException.class})
@ResponseStatus(HttpStatus.FORBIDDEN)
public void handleAccessDeniedException(AuthorizationException e) {
log.warn("<- Res [403]: Request denied access", e);
}
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public void handleException(Exception e) {

View File

@ -1,6 +1,7 @@
package com.rbkmoney.anapi.v2.controller;
import com.rbkmoney.anapi.v2.converter.search.request.*;
import com.rbkmoney.anapi.v2.security.AccessService;
import com.rbkmoney.anapi.v2.service.SearchService;
import com.rbkmoney.magista.*;
import com.rbkmoney.openapi.anapi_v2.api.*;
@ -18,6 +19,7 @@ import java.time.OffsetDateTime;
import java.util.List;
import java.util.Optional;
import static com.rbkmoney.anapi.v2.util.ConverterUtil.merge;
import static com.rbkmoney.anapi.v2.util.DeadlineUtil.checkDeadline;
@ -30,6 +32,7 @@ public class SearchController
implements PaymentsApi, ChargebacksApi, InvoicesApi, PayoutsApi, RefundsApi, InvoiceTemplatesApi {
private final SearchService searchService;
private final AccessService accessService;
private final ParamsToPaymentSearchQueryConverter paymentSearchConverter;
private final ParamsToChargebackSearchQueryConverter chargebackSearchConverter;
private final ParamsToInvoiceSearchQueryConverter invoiceSearchConverter;
@ -67,20 +70,22 @@ public class SearchController
@Pattern(regexp = "^\\d{4}$") @Valid String last4,
@Pattern(regexp = "^[a-zA-Z0-9]{12}$") @Valid String rrn,
@Size(min = 1, max = 40) @Valid String approvalCode,
@Valid BankCardTokenProvider bankCardTokenProvider,
@Valid BankCardPaymentSystem bankCardPaymentSystem,
@Valid String bankCardTokenProvider,
@Valid String bankCardPaymentSystem,
@Min(1L) @Valid Long paymentAmountFrom,
@Min(1L) @Valid Long paymentAmountTo,
@Valid List<String> excludedShops,
@Valid String continuationToken) {
log.info("-> Req: xRequestID={}", xRequestID);
checkDeadline(xRequestDeadline, xRequestID);
shopIDs = accessService
.getAccessibleShops("searchPayments", partyID, merge(shopID, shopIDs), paymentInstitutionRealm);
PaymentSearchQuery query = paymentSearchConverter.convert(partyID,
fromTime,
toTime,
limit,
shopID,
shopIDs,
paymentInstitutionRealm,
invoiceIDs,
paymentStatus, paymentFlow,
paymentMethod,
@ -103,6 +108,7 @@ public class SearchController
excludedShops,
continuationToken);
InlineResponse20010 response = searchService.findPayments(query);
log.info("<- Res [200]: xRequestID={}", xRequestID);
return ResponseEntity.ok(response);
}
@ -116,7 +122,8 @@ public class SearchController
@Size(min = 1, max = 40) @Valid String shopID,
@Valid List<String> shopIDs,
@Valid String paymentInstitutionRealm,
@Min(0L) @Valid Integer offset,
//Not used by magista
@Min(0L) @Valid @Deprecated Integer offset,
@Size(min = 1, max = 40) @Valid String invoiceID,
@Size(min = 1, max = 40) @Valid String paymentID,
@Size(min = 1, max = 40) @Valid String chargebackID,
@ -124,15 +131,15 @@ public class SearchController
@Valid List<String> chargebackStages,
@Valid List<String> chargebackCategories,
@Valid String continuationToken) {
log.info("-> Req: xRequestID={}", xRequestID);
checkDeadline(xRequestDeadline, xRequestID);
shopIDs = accessService
.getAccessibleShops("searchChargebacks", partyID, merge(shopID, shopIDs), paymentInstitutionRealm);
ChargebackSearchQuery query = chargebackSearchConverter.convert(partyID,
fromTime,
toTime,
limit,
shopID,
shopIDs,
paymentInstitutionRealm,
offset,
invoiceID,
paymentID,
chargebackID,
@ -142,6 +149,7 @@ public class SearchController
continuationToken);
InlineResponse2008 response = searchService
.findChargebacks(query);
log.info("<- Res [200]: xRequestID={}", xRequestID);
return ResponseEntity.ok(response);
}
@ -161,25 +169,27 @@ public class SearchController
@Size(min = 1, max = 40) @Valid String externalID,
@Min(1L) @Valid Long invoiceAmountFrom,
@Min(1L) @Valid Long invoiceAmountTo,
@Valid List<String> excludedShops,
//Not used by magista
@Valid @Deprecated List<String> excludedShops,
@Valid String continuationToken) {
log.info("-> Req: xRequestID={}", xRequestID);
checkDeadline(xRequestDeadline, xRequestID);
shopIDs = accessService
.getAccessibleShops("searchInvoices", partyID, merge(shopID, shopIDs), paymentInstitutionRealm);
InvoiceSearchQuery query = invoiceSearchConverter.convert(partyID,
fromTime,
toTime,
limit,
shopID,
shopIDs,
paymentInstitutionRealm,
invoiceIDs,
invoiceStatus,
invoiceID,
externalID,
invoiceAmountFrom,
invoiceAmountTo,
excludedShops,
continuationToken);
InlineResponse2009 response = searchService.findInvoices(query);
log.info("<- Res [200]: xRequestID={}", xRequestID);
return ResponseEntity.ok(response);
}
@ -193,25 +203,27 @@ public class SearchController
@Size(min = 1, max = 40) @Valid String shopID,
@Valid List<String> shopIDs,
@Valid String paymentInstitutionRealm,
@Min(0L) @Valid Integer offset,
//Not used by magista
@Min(0L) @Valid @Deprecated Integer offset,
@Size(min = 1, max = 40) @Valid String payoutID,
@Valid String payoutToolType,
@Valid List<String> excludedShops,
//Not used by magista
@Valid @Deprecated List<String> excludedShops,
@Valid String continuationToken) {
log.info("-> Req: xRequestID={}", xRequestID);
checkDeadline(xRequestDeadline, xRequestID);
shopIDs = accessService
.getAccessibleShops("searchPayouts", partyID, merge(shopID, shopIDs), paymentInstitutionRealm);
PayoutSearchQuery query = payoutSearchConverter.convert(partyID,
fromTime,
toTime,
limit,
shopID,
shopIDs,
paymentInstitutionRealm,
offset,
payoutID,
payoutToolType,
excludedShops,
continuationToken);
InlineResponse20011 response = searchService.findPayouts(query);
log.info("<- Res [200]: xRequestID={}", xRequestID);
return ResponseEntity.ok(response);
}
@ -225,50 +237,56 @@ public class SearchController
@Size(min = 1, max = 40) @Valid String shopID,
@Valid List<String> shopIDs,
@Valid String paymentInstitutionRealm,
@Min(0L) @Valid Integer offset,
//Not used by magista
@Min(0L) @Valid @Deprecated Integer offset,
@Valid List<String> invoiceIDs,
@Size(min = 1, max = 40) @Valid String invoiceID,
@Size(min = 1, max = 40) @Valid String paymentID,
@Size(min = 1, max = 40) @Valid String refundID,
@Size(min = 1, max = 40) @Valid String externalID,
@Valid String refundStatus,
@Valid List<String> excludedShops,
//Not used by magista
@Valid @Deprecated List<String> excludedShops,
@Valid String continuationToken) {
log.info("-> Req: xRequestID={}", xRequestID);
checkDeadline(xRequestDeadline, xRequestID);
shopIDs = accessService
.getAccessibleShops("searchRefunds", partyID, merge(shopID, shopIDs), paymentInstitutionRealm);
RefundSearchQuery query = refundSearchConverter.convert(partyID,
fromTime,
toTime,
limit,
shopID,
shopIDs,
paymentInstitutionRealm,
offset,
invoiceIDs,
invoiceID,
paymentID,
refundID,
externalID,
refundStatus,
excludedShops,
continuationToken);
InlineResponse20012 response = searchService.findRefunds(query);
log.info("<- Res [200]: xRequestID={}", xRequestID);
return ResponseEntity.ok(response);
}
@Override
public ResponseEntity<InlineResponse20013> searchInvoiceTemplates(String xRequestID,
@NotNull @Size(min = 1, max = 40) @Valid String partyID,
@NotNull @Size(min = 1, max = 40) @Valid
String partyID,
@NotNull @Valid OffsetDateTime fromTime,
@NotNull @Valid OffsetDateTime toTime,
@NotNull @Min(1L) @Max(1000L) @Valid Integer limit,
@NotNull @Min(1L) @Max(1000L) @Valid
Integer limit,
String xRequestDeadline,
@Valid List<String> shopIDs,
@Valid String invoiceTemplateStatus,
@Size(min = 1, max = 40) @Valid String invoiceTemplateID,
@Size(min = 1, max = 40) @Valid
String invoiceTemplateID,
@Valid String continuationToken,
@Size(min = 1, max = 40) @Valid String name,
@Size(min = 1, max = 40) @Valid String product,
@Size(min = 1, max = 40) @Valid OffsetDateTime invoiceValidUntil) {
@Size(min = 1, max = 40) @Valid
OffsetDateTime invoiceValidUntil) {
checkDeadline(xRequestDeadline, xRequestID);
InvoiceTemplateSearchQuery query = invoiceTempateSearchConverter.convert(partyID,
fromTime,

View File

@ -13,7 +13,6 @@ import java.util.List;
import java.util.stream.Collectors;
import static com.rbkmoney.anapi.v2.util.ConverterUtil.fillCommonParams;
import static com.rbkmoney.anapi.v2.util.ConverterUtil.merge;
@Component
public class ParamsToChargebackSearchQueryConverter {
@ -22,10 +21,7 @@ public class ParamsToChargebackSearchQueryConverter {
OffsetDateTime fromTime,
OffsetDateTime toTime,
Integer limit,
String shopID,
List<String> shopIDs,
String paymentInstitutionRealm,
Integer offset,
String invoiceID,
String paymentID,
String chargebackID,
@ -33,76 +29,90 @@ public class ParamsToChargebackSearchQueryConverter {
List<String> chargebackStages,
List<String> chargebackCategories,
String continuationToken) {
//TODO: Mapping for paymentInstitutionRealm, offset
return new ChargebackSearchQuery()
.setCommonSearchQueryParams(
fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs),
continuationToken))
fillCommonParams(fromTime, toTime, limit, partyID, shopIDs, continuationToken))
.setInvoiceIds(invoiceID != null ? List.of(invoiceID) : null)
.setPaymentId(paymentID)
.setChargebackId(chargebackID)
.setChargebackStatuses(chargebackStatuses != null
? chargebackStatuses.stream()
.map(this::mapToDamselStatus)
.map(this::mapStatus)
.collect(Collectors.toList())
: null
)
.setChargebackStages(chargebackStages != null
? chargebackStages.stream()
.map(this::mapToDamselStage)
.map(this::mapStage)
.collect(Collectors.toList())
: null
)
.setChargebackCategories(chargebackCategories != null
? chargebackCategories.stream()
.map(this::mapToDamselCategory)
.map(this::mapCategory)
.collect(Collectors.toList())
: null);
}
private InvoicePaymentChargebackStage mapToDamselStage(String chargebackStage) {
var stage = Enum.valueOf(ChargebackStage.class, chargebackStage);
var damselStage = new InvoicePaymentChargebackStage();
switch (stage) {
case CHARGEBACK -> damselStage.setChargeback(new InvoicePaymentChargebackStageChargeback());
case PRE_ARBITRATION -> damselStage.setPreArbitration(new InvoicePaymentChargebackStagePreArbitration());
case ARBITRATION -> damselStage.setArbitration(new InvoicePaymentChargebackStageArbitration());
default -> throw new BadRequestException(
String.format("Chargeback stage %s cannot be processed", chargebackStage));
}
protected InvoicePaymentChargebackStage mapStage(String chargebackStage) {
try {
var stage = ChargebackStage.fromValue(chargebackStage);
var damselStage = new InvoicePaymentChargebackStage();
switch (stage) {
case CHARGEBACK -> damselStage.setChargeback(new InvoicePaymentChargebackStageChargeback());
case PRE_ARBITRATION -> damselStage.setPreArbitration(
new InvoicePaymentChargebackStagePreArbitration());
case ARBITRATION -> damselStage.setArbitration(new InvoicePaymentChargebackStageArbitration());
default -> throw new BadRequestException(
String.format("Chargeback stage %s cannot be processed", chargebackStage));
}
return damselStage;
return damselStage;
} catch (Exception e) {
throw new BadRequestException(e.getMessage());
}
}
private InvoicePaymentChargebackStatus mapToDamselStatus(String chargebackStatus) {
var status = Enum.valueOf(ChargebackStatus.class, chargebackStatus);
var damselStatus = new InvoicePaymentChargebackStatus();
switch (status) {
case PENDING -> damselStatus.setPending(new InvoicePaymentChargebackPending());
case ACCEPTED -> damselStatus.setAccepted(new InvoicePaymentChargebackAccepted());
case REJECTED -> damselStatus.setRejected(new InvoicePaymentChargebackRejected());
case CANCELLED -> damselStatus.setCancelled(new InvoicePaymentChargebackCancelled());
default -> throw new BadRequestException(
String.format("Chargeback status %s cannot be processed", chargebackStatus));
}
protected InvoicePaymentChargebackStatus mapStatus(String chargebackStatus) {
try {
var status = ChargebackStatus.fromValue(chargebackStatus);
var damselStatus = new InvoicePaymentChargebackStatus();
switch (status) {
case PENDING -> damselStatus.setPending(new InvoicePaymentChargebackPending());
case ACCEPTED -> damselStatus.setAccepted(new InvoicePaymentChargebackAccepted());
case REJECTED -> damselStatus.setRejected(new InvoicePaymentChargebackRejected());
case CANCELLED -> damselStatus.setCancelled(new InvoicePaymentChargebackCancelled());
default -> throw new BadRequestException(
String.format("Chargeback status %s cannot be processed", chargebackStatus));
}
return damselStatus;
return damselStatus;
} catch (Exception e) {
throw new BadRequestException(e.getMessage());
}
}
private InvoicePaymentChargebackCategory mapToDamselCategory(String chargebackCategory) {
var category = Enum.valueOf(ChargebackCategory.class, chargebackCategory);
var damselCategory = new InvoicePaymentChargebackCategory();
switch (category) {
case FRAUD -> damselCategory.setFraud(new InvoicePaymentChargebackCategoryFraud());
case DISPUTE -> damselCategory.setDispute(new InvoicePaymentChargebackCategoryDispute());
case AUTHORISATION -> damselCategory
.setAuthorisation(new InvoicePaymentChargebackCategoryAuthorisation());
case PROCESSING_ERROR -> damselCategory
.setProcessingError(new InvoicePaymentChargebackCategoryProcessingError());
default -> throw new BadRequestException(
String.format("Chargeback category %s cannot be processed", chargebackCategory));
}
protected InvoicePaymentChargebackCategory mapCategory(String chargebackCategory) {
try {
var category = ChargebackCategory.fromValue(chargebackCategory);
var damselCategory = new InvoicePaymentChargebackCategory();
switch (category) {
case FRAUD -> damselCategory.setFraud(new InvoicePaymentChargebackCategoryFraud());
case DISPUTE -> damselCategory.setDispute(new InvoicePaymentChargebackCategoryDispute());
case AUTHORISATION -> damselCategory
.setAuthorisation(new InvoicePaymentChargebackCategoryAuthorisation());
case PROCESSING_ERROR -> damselCategory
.setProcessingError(new InvoicePaymentChargebackCategoryProcessingError());
default -> throw new BadRequestException(
String.format("Chargeback category %s cannot be processed", chargebackCategory));
}
return damselCategory;
return damselCategory;
} catch (Exception e) {
throw new BadRequestException(e.getMessage());
}
}
}

View File

@ -1,7 +1,9 @@
package com.rbkmoney.anapi.v2.converter.search.request;
import com.rbkmoney.anapi.v2.exception.BadRequestException;
import com.rbkmoney.magista.*;
import com.rbkmoney.magista.InvoiceSearchQuery;
import com.rbkmoney.magista.InvoiceStatus;
import com.rbkmoney.magista.PaymentParams;
import org.springframework.stereotype.Component;
import java.time.OffsetDateTime;
@ -17,41 +19,41 @@ public class ParamsToInvoiceSearchQueryConverter {
OffsetDateTime fromTime,
OffsetDateTime toTime,
Integer limit,
String shopID,
List<String> shopIDs,
String paymentInstitutionRealm,
List<String> invoiceIDs,
String invoiceStatus,
String invoiceID,
String externalID,
Long invoiceAmountFrom,
Long invoiceAmountTo,
List<String> excludedShops,
String continuationToken) {
//TODO: Mapping for paymentInstitutionRealm, excludedShops
return new InvoiceSearchQuery()
.setCommonSearchQueryParams(
fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs),
continuationToken))
fillCommonParams(fromTime, toTime, limit, partyID, shopIDs, continuationToken))
.setPaymentParams(
new PaymentParams()
.setPaymentAmountFrom(invoiceAmountFrom != null ? invoiceAmountFrom : 0L)
.setPaymentAmountTo(invoiceAmountTo != null ? invoiceAmountTo : 0L)
mapPaymentParams(invoiceAmountFrom, invoiceAmountTo)
)
.setInvoiceStatus(invoiceStatus != null ? mapStatus(invoiceStatus) : null)
.setInvoiceIds(merge(invoiceID, invoiceIDs))
.setExternalId(externalID);
}
private InvoiceStatus mapStatus(String statusParam) {
var status = Enum.valueOf(com.rbkmoney.openapi.anapi_v2.model.InvoiceStatus.StatusEnum.class, statusParam);
return switch (status) {
case CANCELLED -> InvoiceStatus.cancelled;
case FULFILLED -> InvoiceStatus.fulfilled;
case PAID -> InvoiceStatus.paid;
case UNPAID -> InvoiceStatus.unpaid;
default -> throw new BadRequestException(
String.format("Invoice status %s cannot be processed", status));
};
protected PaymentParams mapPaymentParams(Long invoiceAmountFrom, Long invoiceAmountTo) {
var params = new PaymentParams();
if (invoiceAmountFrom != null) {
params.setPaymentAmountFrom(invoiceAmountFrom);
}
if (invoiceAmountTo != null) {
params.setPaymentAmountTo(invoiceAmountTo);
}
return params;
}
protected InvoiceStatus mapStatus(String status) {
try {
return InvoiceStatus.valueOf(status);
} catch (IllegalArgumentException e) {
throw new BadRequestException(String.format("Invoice status %s cannot be processed", status));
}
}
}

View File

@ -4,11 +4,7 @@ import com.rbkmoney.anapi.v2.exception.BadRequestException;
import com.rbkmoney.damsel.domain.LegacyBankCardPaymentSystem;
import com.rbkmoney.damsel.domain.LegacyBankCardTokenProvider;
import com.rbkmoney.damsel.domain.LegacyTerminalPaymentProvider;
import com.rbkmoney.magista.InvoicePaymentStatus;
import com.rbkmoney.magista.PaymentParams;
import com.rbkmoney.magista.PaymentSearchQuery;
import com.rbkmoney.openapi.anapi_v2.model.BankCardPaymentSystem;
import com.rbkmoney.openapi.anapi_v2.model.BankCardTokenProvider;
import com.rbkmoney.magista.*;
import com.rbkmoney.openapi.anapi_v2.model.PaymentStatus;
import org.springframework.stereotype.Component;
@ -17,11 +13,9 @@ import java.util.List;
import static com.rbkmoney.anapi.v2.util.ConverterUtil.fillCommonParams;
import static com.rbkmoney.anapi.v2.util.ConverterUtil.merge;
import static com.rbkmoney.magista.InvoicePaymentFlowType.hold;
import static com.rbkmoney.magista.InvoicePaymentFlowType.instant;
import static com.rbkmoney.magista.InvoicePaymentStatus.*;
import static com.rbkmoney.magista.PaymentTool.bank_card;
import static com.rbkmoney.magista.PaymentTool.payment_terminal;
import static com.rbkmoney.magista.PaymentToolType.bank_card;
import static com.rbkmoney.magista.PaymentToolType.payment_terminal;
@Component
public class ParamsToPaymentSearchQueryConverter {
@ -30,9 +24,7 @@ public class ParamsToPaymentSearchQueryConverter {
OffsetDateTime fromTime,
OffsetDateTime toTime,
Integer limit,
String shopID,
List<String> shopIDs,
String paymentInstitutionRealm,
List<String> invoiceIDs,
String paymentStatus, String paymentFlow,
String paymentMethod,
@ -48,34 +40,26 @@ public class ParamsToPaymentSearchQueryConverter {
String last4,
String rrn,
String approvalCode,
BankCardTokenProvider bankCardTokenProvider,
BankCardPaymentSystem bankCardPaymentSystem,
String bankCardTokenProvider,
String bankCardPaymentSystem,
Long paymentAmountFrom,
Long paymentAmountTo,
List<String> excludedShops,
String continuationToken) {
//TODO: Mapping for paymentInstitutionRealm
PaymentSearchQuery query = new PaymentSearchQuery()
.setCommonSearchQueryParams(
fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs),
continuationToken))
fillCommonParams(fromTime, toTime, limit, partyID, shopIDs, continuationToken))
.setExcludedShopIds(excludedShops)
.setExternalId(externalID)
.setInvoiceIds(merge(invoiceID, invoiceIDs));
PaymentParams paymentParams = new PaymentParams()
.setPaymentTool(paymentMethod != null ? mapToPaymentTool(paymentMethod) : null)
.setPaymentFlow(paymentFlow != null ? mapToInvoicePaymentFlow(paymentFlow) : null)
.setPaymentTool(paymentMethod != null ? mapPaymentTool(paymentMethod) : null)
.setPaymentFlow(paymentFlow != null ? mapInvoicePaymentFlow(paymentFlow) : null)
.setPaymentTerminalProvider(
paymentTerminalProvider != null
? LegacyTerminalPaymentProvider.valueOf(paymentTerminalProvider) :
null)
paymentTerminalProvider != null ? mapTerminalProvider(paymentTerminalProvider) : null)
.setPaymentTokenProvider(
bankCardTokenProvider != null
?
LegacyBankCardTokenProvider
.valueOf(bankCardTokenProvider.getValue()) :
null)
bankCardTokenProvider != null ? mapTokenProvider(bankCardTokenProvider) : null)
.setPaymentEmail(payerEmail)
.setPaymentApprovalCode(approvalCode)
.setPaymentCustomerId(customerID)
@ -87,8 +71,7 @@ public class ParamsToPaymentSearchQueryConverter {
.setPaymentRrn(rrn)
.setPaymentStatus(paymentStatus != null ? mapStatus(paymentStatus) : null)
.setPaymentSystem(bankCardPaymentSystem != null
? LegacyBankCardPaymentSystem.valueOf(bankCardPaymentSystem.getValue()) :
null);
? mapPaymentSystem(bankCardPaymentSystem) : null);
if (paymentAmountFrom != null) {
paymentParams.setPaymentAmountFrom(paymentAmountFrom);
}
@ -99,7 +82,32 @@ public class ParamsToPaymentSearchQueryConverter {
return query;
}
private com.rbkmoney.magista.PaymentTool mapToPaymentTool(String paymentMethod) {
protected LegacyTerminalPaymentProvider mapTerminalProvider(String provider) {
try {
return LegacyTerminalPaymentProvider.valueOf(provider);
} catch (IllegalArgumentException e) {
throw new BadRequestException(String.format("Terminal provider %s cannot be processed", provider));
}
}
protected LegacyBankCardTokenProvider mapTokenProvider(String provider) {
try {
return LegacyBankCardTokenProvider.valueOf(provider);
} catch (IllegalArgumentException e) {
throw new BadRequestException(String.format("Token provider %s cannot be processed", provider));
}
}
protected LegacyBankCardPaymentSystem mapPaymentSystem(String system) {
try {
return LegacyBankCardPaymentSystem.valueOf(system);
} catch (IllegalArgumentException e) {
throw new BadRequestException(
String.format("Payment system %s cannot be processed", system));
}
}
protected PaymentToolType mapPaymentTool(String paymentMethod) {
return switch (paymentMethod) {
case "bankCard" -> bank_card;
case "paymentTerminal" -> payment_terminal;
@ -108,26 +116,31 @@ public class ParamsToPaymentSearchQueryConverter {
};
}
private com.rbkmoney.magista.InvoicePaymentFlowType mapToInvoicePaymentFlow(String paymentFlow) {
return switch (paymentFlow) {
case "instant" -> instant;
case "hold" -> hold;
default -> throw new BadRequestException(
protected com.rbkmoney.magista.InvoicePaymentFlowType mapInvoicePaymentFlow(String paymentFlow) {
try {
return InvoicePaymentFlowType.valueOf(paymentFlow);
} catch (IllegalArgumentException e) {
throw new BadRequestException(
String.format("Payment flow %s cannot be processed", paymentFlow));
};
}
}
private InvoicePaymentStatus mapStatus(String paymentStatus) {
var status = Enum.valueOf(PaymentStatus.StatusEnum.class, paymentStatus);
return switch (status) {
case PENDING -> pending;
case PROCESSED -> processed;
case CAPTURED -> captured;
case CANCELLED -> cancelled;
case REFUNDED -> refunded;
case FAILED -> failed;
default -> throw new BadRequestException(
String.format("Payment status %s cannot be processed", paymentStatus));
};
protected InvoicePaymentStatus mapStatus(String paymentStatus) {
try {
var status = PaymentStatus.StatusEnum.fromValue(paymentStatus);
return switch (status) {
case PENDING -> pending;
case PROCESSED -> processed;
case CAPTURED -> captured;
case CANCELLED -> cancelled;
case REFUNDED -> refunded;
case FAILED -> failed;
case CHARGEDBACK -> charged_back;
default -> throw new BadRequestException(
String.format("Payment status %s cannot be processed", paymentStatus));
};
} catch (Exception e) {
throw new BadRequestException(e.getMessage());
}
}
}

View File

@ -1,18 +1,14 @@
package com.rbkmoney.anapi.v2.converter.search.request;
import com.rbkmoney.anapi.v2.exception.BadRequestException;
import com.rbkmoney.damsel.domain.PaymentInstitutionAccount;
import com.rbkmoney.damsel.domain.PayoutToolInfo;
import com.rbkmoney.damsel.domain.RussianBankAccount;
import com.rbkmoney.damsel.domain.WalletInfo;
import com.rbkmoney.magista.PayoutSearchQuery;
import com.rbkmoney.magista.PayoutToolType;
import org.springframework.stereotype.Component;
import java.time.OffsetDateTime;
import java.util.List;
import static com.rbkmoney.anapi.v2.util.ConverterUtil.fillCommonParams;
import static com.rbkmoney.anapi.v2.util.ConverterUtil.merge;
@Component
public class ParamsToPayoutSearchQueryConverter {
@ -21,35 +17,24 @@ public class ParamsToPayoutSearchQueryConverter {
OffsetDateTime fromTime,
OffsetDateTime toTime,
Integer limit,
String shopID,
List<String> shopIDs,
String paymentInstitutionRealm,
Integer offset,
String payoutID,
String payoutToolType,
List<String> excludedShops,
String continuationToken) {
//TODO: Mapping for paymentInstitutionRealm, offset, excludedShops
return new PayoutSearchQuery()
.setCommonSearchQueryParams(
fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs),
continuationToken))
fillCommonParams(fromTime, toTime, limit, partyID, shopIDs, continuationToken))
.setPayoutId(payoutID)
.setPayoutType(payoutToolType != null ? mapToDamselPayoutToolInfo(payoutToolType) : null);
.setPayoutType(payoutToolType != null ? mapPayoutToolType(payoutToolType) : null);
}
private PayoutToolInfo mapToDamselPayoutToolInfo(String payoutToolType) {
var payoutToolInfo = new PayoutToolInfo();
switch (payoutToolType) {
case "PayoutAccount" -> payoutToolInfo
.setRussianBankAccount(new RussianBankAccount());//TODO: Russian or International?
case "Wallet" -> payoutToolInfo.setWalletInfo(new WalletInfo());
case "PaymentInstitutionAccount" -> payoutToolInfo
.setPaymentInstitutionAccount(new PaymentInstitutionAccount());
protected PayoutToolType mapPayoutToolType(String payoutToolType) {
return switch (payoutToolType) {
case "PayoutAccount" -> PayoutToolType.payout_account;
case "Wallet" -> PayoutToolType.wallet;
case "PaymentInstitutionAccount" -> PayoutToolType.payment_institution_account;
default -> throw new BadRequestException(
String.format("PayoutToolType %s cannot be processed", payoutToolType));
}
return payoutToolInfo;
};
}
}

View File

@ -1,8 +1,8 @@
package com.rbkmoney.anapi.v2.converter.search.request;
import com.rbkmoney.anapi.v2.exception.BadRequestException;
import com.rbkmoney.magista.InvoicePaymentRefundStatus;
import com.rbkmoney.magista.RefundSearchQuery;
import com.rbkmoney.openapi.anapi_v2.model.RefundStatus;
import org.springframework.stereotype.Component;
import java.time.OffsetDateTime;
@ -10,7 +10,6 @@ import java.util.List;
import static com.rbkmoney.anapi.v2.util.ConverterUtil.fillCommonParams;
import static com.rbkmoney.anapi.v2.util.ConverterUtil.merge;
import static com.rbkmoney.magista.InvoicePaymentRefundStatus.*;
@Component
public class ParamsToRefundSearchQueryConverter {
@ -19,38 +18,31 @@ public class ParamsToRefundSearchQueryConverter {
OffsetDateTime fromTime,
OffsetDateTime toTime,
Integer limit,
String shopID,
List<String> shopIDs,
String paymentInstitutionRealm,
Integer offset,
List<String> invoiceIDs,
String invoiceID,
String paymentID,
String refundID,
String externalID,
String refundStatus,
List<String> excludedShops,
String continuationToken) {
//TODO: Mapping for paymentInstitutionRealm, offset, excludedShops
return new RefundSearchQuery()
.setCommonSearchQueryParams(
fillCommonParams(fromTime, toTime, limit, partyID, merge(shopID, shopIDs),
continuationToken))
.setRefundStatus(refundStatus != null ? getRefundStatus(refundStatus) : null)
fillCommonParams(fromTime, toTime, limit, partyID, shopIDs, continuationToken))
.setRefundStatus(refundStatus != null ? mapStatus(refundStatus) : null)
.setInvoiceIds(merge(invoiceID, invoiceIDs))
.setExternalId(externalID)
.setPaymentId(paymentID)
.setRefundId(refundID);
}
private com.rbkmoney.magista.InvoicePaymentRefundStatus getRefundStatus(String refundStatus) {
return switch (Enum.valueOf(RefundStatus.StatusEnum.class, refundStatus)) {
case PENDING -> pending;
case SUCCEEDED -> succeeded;
case FAILED -> failed;
default -> throw new BadRequestException(
String.format("Refund status %s cannot be processed", refundStatus));
};
protected InvoicePaymentRefundStatus mapStatus(String status) {
try {
return InvoicePaymentRefundStatus.valueOf(status);
} catch (IllegalArgumentException e) {
throw new BadRequestException(
String.format("Refund status %s cannot be processed", status));
}
}
}

View File

@ -22,7 +22,7 @@ public class StatChargebackToChargebackConverter {
.fee(chargeback.getFee())
.chargebackReason(chargeback.getChargebackReason() != null
? new ChargebackReason()
.category(mapToChargebackCategory(chargeback.getChargebackReason().getCategory()))
.category(mapCategory(chargeback.getChargebackReason().getCategory()))
.code(chargeback.getChargebackReason().getCode()) : null)
.content(chargeback.getContent() != null
? new Content().data(chargeback.getContent().getData())
@ -30,9 +30,8 @@ public class StatChargebackToChargebackConverter {
.bodyCurrency(chargeback.getCurrencyCode().getSymbolicCode());
}
private ChargebackCategory mapToChargebackCategory(InvoicePaymentChargebackCategory chargebackCategory) {
protected ChargebackCategory mapCategory(InvoicePaymentChargebackCategory chargebackCategory) {
if (chargebackCategory.isSetAuthorisation()) {
//TODO: Is it a typo? Could be fixed? (authorization)
return ChargebackCategory.AUTHORISATION;
}

View File

@ -28,7 +28,7 @@ public class StatInvoiceToInvoiceConverter {
.cost(invoiceLine.getQuantity() * invoiceLine.getPrice().getAmount())
.price(invoiceLine.getPrice().getAmount())
.product(invoiceLine.getProduct())
.taxMode(getTaxMode(invoiceLine.getMetadata()))
.taxMode(mapTaxMode(invoiceLine.getMetadata()))
).collect(Collectors.toList()) : null)
.description(invoice.getDescription())
.dueDate(TypeUtil.stringToInstant(invoice.getDue()).atOffset(ZoneOffset.UTC))
@ -36,11 +36,11 @@ public class StatInvoiceToInvoiceConverter {
.product(invoice.getProduct())
.shopID(invoice.getShopId());
fillStatusInfo(result, invoice.getStatus());
mapStatusInfo(result, invoice.getStatus());
return result;
}
private void fillStatusInfo(Invoice invoice, InvoiceStatus status) {
protected void mapStatusInfo(Invoice invoice, InvoiceStatus status) {
if (status.isSetFulfilled()) {
invoice.setStatus(Invoice.StatusEnum.FULFILLED);
invoice.setReason(status.getFulfilled().getDetails());
@ -67,7 +67,7 @@ public class StatInvoiceToInvoiceConverter {
String.format("Invoice status %s cannot be processed", status));
}
private InvoiceLineTaxMode getTaxMode(Map<String, Value> metadata) {
protected InvoiceLineTaxMode mapTaxMode(Map<String, Value> metadata) {
Value taxMode = metadata.get("TaxMode");
if (taxMode != null) {
return new InvoiceLineTaxVAT()

View File

@ -33,7 +33,7 @@ public class StatPaymentToPaymentSearchResultConverter {
.id(payment.getId())
.invoiceID(payment.getInvoiceId())
.makeRecurrent(payment.isMakeRecurrent())
.payer(getPayer(payment))
.payer(mapPayer(payment.getPayer()))
.shopID(payment.getShopId())
.shortID(payment.getShortId())
.transactionInfo(payment.getAdditionalTransactionInfo() != null
@ -43,27 +43,25 @@ public class StatPaymentToPaymentSearchResultConverter {
: null);
}
private Payer getPayer(StatPayment payment) {
var statPayer = payment.getPayer();
Payer payer = new Payer();
protected Payer mapPayer(com.rbkmoney.magista.Payer payer) {
if (statPayer.isSetCustomer()) {
return payer.payerType(Payer.PayerTypeEnum.CUSTOMERPAYER);
if (payer.isSetCustomer()) {
return new Payer().payerType(Payer.PayerTypeEnum.CUSTOMERPAYER);
}
if (statPayer.isSetPaymentResource()) {
return payer.payerType(Payer.PayerTypeEnum.PAYMENTRESOURCEPAYER);
if (payer.isSetPaymentResource()) {
return new Payer().payerType(Payer.PayerTypeEnum.PAYMENTRESOURCEPAYER);
}
if (statPayer.isSetRecurrent()) {
return payer.payerType(Payer.PayerTypeEnum.RECURRENTPAYER);
if (payer.isSetRecurrent()) {
return new Payer().payerType(Payer.PayerTypeEnum.RECURRENTPAYER);
}
throw new IllegalArgumentException(
String.format("Payer %s cannot be processed", statPayer));
String.format("Payer %s cannot be processed", payer));
}
private PaymentSearchResult.StatusEnum mapStatus(InvoicePaymentStatus status) {
protected PaymentSearchResult.StatusEnum mapStatus(InvoicePaymentStatus status) {
return switch (status) {
case pending -> PENDING;
case processed -> PROCESSED;
@ -71,7 +69,7 @@ public class StatPaymentToPaymentSearchResultConverter {
case cancelled -> CANCELLED;
case refunded -> REFUNDED;
case failed -> FAILED;
//case charged_back ->; TODO: OpenAPI missing status, should be added?
case charged_back -> CHARGEDBACK;
default -> throw new IllegalArgumentException(
String.format("Payment status %s cannot be processed", status));

View File

@ -21,16 +21,16 @@ public class StatPayoutToPayoutConverter {
.currency(payout.getCurrencySymbolicCode())
.fee(payout.getFee())
.id(payout.getId())
.payoutToolDetails(mapToPayoutToolDetails(payout.getPayoutToolInfo()))
.payoutToolDetails(mapPayoutToolDetails(payout.getPayoutToolInfo()))
.shopID(payout.getShopId())
.status(mapToPayoutStatus(payout.getStatus()))
.status(mapStatus(payout.getStatus()))
.cancellationDetails(
payout.getStatus().isSetCancelled()
? payout.getStatus().getCancelled().getDetails()
: null);
}
private String mapToPayoutStatus(PayoutStatus status) {
protected String mapStatus(PayoutStatus status) {
if (status.isSetCancelled()) {
return "Cancelled";
}
@ -51,7 +51,7 @@ public class StatPayoutToPayoutConverter {
String.format("Payout status %s cannot be processed", status));
}
private PayoutToolDetails mapToPayoutToolDetails(PayoutToolInfo payoutToolInfo) {
protected PayoutToolDetails mapPayoutToolDetails(PayoutToolInfo payoutToolInfo) {
if (payoutToolInfo.isSetRussianBankAccount()) {
var account = payoutToolInfo.getRussianBankAccount();
return new PayoutToolDetailsBankAccount()
@ -71,11 +71,12 @@ public class StatPayoutToPayoutConverter {
? new InternationalBankDetails()
.name(account.getBank().getName())
.bic(account.getBank().getBic())
.countryCode(getCountryCode(account.getBank().getCountry()))
.countryCode(mapCountryCode(account.getBank().getCountry()))
.address(account.getBank().getAddress())
.abartn(account.getBank().getAbaRtn())
: null)
.correspondentBankAccount(mapToInternationalCorrespondentBankAccount(account))
.correspondentBankAccount(
mapInternationalCorrespondentBankAccount(account.getCorrespondentAccount()))
.detailsType("PayoutToolDetailsInternationalBankAccount");
}
@ -95,15 +96,18 @@ public class StatPayoutToPayoutConverter {
}
private String getCountryCode(@Nullable CountryCode countryCode) {
protected String mapCountryCode(@Nullable CountryCode countryCode) {
if (countryCode == null) {
return null;
}
return countryCode.name();
}
private InternationalCorrespondentBankAccount mapToInternationalCorrespondentBankAccount(
protected InternationalCorrespondentBankAccount mapInternationalCorrespondentBankAccount(
com.rbkmoney.damsel.domain.InternationalBankAccount account) {
if (account == null) {
return null;
}
var details = account.getBank();
return new InternationalCorrespondentBankAccount()
.bankDetails(details != null
@ -117,7 +121,7 @@ public class StatPayoutToPayoutConverter {
.iban(account.getIban())
.number(account.getNumber())
.correspondentBankAccount(account.getCorrespondentAccount() != null
? mapToInternationalCorrespondentBankAccount(account.getCorrespondentAccount())
? mapInternationalCorrespondentBankAccount(account.getCorrespondentAccount())
: null);
}
}

View File

@ -19,15 +19,15 @@ public class StatRefundToRefundSearchResultConverter {
.currency(refund.getCurrencySymbolicCode())
.id(refund.getId())
.shopID(refund.getShopId())
.status(mapToRefundStatus(refund.getStatus()))
.status(mapStatus(refund.getStatus()))
.externalID(refund.getExternalId())
.error(mapToRefundStatusError(refund.getStatus()))
.error(mapStatusError(refund.getStatus()))
.invoiceID(refund.getInvoiceId())
.paymentID(refund.getPaymentId())
.reason(refund.getReason());
}
private RefundStatusError mapToRefundStatusError(InvoicePaymentRefundStatus status) {
protected RefundStatusError mapStatusError(InvoicePaymentRefundStatus status) {
if (status.isSetFailed() && status.getFailed().getFailure().isSetFailure()) {
var failure = status.getFailed().getFailure().getFailure();
return new RefundStatusError()
@ -38,7 +38,7 @@ public class StatRefundToRefundSearchResultConverter {
return null;
}
private RefundSearchResult.StatusEnum mapToRefundStatus(InvoicePaymentRefundStatus status) {
protected RefundSearchResult.StatusEnum mapStatus(InvoicePaymentRefundStatus status) {
if (status.isSetPending()) {
return RefundSearchResult.StatusEnum.PENDING;
}

View File

@ -0,0 +1,12 @@
package com.rbkmoney.anapi.v2.exception;
public class AuthorizationException extends RuntimeException {
public AuthorizationException(String s) {
super(s);
}
public AuthorizationException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -0,0 +1,11 @@
package com.rbkmoney.anapi.v2.exception;
public class BouncerException extends AuthorizationException {
public BouncerException(String s) {
super(s);
}
public BouncerException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -0,0 +1,77 @@
package com.rbkmoney.anapi.v2.security;
import com.rbkmoney.anapi.v2.exception.AuthorizationException;
import com.rbkmoney.anapi.v2.service.BouncerService;
import com.rbkmoney.anapi.v2.service.KeycloakService;
import com.rbkmoney.anapi.v2.service.VortigonService;
import com.rbkmoney.bouncer.base.Entity;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.keycloak.representations.AccessToken;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
@Slf4j
@RequiredArgsConstructor
@Service
public class AccessService {
private final VortigonService vortigonService;
private final BouncerService bouncerService;
private final KeycloakService keycloakService;
@Value("${service.bouncer.auth.enabled}")
private boolean authEnabled;
public List<String> getAccessibleShops(String operationId, String partyId, List<String> requestShopIds,
String realm) {
List<String> shopIds = vortigonService.getShopIds(partyId, Objects.requireNonNullElse(realm, "live"));
if (!shopIds.isEmpty()) {
shopIds = requestShopIds.stream()
.filter(shopIds::contains)
.collect(Collectors.toList());
}
log.info("Check the user's rights to perform the operation {}", operationId);
var ctx = buildAnapiBouncerContext(operationId, partyId, shopIds);
var resolution = bouncerService.getResolution(ctx);
if (resolution.isSetForbidden()) {
if (authEnabled) {
throw new AuthorizationException(String.format("No rights to perform %s", operationId));
} else {
log.warn("No rights to perform {}", operationId);
}
}
if (resolution.isSetRestricted()) {
if (authEnabled) {
return resolution.getRestricted().getRestrictions().getAnapi().getOp().getShops().stream()
.map(Entity::getId)
.collect(Collectors.toList());
} else {
log.warn("Rights to perform {} are restricted", operationId);
}
}
return new ArrayList<>(shopIds);
}
private AnapiBouncerContext buildAnapiBouncerContext(String operationId, String partyId, List<String> shopIds) {
AccessToken token = keycloakService.getAccessToken();
return AnapiBouncerContext.builder()
.operationId(operationId)
.partyId(partyId)
.shopIds(shopIds)
.tokenExpiration(token.getExp())
.tokenId(token.getId())
.userId(token.getSubject())
.build();
}
}

View File

@ -0,0 +1,18 @@
package com.rbkmoney.anapi.v2.security;
import lombok.Builder;
import lombok.Data;
import java.util.List;
@Builder
@Data
public class AnapiBouncerContext {
private final long tokenExpiration;
private final String tokenId;
private final String userId;
private final String operationId;
private final String partyId;
private final List<String> shopIds;
}

View File

@ -0,0 +1,81 @@
package com.rbkmoney.anapi.v2.security;
import com.rbkmoney.anapi.v2.config.properties.BouncerProperties;
import com.rbkmoney.anapi.v2.service.KeycloakService;
import com.rbkmoney.anapi.v2.service.OrgMgmtService;
import com.rbkmoney.bouncer.base.Entity;
import com.rbkmoney.bouncer.context.v1.*;
import com.rbkmoney.bouncer.ctx.ContextFragmentType;
import com.rbkmoney.bouncer.decisions.Context;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.apache.thrift.TSerializer;
import org.keycloak.representations.AccessToken;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.util.Set;
import java.util.stream.Collectors;
@RequiredArgsConstructor
@Component
public class BouncerContextFactory {
private final BouncerProperties bouncerProperties;
private final OrgMgmtService orgMgmtService;
private final KeycloakService keycloakService;
@SneakyThrows
public Context buildContext(AnapiBouncerContext bouncerContext) {
ContextFragment contextFragment = buildContextFragment(bouncerContext);
var serializer = new TSerializer();
var fragment = new com.rbkmoney.bouncer.ctx.ContextFragment()
.setType(ContextFragmentType.v1_thrift_binary)
.setContent(serializer.serialize(contextFragment));
com.rbkmoney.bouncer.ctx.ContextFragment
userFragment = orgMgmtService.getUserAuthContext(keycloakService.getAccessToken().getSubject());
Context context = new Context();
context.putToFragments(bouncerProperties.getContextFragmentId(), fragment);
context.putToFragments("user", userFragment);
return context;
}
private ContextFragment buildContextFragment(AnapiBouncerContext bouncerContext) {
Environment env = buildEnvironment();
AccessToken accessToken = keycloakService.getAccessToken();
ContextAnalyticsAPI contextAnalyticsApi = buildAnapiContext(bouncerContext);
return new ContextFragment()
.setAuth(buildAuth(bouncerContext, accessToken))
.setEnv(env)
.setAnapi(contextAnalyticsApi);
}
private Auth buildAuth(AnapiBouncerContext bouncerContext, AccessToken accessToken) {
Auth auth = new Auth();
Set<AuthScope> authScopeSet = bouncerContext.getShopIds().stream()
.map(shopId -> new AuthScope()
.setParty(new Entity().setId(bouncerContext.getPartyId()))
.setShop(new Entity().setId(shopId)))
.collect(Collectors.toSet());
return auth.setToken(new Token().setId(accessToken.getId()))
.setMethod(bouncerProperties.getAuthMethod())
.setExpiration(Instant.ofEpochSecond(accessToken.getExp()).toString())
.setScope(authScopeSet);
}
private Environment buildEnvironment() {
Deployment deployment = new Deployment()
.setId(bouncerProperties.getDeploymentId());
return new Environment()
.setDeployment(deployment)
.setNow(Instant.now().toString());
}
private ContextAnalyticsAPI buildAnapiContext(AnapiBouncerContext ctx) {
return new ContextAnalyticsAPI()
.setOp(new AnalyticsAPIOperation()
.setId(ctx.getOperationId()));
}
}

View File

@ -0,0 +1,39 @@
package com.rbkmoney.anapi.v2.service;
import com.rbkmoney.anapi.v2.config.properties.BouncerProperties;
import com.rbkmoney.anapi.v2.exception.BouncerException;
import com.rbkmoney.anapi.v2.security.AnapiBouncerContext;
import com.rbkmoney.anapi.v2.security.BouncerContextFactory;
import com.rbkmoney.bouncer.decisions.ArbiterSrv;
import com.rbkmoney.bouncer.decisions.Context;
import com.rbkmoney.bouncer.decisions.Judgement;
import com.rbkmoney.bouncer.decisions.Resolution;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.thrift.TException;
import org.springframework.stereotype.Service;
@Slf4j
@Service
@RequiredArgsConstructor
public class BouncerService {
private final BouncerProperties bouncerProperties;
private final BouncerContextFactory bouncerContextFactory;
private final ArbiterSrv.Iface bouncerClient;
public Resolution getResolution(AnapiBouncerContext bouncerContext) {
log.debug("Check access with bouncer context: {}", bouncerContext);
Context context = bouncerContextFactory.buildContext(bouncerContext);
log.debug("Built thrift context: {}", context);
try {
Judgement judge = bouncerClient.judge(bouncerProperties.getRuleSetId(), context);
log.debug("Have judge: {}", judge);
Resolution resolution = judge.getResolution();
log.debug("Resolution: {}", resolution);
return resolution;
} catch (TException e) {
throw new BouncerException("Error while call bouncer", e);
}
}
}

View File

@ -0,0 +1,17 @@
package com.rbkmoney.anapi.v2.service;
import org.keycloak.KeycloakPrincipal;
import org.keycloak.representations.AccessToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
@Service
public class KeycloakService {
public AccessToken getAccessToken() {
var keycloakPrincipal = (KeycloakPrincipal) SecurityContextHolder.getContext()
.getAuthentication()
.getPrincipal();
return keycloakPrincipal.getKeycloakSecurityContext().getToken();
}
}

View File

@ -0,0 +1,22 @@
package com.rbkmoney.anapi.v2.service;
import com.rbkmoney.bouncer.ctx.ContextFragment;
import com.rbkmoney.orgmanagement.AuthContextProviderSrv;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@RequiredArgsConstructor
@Service
public class OrgMgmtService {
private final AuthContextProviderSrv.Iface orgMgmtClient;
public ContextFragment getUserAuthContext(String userId) {
try {
return orgMgmtClient.getUserContext(userId);
} catch (Exception e) {
throw new RuntimeException(String.format("Can't get user auth context: userId = %s", userId), e);
}
}
}

View File

@ -0,0 +1,31 @@
package com.rbkmoney.anapi.v2.service;
import com.rbkmoney.anapi.v2.exception.BadRequestException;
import com.rbkmoney.damsel.vortigon.PaymentInstitutionRealm;
import com.rbkmoney.damsel.vortigon.VortigonServiceSrv;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
@RequiredArgsConstructor
public class VortigonService {
private final VortigonServiceSrv.Iface vortigonClient;
@SneakyThrows
public List<String> getShopIds(String partyId, String realm) {
return vortigonClient.getShopsIds(partyId, mapRealm(realm));
}
private PaymentInstitutionRealm mapRealm(String realm) {
try {
return PaymentInstitutionRealm.valueOf(realm);
} catch (IllegalArgumentException e) {
throw new BadRequestException(
String.format("Realm %s cannot be processed", realm));
}
}
}

View File

@ -27,8 +27,24 @@ management:
service:
magista:
url: http://magista:8022/stat
url: http://localhost:8022/change_it
networkTimeout: 5000
vortigon:
url: http://localhost:8022/change_it
networkTimeout: 5000
orgManagement:
url: http://localhost:8022/change_it
networkTimeout: 5000
bouncer:
url: http://localhost:8022/change_it
networkTimeout: 10000
context-fragment-id: anapi
deployment-id: production
auth-method: SessionToken
realm: external
rule-set-id: change_it
auth:
enabled: false
spring:
application:

View File

@ -3,8 +3,11 @@ package com.rbkmoney.anapi.v2;
import com.rbkmoney.anapi.v2.config.AbstractKeycloakOpenIdAsWiremockConfig;
import com.rbkmoney.anapi.v2.testutil.MagistaUtil;
import com.rbkmoney.anapi.v2.testutil.OpenApiUtil;
import com.rbkmoney.bouncer.decisions.ArbiterSrv;
import com.rbkmoney.damsel.vortigon.VortigonServiceSrv;
import com.rbkmoney.magista.MerchantStatisticsServiceSrv;
import com.rbkmoney.openapi.anapi_v2.model.DefaultLogicError;
import com.rbkmoney.orgmanagement.AuthContextProviderSrv;
import lombok.SneakyThrows;
import org.apache.thrift.TException;
import org.junit.jupiter.api.AfterEach;
@ -19,7 +22,10 @@ import org.springframework.util.MultiValueMap;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.List;
import static com.rbkmoney.anapi.v2.testutil.MagistaUtil.createContextFragment;
import static com.rbkmoney.anapi.v2.testutil.MagistaUtil.createJudgementAllowed;
import static java.util.UUID.randomUUID;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
@ -33,6 +39,12 @@ class SearchChargebacksTest extends AbstractKeycloakOpenIdAsWiremockConfig {
@MockBean
public MerchantStatisticsServiceSrv.Iface magistaClient;
@MockBean
public VortigonServiceSrv.Iface vortigonClient;
@MockBean
public AuthContextProviderSrv.Iface orgMgmtClient;
@MockBean
public ArbiterSrv.Iface bouncerClient;
@Autowired
private MockMvc mvc;
@ -44,7 +56,7 @@ class SearchChargebacksTest extends AbstractKeycloakOpenIdAsWiremockConfig {
@BeforeEach
public void init() {
mocks = MockitoAnnotations.openMocks(this);
preparedMocks = new Object[] {magistaClient};
preparedMocks = new Object[] {magistaClient, vortigonClient, orgMgmtClient, bouncerClient};
}
@AfterEach
@ -56,34 +68,46 @@ class SearchChargebacksTest extends AbstractKeycloakOpenIdAsWiremockConfig {
@Test
@SneakyThrows
void searchChargebacksRequiredParamsRequestSuccess() {
when(vortigonClient.getShopsIds(any(), any())).thenReturn(List.of("1", "2", "3"));
when(orgMgmtClient.getUserContext(any())).thenReturn(createContextFragment());
when(bouncerClient.judge(any(), any())).thenReturn(createJudgementAllowed());
when(magistaClient.searchChargebacks(any())).thenReturn(MagistaUtil.createSearchChargebackRequiredResponse());
mvc.perform(get("/chargebacks")
.header("Authorization", "Bearer " + generateInvoicesReadJwt())
.header("X-Request-ID", randomUUID())
.header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString())
.params(OpenApiUtil.getSearchRequiredParams())
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(""))
.header("Authorization", "Bearer " + generateInvoicesReadJwt())
.header("X-Request-ID", randomUUID())
.header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString())
.params(OpenApiUtil.getSearchRequiredParams())
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(""))
.andDo(print())
.andExpect(status().is2xxSuccessful())
.andExpect(jsonPath("$").exists());
verify(vortigonClient, times(1)).getShopsIds(any(), any());
verify(orgMgmtClient, times(1)).getUserContext(any());
verify(bouncerClient, times(1)).judge(any(), any());
verify(magistaClient, times(1)).searchChargebacks(any());
}
@Test
@SneakyThrows
void searchChargebacksAllParamsRequestSuccess() {
when(vortigonClient.getShopsIds(any(), any())).thenReturn(List.of("1", "2", "3"));
when(orgMgmtClient.getUserContext(any())).thenReturn(createContextFragment());
when(bouncerClient.judge(any(), any())).thenReturn(createJudgementAllowed());
when(magistaClient.searchChargebacks(any())).thenReturn(MagistaUtil.createSearchChargebackAllResponse());
mvc.perform(get("/chargebacks")
.header("Authorization", "Bearer " + generateInvoicesReadJwt())
.header("X-Request-ID", randomUUID())
.header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString())
.params(OpenApiUtil.getSearchChargebackAllParams())
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(""))
.header("Authorization", "Bearer " + generateInvoicesReadJwt())
.header("X-Request-ID", randomUUID())
.header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString())
.params(OpenApiUtil.getSearchChargebackAllParams())
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(""))
.andDo(print())
.andExpect(status().is2xxSuccessful())
.andExpect(jsonPath("$").exists());
verify(vortigonClient, times(1)).getShopsIds(any(), any());
verify(orgMgmtClient, times(1)).getUserContext(any());
verify(bouncerClient, times(1)).judge(any(), any());
verify(magistaClient, times(1)).searchChargebacks(any());
}
@ -108,16 +132,22 @@ class SearchChargebacksTest extends AbstractKeycloakOpenIdAsWiremockConfig {
@Test
@SneakyThrows
void searchChargebacksRequestMagistaUnavailable() {
when(vortigonClient.getShopsIds(any(), any())).thenReturn(List.of("1", "2", "3"));
when(orgMgmtClient.getUserContext(any())).thenReturn(createContextFragment());
when(bouncerClient.judge(any(), any())).thenReturn(createJudgementAllowed());
when(magistaClient.searchPayments(any())).thenThrow(TException.class);
mvc.perform(get("/chargebacks")
.header("Authorization", "Bearer " + generateInvoicesReadJwt())
.header("X-Request-ID", randomUUID())
.header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString())
.params(OpenApiUtil.getSearchRequiredParams())
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(""))
.header("Authorization", "Bearer " + generateInvoicesReadJwt())
.header("X-Request-ID", randomUUID())
.header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString())
.params(OpenApiUtil.getSearchRequiredParams())
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(""))
.andDo(print())
.andExpect(status().is5xxServerError());
verify(vortigonClient, times(1)).getShopsIds(any(), any());
verify(orgMgmtClient, times(1)).getUserContext(any());
verify(bouncerClient, times(1)).judge(any(), any());
verify(magistaClient, times(1)).searchChargebacks(any());
}
}

View File

@ -3,8 +3,11 @@ package com.rbkmoney.anapi.v2;
import com.rbkmoney.anapi.v2.config.AbstractKeycloakOpenIdAsWiremockConfig;
import com.rbkmoney.anapi.v2.testutil.MagistaUtil;
import com.rbkmoney.anapi.v2.testutil.OpenApiUtil;
import com.rbkmoney.bouncer.decisions.ArbiterSrv;
import com.rbkmoney.damsel.vortigon.VortigonServiceSrv;
import com.rbkmoney.magista.MerchantStatisticsServiceSrv;
import com.rbkmoney.openapi.anapi_v2.model.DefaultLogicError;
import com.rbkmoney.orgmanagement.AuthContextProviderSrv;
import lombok.SneakyThrows;
import org.apache.thrift.TException;
import org.junit.jupiter.api.AfterEach;
@ -19,7 +22,10 @@ import org.springframework.util.MultiValueMap;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.List;
import static com.rbkmoney.anapi.v2.testutil.MagistaUtil.createContextFragment;
import static com.rbkmoney.anapi.v2.testutil.MagistaUtil.createJudgementAllowed;
import static java.util.UUID.randomUUID;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
@ -32,6 +38,12 @@ class SearchInvoicesTest extends AbstractKeycloakOpenIdAsWiremockConfig {
@MockBean
public MerchantStatisticsServiceSrv.Iface magistaClient;
@MockBean
public VortigonServiceSrv.Iface vortigonClient;
@MockBean
public AuthContextProviderSrv.Iface orgMgmtClient;
@MockBean
public ArbiterSrv.Iface bouncerClient;
@Autowired
private MockMvc mvc;
@ -43,7 +55,7 @@ class SearchInvoicesTest extends AbstractKeycloakOpenIdAsWiremockConfig {
@BeforeEach
public void init() {
mocks = MockitoAnnotations.openMocks(this);
preparedMocks = new Object[] {magistaClient};
preparedMocks = new Object[] {magistaClient, vortigonClient, orgMgmtClient, bouncerClient};
}
@AfterEach
@ -55,6 +67,9 @@ class SearchInvoicesTest extends AbstractKeycloakOpenIdAsWiremockConfig {
@Test
@SneakyThrows
void searchInvoicesRequiredParamsRequestSuccess() {
when(vortigonClient.getShopsIds(any(), any())).thenReturn(List.of("1", "2", "3"));
when(orgMgmtClient.getUserContext(any())).thenReturn(createContextFragment());
when(bouncerClient.judge(any(), any())).thenReturn(createJudgementAllowed());
when(magistaClient.searchInvoices(any())).thenReturn(MagistaUtil.createSearchInvoiceRequiredResponse());
mvc.perform(get("/invoices")
.header("Authorization", "Bearer " + generateInvoicesReadJwt())
@ -66,12 +81,18 @@ class SearchInvoicesTest extends AbstractKeycloakOpenIdAsWiremockConfig {
.andDo(print())
.andExpect(status().is2xxSuccessful())
.andExpect(jsonPath("$").exists());
verify(vortigonClient, times(1)).getShopsIds(any(), any());
verify(orgMgmtClient, times(1)).getUserContext(any());
verify(bouncerClient, times(1)).judge(any(), any());
verify(magistaClient, times(1)).searchInvoices(any());
}
@Test
@SneakyThrows
void searchInvoicesAllParamsRequestSuccess() {
when(vortigonClient.getShopsIds(any(), any())).thenReturn(List.of("1", "2", "3"));
when(orgMgmtClient.getUserContext(any())).thenReturn(createContextFragment());
when(bouncerClient.judge(any(), any())).thenReturn(createJudgementAllowed());
when(magistaClient.searchInvoices(any())).thenReturn(MagistaUtil.createSearchInvoiceAllResponse());
mvc.perform(get("/invoices")
.header("Authorization", "Bearer " + generateInvoicesReadJwt())
@ -83,6 +104,9 @@ class SearchInvoicesTest extends AbstractKeycloakOpenIdAsWiremockConfig {
.andDo(print())
.andExpect(status().is2xxSuccessful())
.andExpect(jsonPath("$").exists());
verify(vortigonClient, times(1)).getShopsIds(any(), any());
verify(orgMgmtClient, times(1)).getUserContext(any());
verify(bouncerClient, times(1)).judge(any(), any());
verify(magistaClient, times(1)).searchInvoices(any());
}
@ -107,6 +131,9 @@ class SearchInvoicesTest extends AbstractKeycloakOpenIdAsWiremockConfig {
@Test
@SneakyThrows
void searchInvoicesRequestMagistaUnavailable() {
when(vortigonClient.getShopsIds(any(), any())).thenReturn(List.of("1", "2", "3"));
when(orgMgmtClient.getUserContext(any())).thenReturn(createContextFragment());
when(bouncerClient.judge(any(), any())).thenReturn(createJudgementAllowed());
when(magistaClient.searchInvoices(any())).thenThrow(TException.class);
mvc.perform(get("/invoices")
.header("Authorization", "Bearer " + generateInvoicesReadJwt())
@ -117,6 +144,9 @@ class SearchInvoicesTest extends AbstractKeycloakOpenIdAsWiremockConfig {
.content(""))
.andDo(print())
.andExpect(status().is5xxServerError());
verify(vortigonClient, times(1)).getShopsIds(any(), any());
verify(orgMgmtClient, times(1)).getUserContext(any());
verify(bouncerClient, times(1)).judge(any(), any());
verify(magistaClient, times(1)).searchInvoices(any());
}
}

View File

@ -3,8 +3,11 @@ package com.rbkmoney.anapi.v2;
import com.rbkmoney.anapi.v2.config.AbstractKeycloakOpenIdAsWiremockConfig;
import com.rbkmoney.anapi.v2.testutil.MagistaUtil;
import com.rbkmoney.anapi.v2.testutil.OpenApiUtil;
import com.rbkmoney.bouncer.decisions.ArbiterSrv;
import com.rbkmoney.damsel.vortigon.VortigonServiceSrv;
import com.rbkmoney.magista.MerchantStatisticsServiceSrv;
import com.rbkmoney.openapi.anapi_v2.model.DefaultLogicError;
import com.rbkmoney.orgmanagement.AuthContextProviderSrv;
import lombok.SneakyThrows;
import org.apache.thrift.TException;
import org.junit.jupiter.api.AfterEach;
@ -19,7 +22,10 @@ import org.springframework.util.MultiValueMap;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.List;
import static com.rbkmoney.anapi.v2.testutil.MagistaUtil.createContextFragment;
import static com.rbkmoney.anapi.v2.testutil.MagistaUtil.createJudgementAllowed;
import static java.util.UUID.randomUUID;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
@ -33,6 +39,12 @@ class SearchPaymentsTest extends AbstractKeycloakOpenIdAsWiremockConfig {
@MockBean
public MerchantStatisticsServiceSrv.Iface magistaClient;
@MockBean
public VortigonServiceSrv.Iface vortigonClient;
@MockBean
public AuthContextProviderSrv.Iface orgMgmtClient;
@MockBean
public ArbiterSrv.Iface bouncerClient;
@Autowired
private MockMvc mvc;
@ -44,7 +56,7 @@ class SearchPaymentsTest extends AbstractKeycloakOpenIdAsWiremockConfig {
@BeforeEach
public void init() {
mocks = MockitoAnnotations.openMocks(this);
preparedMocks = new Object[] {magistaClient};
preparedMocks = new Object[] {magistaClient, vortigonClient, orgMgmtClient, bouncerClient};
}
@AfterEach
@ -56,6 +68,9 @@ class SearchPaymentsTest extends AbstractKeycloakOpenIdAsWiremockConfig {
@Test
@SneakyThrows
void searchPaymentsRequiredParamsRequestSuccess() {
when(vortigonClient.getShopsIds(any(), any())).thenReturn(List.of("1", "2", "3"));
when(orgMgmtClient.getUserContext(any())).thenReturn(createContextFragment());
when(bouncerClient.judge(any(), any())).thenReturn(createJudgementAllowed());
when(magistaClient.searchPayments(any())).thenReturn(MagistaUtil.createSearchPaymentRequiredResponse());
mvc.perform(get("/payments")
.header("Authorization", "Bearer " + generateInvoicesReadJwt())
@ -67,12 +82,18 @@ class SearchPaymentsTest extends AbstractKeycloakOpenIdAsWiremockConfig {
.andDo(print())
.andExpect(status().is2xxSuccessful())
.andExpect(jsonPath("$").exists());
verify(vortigonClient, times(1)).getShopsIds(any(), any());
verify(orgMgmtClient, times(1)).getUserContext(any());
verify(bouncerClient, times(1)).judge(any(), any());
verify(magistaClient, times(1)).searchPayments(any());
}
@Test
@SneakyThrows
void searchPaymentsAllParamsRequestSuccess() {
when(vortigonClient.getShopsIds(any(), any())).thenReturn(List.of("1", "2", "3"));
when(orgMgmtClient.getUserContext(any())).thenReturn(createContextFragment());
when(bouncerClient.judge(any(), any())).thenReturn(createJudgementAllowed());
when(magistaClient.searchPayments(any())).thenReturn(MagistaUtil.createSearchPaymentAllResponse());
mvc.perform(get("/payments")
.header("Authorization", "Bearer " + generateInvoicesReadJwt())
@ -84,6 +105,9 @@ class SearchPaymentsTest extends AbstractKeycloakOpenIdAsWiremockConfig {
.andDo(print())
.andExpect(status().is2xxSuccessful())
.andExpect(jsonPath("$").exists());
verify(vortigonClient, times(1)).getShopsIds(any(), any());
verify(orgMgmtClient, times(1)).getUserContext(any());
verify(bouncerClient, times(1)).judge(any(), any());
verify(magistaClient, times(1)).searchPayments(any());
}
@ -108,6 +132,9 @@ class SearchPaymentsTest extends AbstractKeycloakOpenIdAsWiremockConfig {
@Test
@SneakyThrows
void searchPaymentsRequestMagistaUnavailable() {
when(vortigonClient.getShopsIds(any(), any())).thenReturn(List.of("1", "2", "3"));
when(orgMgmtClient.getUserContext(any())).thenReturn(createContextFragment());
when(bouncerClient.judge(any(), any())).thenReturn(createJudgementAllowed());
when(magistaClient.searchPayments(any())).thenThrow(TException.class);
mvc.perform(get("/payments")
.header("Authorization", "Bearer " + generateInvoicesReadJwt())
@ -118,6 +145,9 @@ class SearchPaymentsTest extends AbstractKeycloakOpenIdAsWiremockConfig {
.content(""))
.andDo(print())
.andExpect(status().is5xxServerError());
verify(vortigonClient, times(1)).getShopsIds(any(), any());
verify(orgMgmtClient, times(1)).getUserContext(any());
verify(bouncerClient, times(1)).judge(any(), any());
verify(magistaClient, times(1)).searchPayments(any());
}
}

View File

@ -3,8 +3,11 @@ package com.rbkmoney.anapi.v2;
import com.rbkmoney.anapi.v2.config.AbstractKeycloakOpenIdAsWiremockConfig;
import com.rbkmoney.anapi.v2.testutil.MagistaUtil;
import com.rbkmoney.anapi.v2.testutil.OpenApiUtil;
import com.rbkmoney.bouncer.decisions.ArbiterSrv;
import com.rbkmoney.damsel.vortigon.VortigonServiceSrv;
import com.rbkmoney.magista.MerchantStatisticsServiceSrv;
import com.rbkmoney.openapi.anapi_v2.model.DefaultLogicError;
import com.rbkmoney.orgmanagement.AuthContextProviderSrv;
import lombok.SneakyThrows;
import org.apache.thrift.TException;
import org.junit.jupiter.api.AfterEach;
@ -19,7 +22,10 @@ import org.springframework.util.MultiValueMap;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.List;
import static com.rbkmoney.anapi.v2.testutil.MagistaUtil.createContextFragment;
import static com.rbkmoney.anapi.v2.testutil.MagistaUtil.createJudgementAllowed;
import static java.util.UUID.randomUUID;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
@ -32,6 +38,12 @@ class SearchPayoutsTest extends AbstractKeycloakOpenIdAsWiremockConfig {
@MockBean
public MerchantStatisticsServiceSrv.Iface magistaClient;
@MockBean
public VortigonServiceSrv.Iface vortigonClient;
@MockBean
public AuthContextProviderSrv.Iface orgMgmtClient;
@MockBean
public ArbiterSrv.Iface bouncerClient;
@Autowired
private MockMvc mvc;
@ -43,7 +55,7 @@ class SearchPayoutsTest extends AbstractKeycloakOpenIdAsWiremockConfig {
@BeforeEach
public void init() {
mocks = MockitoAnnotations.openMocks(this);
preparedMocks = new Object[] {magistaClient};
preparedMocks = new Object[] {magistaClient, vortigonClient, orgMgmtClient, bouncerClient};
}
@AfterEach
@ -55,6 +67,9 @@ class SearchPayoutsTest extends AbstractKeycloakOpenIdAsWiremockConfig {
@Test
@SneakyThrows
void searchPayoutsRequiredParamsRequestSuccess() {
when(vortigonClient.getShopsIds(any(), any())).thenReturn(List.of("1", "2", "3"));
when(orgMgmtClient.getUserContext(any())).thenReturn(createContextFragment());
when(bouncerClient.judge(any(), any())).thenReturn(createJudgementAllowed());
when(magistaClient.searchPayouts(any())).thenReturn(MagistaUtil.createSearchPayoutRequiredResponse());
mvc.perform(get("/payouts")
.header("Authorization", "Bearer " + generateInvoicesReadJwt())
@ -66,12 +81,18 @@ class SearchPayoutsTest extends AbstractKeycloakOpenIdAsWiremockConfig {
.andDo(print())
.andExpect(status().is2xxSuccessful())
.andExpect(jsonPath("$").exists());
verify(vortigonClient, times(1)).getShopsIds(any(), any());
verify(orgMgmtClient, times(1)).getUserContext(any());
verify(bouncerClient, times(1)).judge(any(), any());
verify(magistaClient, times(1)).searchPayouts(any());
}
@Test
@SneakyThrows
void searchPayoutsAllParamsRequestSuccess() {
when(vortigonClient.getShopsIds(any(), any())).thenReturn(List.of("1", "2", "3"));
when(orgMgmtClient.getUserContext(any())).thenReturn(createContextFragment());
when(bouncerClient.judge(any(), any())).thenReturn(createJudgementAllowed());
when(magistaClient.searchPayouts(any())).thenReturn(MagistaUtil.createSearchPayoutAllResponse());
mvc.perform(get("/payouts")
.header("Authorization", "Bearer " + generateInvoicesReadJwt())
@ -83,6 +104,9 @@ class SearchPayoutsTest extends AbstractKeycloakOpenIdAsWiremockConfig {
.andDo(print())
.andExpect(status().is2xxSuccessful())
.andExpect(jsonPath("$").exists());
verify(vortigonClient, times(1)).getShopsIds(any(), any());
verify(orgMgmtClient, times(1)).getUserContext(any());
verify(bouncerClient, times(1)).judge(any(), any());
verify(magistaClient, times(1)).searchPayouts(any());
}
@ -107,6 +131,9 @@ class SearchPayoutsTest extends AbstractKeycloakOpenIdAsWiremockConfig {
@Test
@SneakyThrows
void searchPayoutsRequestMagistaUnavailable() {
when(vortigonClient.getShopsIds(any(), any())).thenReturn(List.of("1", "2", "3"));
when(orgMgmtClient.getUserContext(any())).thenReturn(createContextFragment());
when(bouncerClient.judge(any(), any())).thenReturn(createJudgementAllowed());
when(magistaClient.searchPayouts(any())).thenThrow(TException.class);
mvc.perform(get("/payouts")
.header("Authorization", "Bearer " + generateInvoicesReadJwt())
@ -117,6 +144,9 @@ class SearchPayoutsTest extends AbstractKeycloakOpenIdAsWiremockConfig {
.content(""))
.andDo(print())
.andExpect(status().is5xxServerError());
verify(vortigonClient, times(1)).getShopsIds(any(), any());
verify(orgMgmtClient, times(1)).getUserContext(any());
verify(bouncerClient, times(1)).judge(any(), any());
verify(magistaClient, times(1)).searchPayouts(any());
}
}

View File

@ -3,8 +3,11 @@ package com.rbkmoney.anapi.v2;
import com.rbkmoney.anapi.v2.config.AbstractKeycloakOpenIdAsWiremockConfig;
import com.rbkmoney.anapi.v2.testutil.MagistaUtil;
import com.rbkmoney.anapi.v2.testutil.OpenApiUtil;
import com.rbkmoney.bouncer.decisions.ArbiterSrv;
import com.rbkmoney.damsel.vortigon.VortigonServiceSrv;
import com.rbkmoney.magista.MerchantStatisticsServiceSrv;
import com.rbkmoney.openapi.anapi_v2.model.DefaultLogicError;
import com.rbkmoney.orgmanagement.AuthContextProviderSrv;
import lombok.SneakyThrows;
import org.apache.thrift.TException;
import org.junit.jupiter.api.AfterEach;
@ -19,7 +22,10 @@ import org.springframework.util.MultiValueMap;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.List;
import static com.rbkmoney.anapi.v2.testutil.MagistaUtil.createContextFragment;
import static com.rbkmoney.anapi.v2.testutil.MagistaUtil.createJudgementAllowed;
import static java.util.UUID.randomUUID;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
@ -32,6 +38,12 @@ class SearchRefundsTest extends AbstractKeycloakOpenIdAsWiremockConfig {
@MockBean
public MerchantStatisticsServiceSrv.Iface magistaClient;
@MockBean
public VortigonServiceSrv.Iface vortigonClient;
@MockBean
public AuthContextProviderSrv.Iface orgMgmtClient;
@MockBean
public ArbiterSrv.Iface bouncerClient;
@Autowired
private MockMvc mvc;
@ -43,7 +55,7 @@ class SearchRefundsTest extends AbstractKeycloakOpenIdAsWiremockConfig {
@BeforeEach
public void init() {
mocks = MockitoAnnotations.openMocks(this);
preparedMocks = new Object[] {magistaClient};
preparedMocks = new Object[] {magistaClient, vortigonClient, orgMgmtClient, bouncerClient};
}
@AfterEach
@ -55,6 +67,9 @@ class SearchRefundsTest extends AbstractKeycloakOpenIdAsWiremockConfig {
@Test
@SneakyThrows
void searchRefundsRequiredParamsRequestSuccess() {
when(vortigonClient.getShopsIds(any(), any())).thenReturn(List.of("1", "2", "3"));
when(orgMgmtClient.getUserContext(any())).thenReturn(createContextFragment());
when(bouncerClient.judge(any(), any())).thenReturn(createJudgementAllowed());
when(magistaClient.searchRefunds(any())).thenReturn(MagistaUtil.createSearchRefundRequiredResponse());
mvc.perform(get("/refunds")
.header("Authorization", "Bearer " + generateInvoicesReadJwt())
@ -66,13 +81,19 @@ class SearchRefundsTest extends AbstractKeycloakOpenIdAsWiremockConfig {
.andDo(print())
.andExpect(status().is2xxSuccessful())
.andExpect(jsonPath("$").exists());
verify(vortigonClient, times(1)).getShopsIds(any(), any());
verify(orgMgmtClient, times(1)).getUserContext(any());
verify(bouncerClient, times(1)).judge(any(), any());
verify(magistaClient, times(1)).searchRefunds(any());
}
@Test
@SneakyThrows
void searchRefundsAllParamsRequestSuccess() {
when(magistaClient.searchRefunds(any())).thenReturn(MagistaUtil.createsearchRefundAllResponse());
when(vortigonClient.getShopsIds(any(), any())).thenReturn(List.of("1", "2", "3"));
when(orgMgmtClient.getUserContext(any())).thenReturn(createContextFragment());
when(bouncerClient.judge(any(), any())).thenReturn(createJudgementAllowed());
when(magistaClient.searchRefunds(any())).thenReturn(MagistaUtil.createSearchRefundAllResponse());
mvc.perform(get("/refunds")
.header("Authorization", "Bearer " + generateInvoicesReadJwt())
.header("X-Request-ID", randomUUID())
@ -83,6 +104,9 @@ class SearchRefundsTest extends AbstractKeycloakOpenIdAsWiremockConfig {
.andDo(print())
.andExpect(status().is2xxSuccessful())
.andExpect(jsonPath("$").exists());
verify(vortigonClient, times(1)).getShopsIds(any(), any());
verify(orgMgmtClient, times(1)).getUserContext(any());
verify(bouncerClient, times(1)).judge(any(), any());
verify(magistaClient, times(1)).searchRefunds(any());
}
@ -107,6 +131,9 @@ class SearchRefundsTest extends AbstractKeycloakOpenIdAsWiremockConfig {
@Test
@SneakyThrows
void searchRefundsRequestMagistaUnavailable() {
when(vortigonClient.getShopsIds(any(), any())).thenReturn(List.of("1", "2", "3"));
when(orgMgmtClient.getUserContext(any())).thenReturn(createContextFragment());
when(bouncerClient.judge(any(), any())).thenReturn(createJudgementAllowed());
when(magistaClient.searchRefunds(any())).thenThrow(TException.class);
mvc.perform(get("/refunds")
.header("Authorization", "Bearer " + generateInvoicesReadJwt())
@ -117,6 +144,9 @@ class SearchRefundsTest extends AbstractKeycloakOpenIdAsWiremockConfig {
.content(""))
.andDo(print())
.andExpect(status().is5xxServerError());
verify(vortigonClient, times(1)).getShopsIds(any(), any());
verify(orgMgmtClient, times(1)).getUserContext(any());
verify(bouncerClient, times(1)).judge(any(), any());
verify(magistaClient, times(1)).searchRefunds(any());
}
}

View File

@ -10,9 +10,6 @@ import java.security.PrivateKey;
import java.time.Instant;
import java.util.UUID;
/**
* @since 04.07.17
**/
public class JwtTokenBuilder {
public static final String DEFAULT_USERNAME = "Darth Vader";
@ -38,18 +35,6 @@ public class JwtTokenBuilder {
this.privateKey = privateKey;
}
public String getUserId() {
return userId;
}
public String getUsername() {
return username;
}
public String getEmail() {
return email;
}
public String generateJwtWithRoles(String issuer, String... roles) {
long iat = Instant.now().getEpochSecond();
long exp = iat + 60 * 10;

View File

@ -29,10 +29,6 @@ public abstract class AbstractKeycloakOpenIdAsWiremockConfig {
keycloakOpenIdStub.givenStub();
}
protected String generateJwt(long iat, long exp, String... roles) {
return keycloakOpenIdStub.generateJwt(iat, exp, roles);
}
protected String generateInvoicesReadJwt() {
return keycloakOpenIdStub.generateJwt("invoices:read");
}

View File

@ -1,12 +1,17 @@
package com.rbkmoney.anapi.v2.controller;
import com.rbkmoney.anapi.v2.config.AbstractKeycloakOpenIdAsWiremockConfig;
import com.rbkmoney.anapi.v2.converter.search.request.*;
import com.rbkmoney.anapi.v2.converter.search.request.ParamsToRefundSearchQueryConverter;
import com.rbkmoney.anapi.v2.exception.BadRequestException;
import com.rbkmoney.anapi.v2.service.SearchService;
import com.rbkmoney.anapi.v2.testutil.OpenApiUtil;
import com.rbkmoney.bouncer.decisions.ArbiterSrv;
import com.rbkmoney.damsel.vortigon.VortigonServiceSrv;
import com.rbkmoney.openapi.anapi_v2.model.DefaultLogicError;
import com.rbkmoney.orgmanagement.AuthContextProviderSrv;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockitoAnnotations;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
@ -15,7 +20,10 @@ import org.springframework.util.MultiValueMap;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.List;
import static com.rbkmoney.anapi.v2.testutil.MagistaUtil.createContextFragment;
import static com.rbkmoney.anapi.v2.testutil.MagistaUtil.createJudgementAllowed;
import static java.util.UUID.randomUUID;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
@ -28,19 +36,30 @@ class ErrorControllerTest extends AbstractKeycloakOpenIdAsWiremockConfig {
@Autowired
private MockMvc mockMvc;
@MockBean
private SearchService searchService;
@MockBean
private ParamsToPaymentSearchQueryConverter paymentSearchConverter;
@MockBean
private ParamsToChargebackSearchQueryConverter chargebackSearchConverter;
@MockBean
private ParamsToInvoiceSearchQueryConverter invoiceSearchConverter;
@MockBean
private ParamsToPayoutSearchQueryConverter payoutSearchConverter;
@MockBean
private ParamsToRefundSearchQueryConverter refundSearchConverter;
@MockBean
public VortigonServiceSrv.Iface vortigonClient;
@MockBean
public AuthContextProviderSrv.Iface orgMgmtClient;
@MockBean
public ArbiterSrv.Iface bouncerClient;
private AutoCloseable mocks;
private Object[] preparedMocks;
@BeforeEach
public void init() {
mocks = MockitoAnnotations.openMocks(this);
preparedMocks = new Object[] {refundSearchConverter, vortigonClient, orgMgmtClient, bouncerClient};
}
@AfterEach
public void clean() throws Exception {
verifyNoMoreInteractions(preparedMocks);
mocks.close();
}
@Test
void testConstraintViolationException() throws Exception {
@ -48,9 +67,9 @@ class ErrorControllerTest extends AbstractKeycloakOpenIdAsWiremockConfig {
params.set("limit", "1001");
mockMvc.perform(
get("/payments")
.header("Authorization", "Bearer " + generateInvoicesReadJwt())
.header("X-Request-ID", randomUUID())
get("/payments")
.header("Authorization", "Bearer " + generateInvoicesReadJwt())
.header("X-Request-ID", randomUUID())
.header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString())
.params(params)
.contentType(MediaType.APPLICATION_JSON_UTF8)
@ -63,31 +82,34 @@ class ErrorControllerTest extends AbstractKeycloakOpenIdAsWiremockConfig {
@Test
void testBadRequestException() throws Exception {
when(vortigonClient.getShopsIds(any(), any())).thenReturn(List.of("1", "2", "3"));
when(orgMgmtClient.getUserContext(any())).thenReturn(createContextFragment());
when(bouncerClient.judge(any(), any())).thenReturn(createJudgementAllowed());
String message = "Error!";
doThrow(new BadRequestException(message)).when(refundSearchConverter)
.convert(any(), any(), any(), any(),
any(), any(), any(), any(),
any(), any(), any(), any(),
any(), any(), any(), any());
MultiValueMap<String, String> params = OpenApiUtil.getSearchRequiredParams();
mockMvc.perform(
get("/refunds")
.header("Authorization", "Bearer " + generateInvoicesReadJwt())
.header("X-Request-ID", randomUUID())
.header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString())
.params(params)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(""))
get("/refunds")
.header("Authorization", "Bearer " + generateInvoicesReadJwt())
.header("X-Request-ID", randomUUID())
.header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString())
.params(params)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(""))
.andDo(print())
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.code").value(DefaultLogicError.CodeEnum.INVALIDREQUEST.getValue()))
.andExpect(jsonPath("$.message").value(message));
verify(vortigonClient, times(1)).getShopsIds(any(), any());
verify(orgMgmtClient, times(1)).getUserContext(any());
verify(bouncerClient, times(1)).judge(any(), any());
verify(refundSearchConverter, times(1))
.convert(any(), any(), any(), any(),
any(), any(), any(), any(),
any(), any(), any(), any(),
any(), any(), any(), any());
}
@ -130,28 +152,32 @@ class ErrorControllerTest extends AbstractKeycloakOpenIdAsWiremockConfig {
@Test
void testInternalException() throws Exception {
when(vortigonClient.getShopsIds(any(), any())).thenReturn(List.of("1", "2", "3"));
when(orgMgmtClient.getUserContext(any())).thenReturn(createContextFragment());
when(bouncerClient.judge(any(), any())).thenReturn(createJudgementAllowed());
doThrow(new RuntimeException()).when(refundSearchConverter)
.convert(any(), any(), any(), any(),
any(), any(), any(), any(),
any(), any(), any(), any(),
any(), any(), any(), any());
MultiValueMap<String, String> params = OpenApiUtil.getSearchRequiredParams();
mockMvc.perform(
get("/refunds")
.header("Authorization", "Bearer " + generateInvoicesReadJwt())
.header("X-Request-ID", randomUUID())
.header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString())
.params(params)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(""))
get("/refunds")
.header("Authorization", "Bearer " + generateInvoicesReadJwt())
.header("X-Request-ID", randomUUID())
.header("X-Request-Deadline", Instant.now().plus(1, ChronoUnit.DAYS).toString())
.params(params)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(""))
.andDo(print())
.andExpect(status().isInternalServerError())
.andExpect(jsonPath("$").doesNotExist());
verify(vortigonClient, times(1)).getShopsIds(any(), any());
verify(orgMgmtClient, times(1)).getUserContext(any());
verify(bouncerClient, times(1)).judge(any(), any());
verify(refundSearchConverter, times(1))
.convert(any(), any(), any(), any(),
any(), any(), any(), any(),
any(), any(), any(), any(),
any(), any(), any(), any());
}

View File

@ -0,0 +1,61 @@
package com.rbkmoney.anapi.v2.converter.search.request;
import com.rbkmoney.anapi.v2.exception.BadRequestException;
import com.rbkmoney.magista.ChargebackSearchQuery;
import org.junit.jupiter.api.Test;
import java.time.OffsetDateTime;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
class ParamsToChargebackSearchQueryConverterTest {
private static final ParamsToChargebackSearchQueryConverter converter =
new ParamsToChargebackSearchQueryConverter();
@Test
void convert() {
ChargebackSearchQuery query = converter.convert("1",
OffsetDateTime.MIN,
OffsetDateTime.MAX,
10,
List.of("1", "2"),
"1",
"2",
"3",
List.of("pending", "accepted"),
List.of("pre_arbitration"),
List.of("fraud"),
"test"
);
assertNotNull(query);
}
@Test
void mapStage() {
assertTrue(converter.mapStage("chargeback").isSetChargeback());
assertTrue(converter.mapStage("pre_arbitration").isSetPreArbitration());
assertTrue(converter.mapStage("arbitration").isSetArbitration());
assertThrows(BadRequestException.class, () -> converter.mapStage("unexpected"));
}
@Test
void mapStatus() {
assertTrue(converter.mapStatus("pending").isSetPending());
assertTrue(converter.mapStatus("accepted").isSetAccepted());
assertTrue(converter.mapStatus("rejected").isSetRejected());
assertTrue(converter.mapStatus("cancelled").isSetCancelled());
assertThrows(BadRequestException.class, () -> converter.mapStatus("unexpected"));
}
@Test
void mapCategory() {
assertTrue(converter.mapCategory("fraud").isSetFraud());
assertTrue(converter.mapCategory("dispute").isSetDispute());
assertTrue(converter.mapCategory("authorisation").isSetAuthorisation());
assertTrue(converter.mapCategory("processing_error").isSetProcessingError());
assertThrows(BadRequestException.class, () -> converter.mapCategory("unexpected"));
}
}

View File

@ -0,0 +1,52 @@
package com.rbkmoney.anapi.v2.converter.search.request;
import com.rbkmoney.anapi.v2.exception.BadRequestException;
import com.rbkmoney.magista.InvoiceSearchQuery;
import com.rbkmoney.magista.InvoiceStatus;
import com.rbkmoney.magista.PaymentParams;
import org.junit.jupiter.api.Test;
import java.time.OffsetDateTime;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
class ParamsToInvoiceSearchQueryConverterTest {
private static final ParamsToInvoiceSearchQueryConverter converter =
new ParamsToInvoiceSearchQueryConverter();
@Test
void convert() {
InvoiceSearchQuery query = converter.convert("1",
OffsetDateTime.MIN,
OffsetDateTime.MAX,
10,
List.of("1", "2"),
List.of("1", "2"),
"paid",
"1",
"2",
0L,
1000L,
"test");
assertNotNull(query);
}
@Test
void mapPaymentParams() {
Long amountFrom = 0L;
Long amountTo = 1000L;
PaymentParams params = converter.mapPaymentParams(amountFrom, amountTo);
assertEquals(amountFrom, params.getPaymentAmountFrom());
assertEquals(amountTo, params.getPaymentAmountTo());
}
@Test
void mapStatus() {
for (InvoiceStatus status : InvoiceStatus.values()) {
assertEquals(status, converter.mapStatus(status.name()));
}
assertThrows(BadRequestException.class, () -> converter.mapStatus("unexpected"));
}
}

View File

@ -0,0 +1,100 @@
package com.rbkmoney.anapi.v2.converter.search.request;
import com.rbkmoney.anapi.v2.exception.BadRequestException;
import com.rbkmoney.damsel.domain.LegacyBankCardPaymentSystem;
import com.rbkmoney.damsel.domain.LegacyBankCardTokenProvider;
import com.rbkmoney.damsel.domain.LegacyTerminalPaymentProvider;
import com.rbkmoney.magista.InvoicePaymentFlowType;
import com.rbkmoney.magista.PaymentSearchQuery;
import com.rbkmoney.magista.PaymentToolType;
import com.rbkmoney.openapi.anapi_v2.model.PaymentStatus;
import org.junit.jupiter.api.Test;
import java.time.OffsetDateTime;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
class ParamsToPaymentSearchQueryConverterTest {
private static final ParamsToPaymentSearchQueryConverter converter = new ParamsToPaymentSearchQueryConverter();
@Test
void convert() {
PaymentSearchQuery query = converter.convert("1",
OffsetDateTime.MIN,
OffsetDateTime.MAX,
10,
List.of("1", "2", "3"),
List.of("1", "2", "3"),
"cancelled",
"hold",
"paymentTerminal",
"euroset",
"1",
"1",
"1",
"mail@mail.com",
"127.0.0.1",
"fingerprint",
"1",
"123456",
"7890",
"012345678",
"123456",
"applepay",
"mastercard",
0L,
1000L,
List.of("1", "2", "3"),
"test");
assertNotNull(query);
}
@Test
void mapPaymentTool() {
assertEquals(PaymentToolType.bank_card, converter.mapPaymentTool("bankCard"));
assertEquals(PaymentToolType.payment_terminal, converter.mapPaymentTool("paymentTerminal"));
assertThrows(BadRequestException.class, () -> converter.mapPaymentTool("unexpected"));
}
@Test
void mapInvoicePaymentFlow() {
assertEquals(InvoicePaymentFlowType.instant, converter.mapInvoicePaymentFlow("instant"));
assertEquals(InvoicePaymentFlowType.hold, converter.mapInvoicePaymentFlow("hold"));
assertThrows(BadRequestException.class, () -> converter.mapInvoicePaymentFlow("unexpected"));
}
@Test
void mapStatus() {
for (PaymentStatus.StatusEnum status : PaymentStatus.StatusEnum.values()) {
assertNotNull(converter.mapStatus(status.getValue()));
}
assertThrows(BadRequestException.class, () -> converter.mapStatus("unexpected"));
}
@Test
void mapTerminalProvider() {
for (LegacyTerminalPaymentProvider provider : LegacyTerminalPaymentProvider.values()) {
assertEquals(provider, converter.mapTerminalProvider(provider.name()));
}
assertThrows(BadRequestException.class, () -> converter.mapTerminalProvider("unexpected"));
}
@Test
void mapTokenProvider() {
for (LegacyBankCardTokenProvider provider : LegacyBankCardTokenProvider.values()) {
assertEquals(provider, converter.mapTokenProvider(provider.name()));
}
assertThrows(BadRequestException.class, () -> converter.mapTokenProvider("unexpected"));
}
@Test
void mapPaymentSystem() {
for (LegacyBankCardPaymentSystem system : LegacyBankCardPaymentSystem.values()) {
assertEquals(system, converter.mapPaymentSystem(system.name()));
}
assertThrows(BadRequestException.class, () -> converter.mapPaymentSystem("unexpected"));
}
}

View File

@ -0,0 +1,38 @@
package com.rbkmoney.anapi.v2.converter.search.request;
import com.rbkmoney.anapi.v2.exception.BadRequestException;
import com.rbkmoney.magista.PayoutSearchQuery;
import com.rbkmoney.magista.PayoutToolType;
import org.junit.jupiter.api.Test;
import java.time.OffsetDateTime;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
class ParamsToPayoutSearchQueryConverterTest {
private static final ParamsToPayoutSearchQueryConverter converter = new ParamsToPayoutSearchQueryConverter();
@Test
void convert() {
PayoutSearchQuery query = converter.convert("1",
OffsetDateTime.MIN,
OffsetDateTime.MAX,
10,
List.of("1", "2", "3"),
"1",
"Wallet",
"test");
assertNotNull(query);
}
@Test
void mapPayoutToolType() {
assertEquals(PayoutToolType.payout_account, converter.mapPayoutToolType("PayoutAccount"));
assertEquals(PayoutToolType.wallet, converter.mapPayoutToolType("Wallet"));
assertEquals(PayoutToolType.payment_institution_account,
converter.mapPayoutToolType("PaymentInstitutionAccount"));
assertThrows(BadRequestException.class, () -> converter.mapPayoutToolType("unexpected"));
}
}

View File

@ -0,0 +1,41 @@
package com.rbkmoney.anapi.v2.converter.search.request;
import com.rbkmoney.anapi.v2.exception.BadRequestException;
import com.rbkmoney.magista.InvoicePaymentRefundStatus;
import com.rbkmoney.magista.RefundSearchQuery;
import org.junit.jupiter.api.Test;
import java.time.OffsetDateTime;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
class ParamsToRefundSearchQueryConverterTest {
private static final ParamsToRefundSearchQueryConverter converter = new ParamsToRefundSearchQueryConverter();
@Test
void convert() {
RefundSearchQuery query = converter.convert("1",
OffsetDateTime.MIN,
OffsetDateTime.MAX,
10,
List.of("1", "2", "3"),
List.of("1", "2", "3"),
"1",
"1",
"1",
"1",
"pending",
"test");
assertNotNull(query);
}
@Test
void mapRefundStatus() {
assertEquals(InvoicePaymentRefundStatus.succeeded, converter.mapStatus("succeeded"));
assertEquals(InvoicePaymentRefundStatus.failed, converter.mapStatus("failed"));
assertEquals(InvoicePaymentRefundStatus.pending, converter.mapStatus("pending"));
assertThrows(BadRequestException.class, () -> converter.mapStatus("unexpected"));
}
}

View File

@ -0,0 +1,56 @@
package com.rbkmoney.anapi.v2.converter.search.response;
import com.rbkmoney.damsel.base.Content;
import com.rbkmoney.damsel.domain.*;
import com.rbkmoney.magista.StatChargeback;
import com.rbkmoney.magista.StatChargebackResponse;
import com.rbkmoney.openapi.anapi_v2.model.Chargeback;
import org.junit.jupiter.api.Test;
import static com.rbkmoney.anapi.v2.testutil.MagistaUtil.createSearchChargebackAllResponse;
import static com.rbkmoney.anapi.v2.testutil.RandomUtil.randomBytes;
import static com.rbkmoney.anapi.v2.testutil.RandomUtil.randomString;
import static com.rbkmoney.openapi.anapi_v2.model.ChargebackCategory.*;
import static org.junit.jupiter.api.Assertions.*;
class StatChargebackToChargebackConverterTest {
private static final StatChargebackToChargebackConverter converter = new StatChargebackToChargebackConverter();
@Test
void convert() {
StatChargebackResponse magistaResponse = createSearchChargebackAllResponse();
StatChargeback magistaChargeback = magistaResponse.getChargebacks().get(0);
magistaChargeback.setContent(new Content()
.setType(randomString(10))
.setData(randomBytes(10)));
Chargeback result = converter.convert(magistaChargeback);
assertAll(
() -> assertEquals(magistaChargeback.getAmount(), result.getBodyAmount()),
() -> assertEquals(magistaChargeback.getCreatedAt(), result.getCreatedAt().toString()),
() -> assertEquals(magistaChargeback.getChargebackId(), result.getChargebackId()),
() -> assertEquals(magistaChargeback.getChargebackReason().getCode(),
result.getChargebackReason().getCode()),
() -> assertArrayEquals(magistaChargeback.getContent().getData(), result.getContent().getData()),
() -> assertEquals(magistaChargeback.getContent().getType(), result.getContent().getType()),
() -> assertEquals(magistaChargeback.getCurrencyCode().getSymbolicCode(), result.getBodyCurrency())
);
}
@Test
void mapCategory() {
assertAll(
() -> assertEquals(AUTHORISATION, converter.mapCategory(InvoicePaymentChargebackCategory.authorisation(
new InvoicePaymentChargebackCategoryAuthorisation()))),
() -> assertEquals(DISPUTE, converter.mapCategory(
InvoicePaymentChargebackCategory.dispute(new InvoicePaymentChargebackCategoryDispute()))),
() -> assertEquals(FRAUD, converter.mapCategory(
InvoicePaymentChargebackCategory.fraud(new InvoicePaymentChargebackCategoryFraud()))),
() -> assertEquals(PROCESSING_ERROR, converter.mapCategory(
InvoicePaymentChargebackCategory.processing_error(
new InvoicePaymentChargebackCategoryProcessingError()))),
() -> assertThrows(IllegalArgumentException.class,
() -> converter.mapCategory(new InvoicePaymentChargebackCategory()))
);
}
}

View File

@ -0,0 +1,91 @@
package com.rbkmoney.anapi.v2.converter.search.response;
import com.rbkmoney.damsel.domain.*;
import com.rbkmoney.damsel.msgpack.Value;
import com.rbkmoney.magista.StatInvoice;
import com.rbkmoney.magista.StatInvoiceResponse;
import com.rbkmoney.openapi.anapi_v2.model.Invoice;
import com.rbkmoney.openapi.anapi_v2.model.InvoiceLineTaxVAT;
import org.junit.jupiter.api.Test;
import java.util.HashMap;
import java.util.Map;
import static com.rbkmoney.anapi.v2.testutil.MagistaUtil.createSearchInvoiceAllResponse;
import static com.rbkmoney.anapi.v2.testutil.RandomUtil.randomInt;
import static com.rbkmoney.anapi.v2.testutil.RandomUtil.randomString;
import static com.rbkmoney.openapi.anapi_v2.model.Invoice.StatusEnum.*;
import static org.junit.jupiter.api.Assertions.*;
class StatInvoiceToInvoiceConverterTest {
private static final StatInvoiceToInvoiceConverter converter = new StatInvoiceToInvoiceConverter();
@Test
void convert() {
StatInvoiceResponse magistaResponse = createSearchInvoiceAllResponse();
StatInvoice magistaInvoice = magistaResponse.getInvoices().get(0);
magistaInvoice.getCart().getLines().get(0).setMetadata(Map.of("TaxMode", Value.str("10%")))
.setQuantity(randomInt(1, 10000))
.setPrice(new Cash()
.setAmount(randomInt(0, 1000000)));
Invoice result = converter.convert(magistaInvoice);
var expectedLine = magistaInvoice.getCart().getLines().get(0);
var actualLine = result.getCart().get(0);
assertAll(
() -> assertEquals(magistaInvoice.getAmount(), result.getAmount()),
() -> assertEquals(magistaInvoice.getCreatedAt(), result.getCreatedAt().toString()),
() -> assertEquals(magistaInvoice.getCurrencySymbolicCode(), result.getCurrency()),
() -> assertEquals(magistaInvoice.getExternalId(), result.getExternalID()),
() -> assertEquals(expectedLine.getPrice().getAmount(), actualLine.getPrice()),
() -> assertEquals(expectedLine.getProduct(), actualLine.getProduct()),
() -> assertEquals(expectedLine.getQuantity() * expectedLine.getPrice().getAmount(),
actualLine.getCost()),
() -> assertEquals(expectedLine.getMetadata().get("TaxMode").getStr(),
((InvoiceLineTaxVAT) actualLine.getTaxMode()).getRate().getValue()),
() -> assertEquals(magistaInvoice.getDescription(), result.getDescription()),
() -> assertEquals(magistaInvoice.getDue(), result.getDueDate().toString()),
() -> assertEquals(magistaInvoice.getId(), result.getId()),
() -> assertEquals(magistaInvoice.getProduct(), result.getProduct()),
() -> assertEquals(magistaInvoice.getShopId(), result.getShopID())
);
}
@Test
void mapStatusInfo() {
Invoice invoice = new Invoice();
InvoiceStatus status = InvoiceStatus.fulfilled(new InvoiceFulfilled()
.setDetails(randomString(10)));
converter.mapStatusInfo(invoice, status);
assertEquals(FULFILLED, invoice.getStatus());
assertEquals(status.getFulfilled().getDetails(), invoice.getReason());
invoice = new Invoice();
status = InvoiceStatus.paid(new InvoicePaid());
converter.mapStatusInfo(invoice, status);
assertEquals(PAID, invoice.getStatus());
invoice = new Invoice();
status = InvoiceStatus.unpaid(new InvoiceUnpaid());
converter.mapStatusInfo(invoice, status);
assertEquals(UNPAID, invoice.getStatus());
invoice = new Invoice();
status = InvoiceStatus.cancelled(new InvoiceCancelled()
.setDetails(randomString(10)));
converter.mapStatusInfo(invoice, status);
assertEquals(CANCELLED, invoice.getStatus());
assertEquals(status.getCancelled().getDetails(), invoice.getReason());
assertThrows(IllegalArgumentException.class, () -> converter.mapStatusInfo(new Invoice(), new InvoiceStatus()));
}
@Test
void mapTaxMode() {
assertNull(converter.mapTaxMode(Map.of()));
String taxMode = "10%";
Map<String, Value> metadata = new HashMap<>();
metadata.put("TaxMode", Value.str(taxMode));
assertEquals(taxMode, ((InvoiceLineTaxVAT)converter.mapTaxMode(metadata)).getRate().getValue());
}
}

View File

@ -0,0 +1,73 @@
package com.rbkmoney.anapi.v2.converter.search.response;
import com.rbkmoney.damsel.domain.AdditionalTransactionInfo;
import com.rbkmoney.damsel.domain.PaymentResourcePayer;
import com.rbkmoney.damsel.domain.RecurrentPayer;
import com.rbkmoney.geck.common.util.TypeUtil;
import com.rbkmoney.magista.*;
import com.rbkmoney.openapi.anapi_v2.model.PaymentFlow;
import com.rbkmoney.openapi.anapi_v2.model.PaymentSearchResult;
import org.junit.jupiter.api.Test;
import java.time.OffsetDateTime;
import static com.rbkmoney.anapi.v2.testutil.MagistaUtil.createSearchPaymentAllResponse;
import static com.rbkmoney.anapi.v2.testutil.RandomUtil.randomString;
import static com.rbkmoney.openapi.anapi_v2.model.Payer.PayerTypeEnum.*;
import static org.junit.jupiter.api.Assertions.*;
class StatPaymentToPaymentSearchResultConverterTest {
private static final StatPaymentToPaymentSearchResultConverter converter =
new StatPaymentToPaymentSearchResultConverter();
@Test
void convert() {
StatPaymentResponse magistaResponse = createSearchPaymentAllResponse();
StatPayment magistaPayment = magistaResponse.getPayments().get(0);
magistaPayment.setFlow(InvoicePaymentFlow.hold(new InvoicePaymentFlowHold()));
magistaPayment.setStatusChangedAt(TypeUtil.temporalToString(OffsetDateTime.now().toLocalDateTime()));
magistaPayment.setAdditionalTransactionInfo(new AdditionalTransactionInfo()
.setRrn(randomString(10))
.setApprovalCode(randomString(10)));
PaymentSearchResult result = converter.convert(magistaPayment);
assertAll(
() -> assertEquals(magistaPayment.getAmount(), result.getAmount()),
() -> assertEquals(magistaPayment.getCreatedAt(), result.getCreatedAt().toString()),
() -> assertEquals(magistaPayment.getCurrencySymbolicCode(), result.getCurrency()),
() -> assertEquals(magistaPayment.getExternalId(), result.getExternalID()),
() -> assertEquals(magistaPayment.getFee(), result.getFee()),
() -> assertEquals(PaymentFlow.TypeEnum.PAYMENTFLOWHOLD, result.getFlow().getType()),
() -> assertEquals(magistaPayment.getLocationInfo().getCityGeoId(),
result.getGeoLocationInfo().getCityGeoID()),
() -> assertEquals(magistaPayment.getLocationInfo().getCountryGeoId(),
result.getGeoLocationInfo().getCountryGeoID()),
() -> assertEquals(magistaPayment.getStatusChangedAt(), result.getStatusChangedAt().toString()),
() -> assertEquals(magistaPayment.getId(), result.getId()),
() -> assertEquals(magistaPayment.getInvoiceId(), result.getInvoiceID()),
() -> assertEquals(magistaPayment.isMakeRecurrent(), result.getMakeRecurrent()),
() -> assertEquals(magistaPayment.getShopId(), result.getShopID()),
() -> assertEquals(magistaPayment.getShortId(), result.getShortID()),
() -> assertEquals(magistaPayment.getAdditionalTransactionInfo().getApprovalCode(),
result.getTransactionInfo().getApprovalCode()),
() -> assertEquals(magistaPayment.getAdditionalTransactionInfo().getRrn(),
result.getTransactionInfo().getRrn())
);
}
@Test
void mapPayer() {
assertEquals(CUSTOMERPAYER, converter.mapPayer(Payer.customer(new CustomerPayer())).getPayerType());
assertEquals(PAYMENTRESOURCEPAYER,
converter.mapPayer(Payer.payment_resource(new PaymentResourcePayer())).getPayerType());
assertEquals(RECURRENTPAYER, converter.mapPayer(Payer.recurrent(new RecurrentPayer())).getPayerType());
assertThrows(IllegalArgumentException.class, () -> converter.mapPayer(new Payer()));
}
@Test
void mapStatus() {
for (InvoicePaymentStatus status : InvoicePaymentStatus.values()) {
assertNotNull(converter.mapStatus(status));
}
}
}

View File

@ -0,0 +1,190 @@
package com.rbkmoney.anapi.v2.converter.search.response;
import com.rbkmoney.damsel.domain.InternationalBankAccount;
import com.rbkmoney.damsel.domain.InternationalBankDetails;
import com.rbkmoney.damsel.domain.*;
import com.rbkmoney.magista.*;
import com.rbkmoney.openapi.anapi_v2.model.*;
import org.junit.jupiter.api.Test;
import static com.rbkmoney.anapi.v2.testutil.MagistaUtil.createSearchPayoutAllResponse;
import static com.rbkmoney.anapi.v2.testutil.RandomUtil.randomString;
import static org.junit.jupiter.api.Assertions.*;
class StatPayoutToPayoutConverterTest {
private static final StatPayoutToPayoutConverter converter = new StatPayoutToPayoutConverter();
@Test
void convert() {
StatPayoutResponse magistaResponse = createSearchPayoutAllResponse();
StatPayout magistaPayout = magistaResponse.getPayouts().get(0);
Payout result = converter.convert(magistaPayout);
assertAll(
() -> assertEquals(magistaPayout.getAmount(), result.getAmount()),
() -> assertEquals(magistaPayout.getCreatedAt(), result.getCreatedAt().toString()),
() -> assertEquals(magistaPayout.getCurrencySymbolicCode(), result.getCurrency()),
() -> assertEquals(magistaPayout.getFee(), result.getFee()),
() -> assertEquals(magistaPayout.getId(), result.getId()),
() -> assertEquals(magistaPayout.getShopId(), result.getShopID())
);
}
@Test
void mapPayoutStatus() {
assertAll(
() -> assertEquals("Cancelled", converter.mapStatus(PayoutStatus.cancelled(new PayoutCancelled()))),
() -> assertEquals("Paid", converter.mapStatus(PayoutStatus.paid(new PayoutPaid()))),
() -> assertEquals("Confirmed", converter.mapStatus(PayoutStatus.confirmed(new PayoutConfirmed()))),
() -> assertEquals("Unpaid", converter.mapStatus(PayoutStatus.unpaid(new PayoutUnpaid()))),
() -> assertThrows(IllegalArgumentException.class, () -> converter.mapStatus(new PayoutStatus()))
);
}
@Test
void mapPayoutToolDetails() {
//RussianBankAccount
PayoutToolInfo toolInfo = new PayoutToolInfo();
toolInfo.setRussianBankAccount(new RussianBankAccount()
.setAccount(randomString(10))
.setBankBik(randomString(10))
.setBankName(randomString(10))
.setBankPostAccount(randomString(10)));
PayoutToolDetailsBankAccount actualRussianBankAccount =
(PayoutToolDetailsBankAccount) converter.mapPayoutToolDetails(toolInfo);
RussianBankAccount expectedRussianBankAccount = toolInfo.getRussianBankAccount();
assertAll(
() -> assertEquals(expectedRussianBankAccount.getAccount(), actualRussianBankAccount.getAccount()),
() -> assertEquals(expectedRussianBankAccount.getBankBik(), actualRussianBankAccount.getBankBik()),
() -> assertEquals(expectedRussianBankAccount.getBankName(), actualRussianBankAccount.getBankName()),
() -> assertEquals(expectedRussianBankAccount.getBankPostAccount(),
actualRussianBankAccount.getBankPostAccount())
);
//WalletInfo
toolInfo = new PayoutToolInfo();
toolInfo.setWalletInfo(new WalletInfo()
.setWalletId(randomString(10)));
PayoutToolDetailsWalletInfo walletActual =
(PayoutToolDetailsWalletInfo) converter.mapPayoutToolDetails(toolInfo);
WalletInfo walletExpected = toolInfo.getWalletInfo();
assertEquals(walletExpected.getWalletId(), walletActual.getWalletID());
//PaymentInstitutionAccount
toolInfo = new PayoutToolInfo();
toolInfo.setPaymentInstitutionAccount(new PaymentInstitutionAccount());
PayoutToolDetailsPaymentInstitutionAccount actualPaymentInstitutionAccount =
(PayoutToolDetailsPaymentInstitutionAccount) converter.mapPayoutToolDetails(toolInfo);
PaymentInstitutionAccount expectedPaymentInstitutionAccount = toolInfo.getPaymentInstitutionAccount();
assertNotNull(expectedPaymentInstitutionAccount);
//InternationalBankAccount
toolInfo = new PayoutToolInfo();
toolInfo.setInternationalBankAccount(new InternationalBankAccount()
.setAccountHolder(randomString(10))
.setIban(randomString(10))
.setNumber(randomString(10))
.setBank(new InternationalBankDetails()
.setName(randomString(10))
.setAbaRtn(randomString(10))
.setAddress(randomString(10))
.setBic(randomString(10))
.setCountry(CountryCode.ABW)));
PayoutToolDetailsInternationalBankAccount actualInternationalBankAccount =
(PayoutToolDetailsInternationalBankAccount) converter.mapPayoutToolDetails(toolInfo);
InternationalBankAccount expectedInternationalBankAccount = toolInfo.getInternationalBankAccount();
assertAll(
() -> assertEquals(expectedInternationalBankAccount.getIban(),
actualInternationalBankAccount.getIban()),
() -> assertEquals(expectedInternationalBankAccount.getNumber(),
actualInternationalBankAccount.getNumber()),
() -> assertEquals(expectedInternationalBankAccount.getBank().getAbaRtn(),
actualInternationalBankAccount.getBankDetails().getAbartn()),
() -> assertEquals(expectedInternationalBankAccount.getBank().getAddress(),
actualInternationalBankAccount.getBankDetails().getAddress()),
() -> assertEquals(expectedInternationalBankAccount.getBank().getBic(),
actualInternationalBankAccount.getBankDetails().getBic()),
() -> assertEquals(expectedInternationalBankAccount.getBank().getName(),
actualInternationalBankAccount.getBankDetails().getName()),
() -> assertEquals(expectedInternationalBankAccount.getBank().getCountry().name(),
actualInternationalBankAccount.getBankDetails().getCountryCode()),
//tested via mapInternationalCorrespondentBankAccount test
() -> assertNull(actualInternationalBankAccount.getCorrespondentBankAccount())
);
//Some missing type
assertThrows(IllegalArgumentException.class, () -> converter.mapPayoutToolDetails(new PayoutToolInfo()));
}
@Test
void mapCountryCode() {
CountryCode countryCode = CountryCode.ABH;
assertEquals("ABH", converter.mapCountryCode(countryCode));
assertNull(converter.mapCountryCode(null));
}
@Test
void mapInternationalCorrespondentBankAccount() {
InternationalBankAccount expected = new InternationalBankAccount()
.setAccountHolder(randomString(10))
.setIban(randomString(10))
.setNumber(randomString(10))
.setBank(new InternationalBankDetails()
.setName(randomString(10))
.setAbaRtn(randomString(10))
.setAddress(randomString(10))
.setBic(randomString(10))
.setCountry(CountryCode.ABW))
.setCorrespondentAccount(new InternationalBankAccount()
.setAccountHolder(randomString(5))
.setIban(randomString(5))
.setNumber(randomString(5))
.setBank(new InternationalBankDetails()
.setName(randomString(5))
.setAbaRtn(randomString(5))
.setAddress(randomString(5))
.setBic(randomString(5))
.setCountry(CountryCode.RUS)));
InternationalCorrespondentBankAccount actual = converter.mapInternationalCorrespondentBankAccount(expected);
assertAll(
() -> assertEquals(expected.getIban(),
actual.getIban()),
() -> assertEquals(expected.getNumber(),
actual.getNumber()),
() -> assertEquals(expected.getBank().getAbaRtn(),
actual.getBankDetails().getAbartn()),
() -> assertEquals(expected.getBank().getAddress(),
actual.getBankDetails().getAddress()),
() -> assertEquals(expected.getBank().getBic(),
actual.getBankDetails().getBic()),
() -> assertEquals(expected.getBank().getName(),
actual.getBankDetails().getName()),
() -> assertEquals(expected.getBank().getCountry().name(),
actual.getBankDetails().getCountryCode()),
() -> assertEquals(expected.getCorrespondentAccount().getIban(),
actual.getCorrespondentBankAccount().getIban()),
() -> assertEquals(expected.getCorrespondentAccount().getNumber(),
actual.getCorrespondentBankAccount().getNumber()),
() -> assertEquals(expected.getCorrespondentAccount().getBank().getAbaRtn(),
actual.getCorrespondentBankAccount().getBankDetails().getAbartn()),
() -> assertEquals(expected.getCorrespondentAccount().getBank().getAddress(),
actual.getCorrespondentBankAccount().getBankDetails().getAddress()),
() -> assertEquals(expected.getCorrespondentAccount().getBank().getBic(),
actual.getCorrespondentBankAccount().getBankDetails().getBic()),
() -> assertEquals(expected.getCorrespondentAccount().getBank().getName(),
actual.getCorrespondentBankAccount().getBankDetails().getName()),
() -> assertEquals(expected.getCorrespondentAccount().getBank().getCountry().name(),
actual.getCorrespondentBankAccount().getBankDetails().getCountryCode()),
() -> assertNull(actual.getCorrespondentBankAccount().getCorrespondentBankAccount())
);
}
}

View File

@ -0,0 +1,63 @@
package com.rbkmoney.anapi.v2.converter.search.response;
import com.rbkmoney.damsel.domain.*;
import com.rbkmoney.magista.StatRefund;
import com.rbkmoney.magista.StatRefundResponse;
import com.rbkmoney.openapi.anapi_v2.model.RefundSearchResult;
import com.rbkmoney.openapi.anapi_v2.model.RefundStatusError;
import org.junit.jupiter.api.Test;
import static com.rbkmoney.anapi.v2.testutil.MagistaUtil.createSearchRefundAllResponse;
import static com.rbkmoney.anapi.v2.testutil.RandomUtil.randomString;
import static com.rbkmoney.openapi.anapi_v2.model.RefundSearchResult.StatusEnum.*;
import static org.junit.jupiter.api.Assertions.*;
class StatRefundToRefundSearchResultConverterTest {
private static final StatRefundToRefundSearchResultConverter converter =
new StatRefundToRefundSearchResultConverter();
@Test
void convert() {
StatRefundResponse magistaResponse = createSearchRefundAllResponse();
StatRefund magistaRefund = magistaResponse.getRefunds().get(0);
RefundSearchResult result = converter.convert(magistaRefund);
assertAll(
() -> assertEquals(magistaRefund.getAmount(), result.getAmount()),
() -> assertEquals(magistaRefund.getCreatedAt(), result.getCreatedAt().toString()),
() -> assertEquals(magistaRefund.getCurrencySymbolicCode(), result.getCurrency()),
() -> assertEquals(magistaRefund.getId(), result.getId()),
() -> assertEquals(magistaRefund.getShopId(), result.getShopID()),
() -> assertEquals(magistaRefund.getExternalId(), result.getExternalID()),
() -> assertEquals(magistaRefund.getInvoiceId(), result.getInvoiceID()),
() -> assertEquals(magistaRefund.getPaymentId(), result.getPaymentID()),
() -> assertEquals(magistaRefund.getReason(), result.getReason())
);
}
@Test
void mapStatusError() {
assertNull(converter.mapStatusError(new InvoicePaymentRefundStatus()));
String reason = randomString(10);
String code = randomString(10);
InvoicePaymentRefundStatus status = InvoicePaymentRefundStatus
.failed(new InvoicePaymentRefundFailed()
.setFailure(OperationFailure.failure(new Failure()
.setReason(reason)
.setCode(code))));
RefundStatusError statusError = converter.mapStatusError(status);
assertEquals(reason, statusError.getMessage());
assertEquals(code, statusError.getCode());
}
@Test
void mapStatus() {
assertEquals(PENDING,
converter.mapStatus(InvoicePaymentRefundStatus.pending(new InvoicePaymentRefundPending())));
assertEquals(FAILED,
converter.mapStatus(InvoicePaymentRefundStatus.failed(new InvoicePaymentRefundFailed())));
assertEquals(SUCCEEDED,
converter.mapStatus(InvoicePaymentRefundStatus.succeeded(new InvoicePaymentRefundSucceeded())));
assertThrows(IllegalArgumentException.class, () -> converter.mapStatus(new InvoicePaymentRefundStatus()));
}
}

View File

@ -1,5 +1,9 @@
package com.rbkmoney.anapi.v2.testutil;
import com.rbkmoney.bouncer.ctx.ContextFragment;
import com.rbkmoney.bouncer.decisions.Judgement;
import com.rbkmoney.bouncer.decisions.Resolution;
import com.rbkmoney.bouncer.decisions.ResolutionAllowed;
import com.rbkmoney.damsel.domain.InvoicePaymentRefundStatus;
import com.rbkmoney.damsel.domain.InvoiceStatus;
import com.rbkmoney.damsel.domain.*;
@ -24,8 +28,7 @@ import java.util.Map;
public class MagistaUtil {
private static final MockTBaseProcessor mockRequiredTBaseProcessor;
private static final MockTBaseProcessor mockFullTBaseProcessor;
static {
mockRequiredTBaseProcessor = new MockTBaseProcessor(MockMode.REQUIRED_ONLY, 15, 1);
Map.Entry<FieldHandler, String[]> timeFields = Map.entry(
@ -33,9 +36,6 @@ public class MagistaUtil {
new String[] {"created_at", "at", "due", "status_changed_at"}
);
mockRequiredTBaseProcessor.addFieldHandler(timeFields.getKey(), timeFields.getValue());
mockFullTBaseProcessor = new MockTBaseProcessor(MockMode.ALL, 15, 1);
mockFullTBaseProcessor.addFieldHandler(timeFields.getKey(), timeFields.getValue());
}
public static StatPaymentResponse createSearchPaymentRequiredResponse() {
@ -47,12 +47,12 @@ public class MagistaUtil {
}
public static StatPaymentResponse createSearchPaymentAllResponse() {
var payment = fillAllTBaseObject(new StatPayment(), StatPayment.class);
var cart = fillAllTBaseObject(new InvoiceCart(), InvoiceCart.class);
var line = fillAllTBaseObject(new InvoiceLine(), InvoiceLine.class);
var instant = fillAllTBaseObject(new InvoicePaymentFlowInstant(), InvoicePaymentFlowInstant.class);
var locationInfo = fillAllTBaseObject(new LocationInfo(), LocationInfo.class);
var response = fillAllTBaseObject(new StatPaymentResponse(), StatPaymentResponse.class);
var payment = fillRequiredTBaseObject(new StatPayment(), StatPayment.class);
var cart = fillRequiredTBaseObject(new InvoiceCart(), InvoiceCart.class);
var line = fillRequiredTBaseObject(new InvoiceLine(), InvoiceLine.class);
var instant = fillRequiredTBaseObject(new InvoicePaymentFlowInstant(), InvoicePaymentFlowInstant.class);
var locationInfo = fillRequiredTBaseObject(new LocationInfo(), LocationInfo.class);
var response = fillRequiredTBaseObject(new StatPaymentResponse(), StatPaymentResponse.class);
return response.setPayments(
List.of(payment
@ -65,11 +65,13 @@ public class MagistaUtil {
}
public static StatChargebackResponse createSearchChargebackAllResponse() {
var chargeback = fillAllTBaseObject(new StatChargeback(), StatChargeback.class);
var stage = fillAllTBaseObject(new InvoicePaymentChargebackStage(), InvoicePaymentChargebackStage.class);
var reason = fillAllTBaseObject(new InvoicePaymentChargebackReason(), InvoicePaymentChargebackReason.class);
var status = fillAllTBaseObject(new InvoicePaymentChargebackStatus(), InvoicePaymentChargebackStatus.class);
var response = fillAllTBaseObject(new StatChargebackResponse(), StatChargebackResponse.class);
var chargeback = fillRequiredTBaseObject(new StatChargeback(), StatChargeback.class);
var stage = fillRequiredTBaseObject(new InvoicePaymentChargebackStage(), InvoicePaymentChargebackStage.class);
var reason =
fillRequiredTBaseObject(new InvoicePaymentChargebackReason(), InvoicePaymentChargebackReason.class);
var status =
fillRequiredTBaseObject(new InvoicePaymentChargebackStatus(), InvoicePaymentChargebackStatus.class);
var response = fillRequiredTBaseObject(new StatChargebackResponse(), StatChargebackResponse.class);
return response.setChargebacks(
List.of(chargeback
@ -83,13 +85,13 @@ public class MagistaUtil {
return fillRequiredTBaseObject(new StatRefundResponse(), StatRefundResponse.class);
}
public static StatRefundResponse createsearchRefundAllResponse() {
var refund = fillAllTBaseObject(new StatRefund(), StatRefund.class);
var cart = fillAllTBaseObject(new InvoiceCart(), InvoiceCart.class);
var line = fillAllTBaseObject(new InvoiceLine(), InvoiceLine.class);
var cash = fillAllTBaseObject(new Cash(), Cash.class);
var status = fillAllTBaseObject(new InvoicePaymentRefundStatus(), InvoicePaymentRefundStatus.class);
var response = fillAllTBaseObject(new StatRefundResponse(), StatRefundResponse.class);
public static StatRefundResponse createSearchRefundAllResponse() {
var refund = fillRequiredTBaseObject(new StatRefund(), StatRefund.class);
var cart = fillRequiredTBaseObject(new InvoiceCart(), InvoiceCart.class);
var line = fillRequiredTBaseObject(new InvoiceLine(), InvoiceLine.class);
var cash = fillRequiredTBaseObject(new Cash(), Cash.class);
var status = fillRequiredTBaseObject(new InvoicePaymentRefundStatus(), InvoicePaymentRefundStatus.class);
var response = fillRequiredTBaseObject(new StatRefundResponse(), StatRefundResponse.class);
return response.setRefunds(
List.of(refund
@ -104,13 +106,13 @@ public class MagistaUtil {
}
public static StatInvoiceResponse createSearchInvoiceAllResponse() {
var invoice = fillAllTBaseObject(new StatInvoice(), StatInvoice.class);
var cart = fillAllTBaseObject(new InvoiceCart(), InvoiceCart.class);
var line = fillAllTBaseObject(new InvoiceLine(), InvoiceLine.class);
var cash = fillAllTBaseObject(new Cash(), Cash.class);
var status = fillAllTBaseObject(new InvoiceStatus(),
var invoice = fillRequiredTBaseObject(new StatInvoice(), StatInvoice.class);
var cart = fillRequiredTBaseObject(new InvoiceCart(), InvoiceCart.class);
var line = fillRequiredTBaseObject(new InvoiceLine(), InvoiceLine.class);
var cash = fillRequiredTBaseObject(new Cash(), Cash.class);
var status = fillRequiredTBaseObject(new InvoiceStatus(),
InvoiceStatus.class);
var response = fillAllTBaseObject(new StatInvoiceResponse(), StatInvoiceResponse.class);
var response = fillRequiredTBaseObject(new StatInvoiceResponse(), StatInvoiceResponse.class);
return response.setInvoices(
List.of(invoice
@ -125,11 +127,11 @@ public class MagistaUtil {
}
public static StatPayoutResponse createSearchPayoutAllResponse() {
var payout = fillAllTBaseObject(new StatPayout(), StatPayout.class);
var toolInfo = fillAllTBaseObject(new PayoutToolInfo(), PayoutToolInfo.class);
var bank = fillAllTBaseObject(new RussianBankAccount(), RussianBankAccount.class);
var status = fillAllTBaseObject(new PayoutStatus(), PayoutStatus.class);
var response = fillAllTBaseObject(new StatPayoutResponse(), StatPayoutResponse.class);
var payout = fillRequiredTBaseObject(new StatPayout(), StatPayout.class);
var toolInfo = fillRequiredTBaseObject(new PayoutToolInfo(), PayoutToolInfo.class);
var bank = fillRequiredTBaseObject(new RussianBankAccount(), RussianBankAccount.class);
var status = fillRequiredTBaseObject(new PayoutStatus(), PayoutStatus.class);
var response = fillRequiredTBaseObject(new StatPayoutResponse(), StatPayoutResponse.class);
toolInfo.setRussianBankAccount(bank);
return response.setPayouts(
List.of(payout
@ -138,13 +140,18 @@ public class MagistaUtil {
);
}
public static ContextFragment createContextFragment() {
return fillRequiredTBaseObject(new ContextFragment(), ContextFragment.class);
}
public static Judgement createJudgementAllowed() {
Resolution resolution = new Resolution();
resolution.setAllowed(new ResolutionAllowed());
return new Judgement().setResolution(resolution);
}
@SneakyThrows
public static <T extends TBase> T fillRequiredTBaseObject(T tbase, Class<T> type) {
return mockRequiredTBaseProcessor.process(tbase, new TBaseHandler<>(type));
}
@SneakyThrows
public static <T extends TBase> T fillAllTBaseObject(T tbase, Class<T> type) {
return mockFullTBaseProcessor.process(tbase, new TBaseHandler<>(type));
}
}

View File

@ -7,104 +7,104 @@ import lombok.experimental.UtilityClass;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import static com.rbkmoney.anapi.v2.testutil.RandomUtil.randomInteger;
import static com.rbkmoney.anapi.v2.testutil.RandomUtil.randomIntegerAsString;
@UtilityClass
public class OpenApiUtil {
public static MultiValueMap<String, String> getSearchRequiredParams() {
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("partyID", randomInteger(1, 1000));
params.add("partyID", randomIntegerAsString(1, 1000));
params.add("fromTime", "2007-12-03T10:15:30+01:00");
params.add("toTime", "2020-12-03T10:15:30+01:00");
params.add("limit", randomInteger(1, 40));
params.add("limit", randomIntegerAsString(1, 40));
return params;
}
public static MultiValueMap<String, String> getSearchPaymentAllParams() {
MultiValueMap<String, String> params = getSearchRequiredParams();
params.add("shopID", randomInteger(1, 10));
params.add("shopIDs", randomInteger(11, 20));
params.add("shopIDs", randomInteger(21, 30));
params.add("shopID", randomIntegerAsString(1, 10));
params.add("shopIDs", randomIntegerAsString(11, 20));
params.add("shopIDs", randomIntegerAsString(21, 30));
params.add("paymentInstitutionRealm", PaymentInstitutionRealm.live.name());
params.add("invoiceIDs", randomInteger(1, 10));
params.add("invoiceIDs", randomInteger(11, 20));
params.add("paymentStatus", PaymentStatus.StatusEnum.PENDING.name());
params.add("invoiceIDs", randomIntegerAsString(1, 10));
params.add("invoiceIDs", randomIntegerAsString(11, 20));
params.add("paymentStatus", PaymentStatus.StatusEnum.PENDING.getValue());
params.add("paymentFlow", "instant");
params.add("paymentMethod", "paymentTerminal");
params.add("paymentTerminalProvider", TerminalPaymentProvider.alipay.name());
params.add("invoiceID", randomInteger(1, 1000));
params.add("paymentID", randomInteger(1, 1000));
params.add("externalID", randomInteger(1, 1000));
params.add("invoiceID", randomIntegerAsString(1, 1000));
params.add("paymentID", randomIntegerAsString(1, 1000));
params.add("externalID", randomIntegerAsString(1, 1000));
params.add("payerEmail", "payer@mail.com");
params.add("payerIP", "0.0.0.0");
params.add("payerFingerprint", "iamveryunique");
params.add("first6", randomInteger(100000, 999999));
params.add("last4", randomInteger(1000, 9999));
params.add("first6", randomIntegerAsString(100000, 999999));
params.add("last4", randomIntegerAsString(1000, 9999));
params.add("rrn", "123456789010");
params.add("approvalCode", "QWERTY");
params.add("bankCardTokenProvider", BankCardTokenProvider.APPLEPAY.name());
params.add("bankCardPaymentSystem", BankCardPaymentSystem.MASTERCARD.name());
params.add("paymentAmountFrom", randomInteger(1, 9999));
params.add("paymentAmountTo", randomInteger(9999, 999999));
params.add("excludedShops", randomInteger(1, 10));
params.add("excludedShops", randomInteger(11, 20));
params.add("bankCardTokenProvider", "applepay");
params.add("bankCardPaymentSystem", "mastercard");
params.add("paymentAmountFrom", randomIntegerAsString(1, 9999));
params.add("paymentAmountTo", randomIntegerAsString(9999, 999999));
params.add("excludedShops", randomIntegerAsString(1, 10));
params.add("excludedShops", randomIntegerAsString(11, 20));
params.add("continuationToken", "test");
return params;
}
public static MultiValueMap<String, String> getSearchChargebackAllParams() {
MultiValueMap<String, String> params = getSearchRequiredParams();
params.add("shopID", randomInteger(1, 10));
params.add("shopIDs", randomInteger(11, 20));
params.add("shopIDs", randomInteger(21, 30));
params.add("shopID", randomIntegerAsString(1, 10));
params.add("shopIDs", randomIntegerAsString(11, 20));
params.add("shopIDs", randomIntegerAsString(21, 30));
params.add("paymentInstitutionRealm", PaymentInstitutionRealm.live.name());
params.add("offset", randomInteger(1, 10));
params.add("invoiceID", randomInteger(1, 1000));
params.add("paymentID", randomInteger(1, 1000));
params.add("chargebackID", randomInteger(1, 1000));
params.add("chargebackStatuses", ChargebackStatus.PENDING.name());
params.add("chargebackStages", ChargebackStage.CHARGEBACK.name());
params.add("chargebackCategories", ChargebackCategory.AUTHORISATION.name());
//params.add("continuationToken", "test");
params.add("offset", randomIntegerAsString(1, 10));
params.add("invoiceID", randomIntegerAsString(1, 1000));
params.add("paymentID", randomIntegerAsString(1, 1000));
params.add("chargebackID", randomIntegerAsString(1, 1000));
params.add("chargebackStatuses", ChargebackStatus.PENDING.getValue());
params.add("chargebackStages", ChargebackStage.CHARGEBACK.getValue());
params.add("chargebackCategories", ChargebackCategory.AUTHORISATION.getValue());
params.add("continuationToken", "test");
return params;
}
public static MultiValueMap<String, String> getSearchRefundAllParams() {
MultiValueMap<String, String> params = getSearchRequiredParams();
params.add("shopID", randomInteger(1, 10));
params.add("shopIDs", randomInteger(11, 20));
params.add("shopIDs", randomInteger(21, 30));
params.add("shopID", randomIntegerAsString(1, 10));
params.add("shopIDs", randomIntegerAsString(11, 20));
params.add("shopIDs", randomIntegerAsString(21, 30));
params.add("paymentInstitutionRealm", PaymentInstitutionRealm.live.name());
params.add("offset", randomInteger(1, 10));
params.add("invoiceIDs", randomInteger(1, 10));
params.add("invoiceIDs", randomInteger(11, 20));
params.add("invoiceID", randomInteger(1, 1000));
params.add("paymentID", randomInteger(1, 1000));
params.add("refundID", randomInteger(1, 1000));
params.add("externalID", randomInteger(1, 1000));
params.add("refundStatus", RefundStatus.StatusEnum.PENDING.name());
params.add("excludedShops", randomInteger(1, 10));
params.add("excludedShops", randomInteger(11, 20));
params.add("offset", randomIntegerAsString(1, 10));
params.add("invoiceIDs", randomIntegerAsString(1, 10));
params.add("invoiceIDs", randomIntegerAsString(11, 20));
params.add("invoiceID", randomIntegerAsString(1, 1000));
params.add("paymentID", randomIntegerAsString(1, 1000));
params.add("refundID", randomIntegerAsString(1, 1000));
params.add("externalID", randomIntegerAsString(1, 1000));
params.add("refundStatus", "pending");
params.add("excludedShops", randomIntegerAsString(1, 10));
params.add("excludedShops", randomIntegerAsString(11, 20));
params.add("continuationToken", "test");
return params;
}
public static MultiValueMap<String, String> getSearchInvoiceAllParams() {
MultiValueMap<String, String> params = getSearchRequiredParams();
params.add("shopID", randomInteger(1, 10));
params.add("shopIDs", randomInteger(11, 20));
params.add("shopIDs", randomInteger(21, 30));
params.add("shopID", randomIntegerAsString(1, 10));
params.add("shopIDs", randomIntegerAsString(11, 20));
params.add("shopIDs", randomIntegerAsString(21, 30));
params.add("paymentInstitutionRealm", PaymentInstitutionRealm.live.name());
params.add("invoiceIDs", randomInteger(1, 10));
params.add("invoiceIDs", randomInteger(11, 20));
params.add("invoiceStatus", Invoice.StatusEnum.PAID.name());
params.add("invoiceID", randomInteger(1, 1000));
params.add("externalID", randomInteger(1, 1000));
params.add("invoiceAmountFrom", randomInteger(1, 1000));
params.add("invoiceAmountTo", randomInteger(1, 1000));
params.add("excludedShops", randomInteger(1, 10));
params.add("excludedShops", randomInteger(11, 20));
params.add("invoiceIDs", randomIntegerAsString(1, 10));
params.add("invoiceIDs", randomIntegerAsString(11, 20));
params.add("invoiceStatus", "paid");
params.add("invoiceID", randomIntegerAsString(1, 1000));
params.add("externalID", randomIntegerAsString(1, 1000));
params.add("invoiceAmountFrom", randomIntegerAsString(1, 1000));
params.add("invoiceAmountTo", randomIntegerAsString(1, 1000));
params.add("excludedShops", randomIntegerAsString(1, 10));
params.add("excludedShops", randomIntegerAsString(11, 20));
params.add("continuationToken", "test");
return params;
@ -112,15 +112,15 @@ public class OpenApiUtil {
public static MultiValueMap<String, String> getSearchPayoutAllParams() {
MultiValueMap<String, String> params = getSearchRequiredParams();
params.add("shopID", randomInteger(1, 10));
params.add("shopIDs", randomInteger(11, 20));
params.add("shopIDs", randomInteger(21, 30));
params.add("shopID", randomIntegerAsString(1, 10));
params.add("shopIDs", randomIntegerAsString(11, 20));
params.add("shopIDs", randomIntegerAsString(21, 30));
params.add("paymentInstitutionRealm", PaymentInstitutionRealm.live.name());
params.add("offset", randomInteger(1, 10));
params.add("payoutID", randomInteger(1, 1000));
params.add("offset", randomIntegerAsString(1, 10));
params.add("payoutID", randomIntegerAsString(1, 1000));
params.add("payoutToolType", "PayoutAccount");
params.add("excludedShops", randomInteger(1, 10));
params.add("excludedShops", randomInteger(11, 20));
params.add("excludedShops", randomIntegerAsString(1, 10));
params.add("excludedShops", randomIntegerAsString(11, 20));
params.add("continuationToken", "test");
return params;
}

View File

@ -2,6 +2,7 @@ package com.rbkmoney.anapi.v2.testutil;
import lombok.experimental.UtilityClass;
import java.nio.charset.StandardCharsets;
import java.util.Random;
@UtilityClass
@ -9,8 +10,22 @@ public class RandomUtil {
private static final Random random = new Random();
public static String randomInteger(int from, int to) {
public static int randomInt(int from, int to) {
return random.nextInt(to - from) + from;
}
public static String randomIntegerAsString(int from, int to) {
return String.valueOf(random.nextInt(to - from) + from);
}
public static String randomString(int length) {
return new String(randomBytes(length), StandardCharsets.UTF_8);
}
public static byte[] randomBytes(int length) {
byte[] array = new byte[length];
new Random().nextBytes(array);
return array;
}
}

View File

@ -5,7 +5,7 @@ import org.junit.jupiter.api.Test;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertNotNull;
class ConverterUtilTest {
@ -23,6 +23,6 @@ class ConverterUtilTest {
assertEquals(List.of("1"), result);
result = ConverterUtil.merge(null, null);
assertNull(result);
assertNotNull(result);
}
}