PROX-370: add exponent (#15)

* PROX-370: add exponent
This commit is contained in:
Anatoly Cherkasov 2020-01-22 19:17:42 +03:00 committed by GitHub
parent 1d0f8a8c05
commit 77343b02c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 294 additions and 51 deletions

View File

@ -71,7 +71,7 @@
<dependency>
<groupId>com.rbkmoney</groupId>
<artifactId>adapter-common-lib</artifactId>
<version>0.0.7-SNAPSHOT</version>
<version>0.0.15</version>
</dependency>
<dependency>
<groupId>com.rbkmoney</groupId>
@ -79,6 +79,12 @@
<version>${error-mapping.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.rbkmoney.geck</groupId>
<artifactId>serializer</artifactId>
<version>0.6.7</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.rbkmoney</groupId>
<artifactId>damsel</artifactId>

View File

@ -1,8 +1,6 @@
package com.rbkmoney.adapter.bank.payout.spring.boot.starter.config.properties;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.rbkmoney.adapter.common.mapper.SimpleErrorMapping;
import com.rbkmoney.adapter.common.mapper.SimpleObjectMapper;
import com.rbkmoney.error.mapping.ErrorMapping;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
@ -23,6 +21,6 @@ public class ErrorMappingProperties {
@Bean
public ErrorMapping errorMapping() throws IOException {
return new SimpleErrorMapping(errorMappingFilePath, errorMappingPattern).getErrorMapping();
return new SimpleErrorMapping(errorMappingFilePath, errorMappingPattern).createErrorMapping();
}
}

View File

@ -6,5 +6,6 @@ import com.rbkmoney.adapter.bank.payout.spring.boot.starter.model.Step;
public interface StepResolver<T extends EntryStateModel, X extends ExitStateModel> {
Step resolveEntry(T entryStateModel);
Step resolveExit(X exitStateModel);
}

View File

@ -5,5 +5,6 @@ import com.rbkmoney.adapter.bank.payout.spring.boot.starter.model.ExitStateModel
public interface CommonHandler<T extends EntryStateModel, X extends ExitStateModel> {
boolean isHandle(T entryStateModel);
X handle(T entryStateModel);
}

View File

@ -1,18 +1,24 @@
package com.rbkmoney.adapter.bank.payout.spring.boot.starter.model;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
import com.rbkmoney.adapter.common.model.PollingInfo;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.Instant;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AdapterState {
private Step step;
// TODO: backward compatibility
private Long maxTimePoolingMillis;
private TransactionInfo trxInfo;
@JsonUnwrapped
private PollingInfo pollingInfo;
}

View File

@ -1,26 +0,0 @@
package com.rbkmoney.adapter.bank.payout.spring.boot.starter.serializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.rbkmoney.adapter.bank.payout.spring.boot.starter.model.AdapterState;
import com.rbkmoney.adapter.common.serializer.StateSerializer;
import java.io.IOException;
public class AdapterStateSerializer extends StateSerializer<AdapterState> {
public AdapterStateSerializer(ObjectMapper mapper) {
super(mapper);
}
@Override
public AdapterState read(byte[] data) {
if (data == null) {
return new AdapterState();
}
try {
return getMapper().readValue(data, AdapterState.class);
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
}
}

View File

@ -4,10 +4,18 @@ import com.rbkmoney.adapter.bank.payout.spring.boot.starter.model.EntryStateMode
import com.rbkmoney.adapter.bank.payout.spring.boot.starter.model.ExitStateModel;
import com.rbkmoney.damsel.withdrawals.provider_adapter.Intent;
import java.time.Instant;
public interface IntentService {
Intent getFailureByCode(ExitStateModel exitStateModel);
Intent getFailureByCodeAndDesc(ExitStateModel exitStateModel);
Intent getSuccess(ExitStateModel exitStateModel);
Intent getSleep(ExitStateModel exitStateModel);
Long getMaxDateTimeInstant(EntryStateModel entryStateModel);
Long getMaxDateTimeInstantMillis(EntryStateModel entryStateModel);
Instant extractMaxDateTimeInstant(EntryStateModel entryStateModel);
}

View File

@ -3,11 +3,15 @@ package com.rbkmoney.adapter.bank.payout.spring.boot.starter.service;
import com.rbkmoney.adapter.bank.payout.spring.boot.starter.config.properties.TimerProperties;
import com.rbkmoney.adapter.bank.payout.spring.boot.starter.model.EntryStateModel;
import com.rbkmoney.adapter.bank.payout.spring.boot.starter.model.ExitStateModel;
import com.rbkmoney.damsel.base.Timer;
import com.rbkmoney.adapter.common.model.PollingInfo;
import com.rbkmoney.adapter.common.utils.times.ExponentialBackOffPollingService;
import com.rbkmoney.damsel.domain.TransactionInfo;
import com.rbkmoney.damsel.withdrawals.provider_adapter.*;
import com.rbkmoney.damsel.withdrawals.provider_adapter.FinishIntent;
import com.rbkmoney.damsel.withdrawals.provider_adapter.FinishStatus;
import com.rbkmoney.damsel.withdrawals.provider_adapter.Intent;
import com.rbkmoney.damsel.withdrawals.provider_adapter.Success;
import com.rbkmoney.error.mapping.ErrorMapping;
import com.rbkmoney.java.damsel.utils.extractors.OptionsExtractors;
import com.rbkmoney.java.damsel.utils.creators.WithdrawalsProviderAdapterPackageCreators;
import lombok.RequiredArgsConstructor;
import java.time.Instant;
@ -37,21 +41,38 @@ public class IntentServiceImpl implements IntentService {
}
public Intent getSleep(ExitStateModel exitStateModel) {
if (exitStateModel.getNextState().getMaxTimePoolingMillis() == null) {
throw new IllegalArgumentException("Need to specify 'maxTimePoolingMillis' before sleep");
Instant maxDateTimePolling = exitStateModel.getNextState().getPollingInfo().getMaxDateTimePolling();
if (maxDateTimePolling == null) {
throw new IllegalArgumentException("Need to specify 'maxDateTimePolling' before sleep");
}
if (exitStateModel.getNextState().getMaxTimePoolingMillis() < Instant.now().toEpochMilli()) {
if (maxDateTimePolling.toEpochMilli() < Instant.now().toEpochMilli()) {
return prepareFailureIntent();
}
int timerPollingDelay = computePollingInterval(exitStateModel);
return WithdrawalsProviderAdapterPackageCreators.createIntentWithSleepIntent(timerPollingDelay);
}
private Intent prepareFailureIntent() {
String code = "Sleep timeout";
String reason = "Max time pool limit reached";
return Intent.finish(new FinishIntent(FinishStatus.failure(errorMapping.mapFailure(code, reason))));
}
int timerPollingDelay = OptionsExtractors.extractPollingDelay(exitStateModel.getEntryStateModel().getOptions(), timerProperties.getPollingDelay());
return Intent.sleep(new SleepIntent(new Timer(Timer.timeout(timerPollingDelay))));
private int computePollingInterval(ExitStateModel exitStateModel) {
ExponentialBackOffPollingService<PollingInfo> pollingService = new ExponentialBackOffPollingService<>();
return pollingService.prepareNextPollingInterval(
exitStateModel.getNextState().getPollingInfo(),
exitStateModel.getEntryStateModel().getOptions()
);
}
public Long getMaxDateTimeInstant(EntryStateModel entryStateModel) {
public Long getMaxDateTimeInstantMillis(EntryStateModel entryStateModel) {
int maxTimePolling = extractMaxTimePolling(entryStateModel.getOptions(), timerProperties.getMaxTimePolling());
return Instant.now().plus(maxTimePolling, ChronoUnit.MINUTES).toEpochMilli();
}
public Instant extractMaxDateTimeInstant(EntryStateModel entryStateModel) {
int maxTimePolling = extractMaxTimePolling(entryStateModel.getOptions(), timerProperties.getMaxTimePolling());
return Instant.now().plus(maxTimePolling, ChronoUnit.MINUTES);
}
}

View File

@ -0,0 +1,41 @@
package com.rbkmoney.adapter.bank.payout.spring.boot.starter.state.deserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.rbkmoney.adapter.bank.payout.spring.boot.starter.model.AdapterState;
import com.rbkmoney.adapter.common.state.deserializer.DeserializationException;
import com.rbkmoney.adapter.common.state.deserializer.Deserializer;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import java.io.IOException;
@Getter
@Setter
@AllArgsConstructor
public class AdapterStateDeserializer implements Deserializer<AdapterState> {
private final ObjectMapper mapper;
public AdapterState read(byte[] data) {
if (data == null) {
return new AdapterState();
} else {
try {
return this.getMapper().readValue(data, AdapterState.class);
} catch (IOException ex) {
throw new IllegalArgumentException(ex);
}
}
}
public AdapterState read(String data) {
throw new DeserializationException("Deserialization not supported");
}
public ObjectMapper getMapper() {
return this.mapper;
}
}

View File

@ -0,0 +1,11 @@
package com.rbkmoney.adapter.bank.payout.spring.boot.starter.state.serializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.rbkmoney.adapter.bank.payout.spring.boot.starter.model.AdapterState;
import com.rbkmoney.adapter.common.state.serializer.StateSerializer;
public class AdapterStateSerializer extends StateSerializer<AdapterState> {
public AdapterStateSerializer(ObjectMapper mapper) {
super(mapper);
}
}

View File

@ -0,0 +1,32 @@
package com.rbkmoney.adapter.bank.payout.spring.boot.starter.state.utils;
import com.rbkmoney.adapter.bank.payout.spring.boot.starter.model.AdapterState;
import com.rbkmoney.adapter.bank.payout.spring.boot.starter.state.deserializer.AdapterStateDeserializer;
import com.rbkmoney.damsel.proxy_provider.PaymentContext;
import com.rbkmoney.damsel.proxy_provider.RecurrentTokenContext;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class AdapterStateUtils {
public static AdapterState getAdapterState(Object context, AdapterStateDeserializer adapterDeserializer) {
AdapterState adapterState = new AdapterState();
byte[] state = getState(context);
if (state != null && state.length > 0) {
return adapterDeserializer.read(state);
}
return adapterState;
}
private static byte[] getState(Object context) {
if (context instanceof RecurrentTokenContext) {
if (((RecurrentTokenContext) context).getSession() == null) {
return new byte[0];
}
return ((RecurrentTokenContext) context).getSession().getState();
}
return ((PaymentContext) context).getSession().getState();
}
}

View File

@ -1,8 +1,7 @@
[
{
"code":"unknown",
"description":"unknown",
"regexp":".*",
"mapping":"authorization_failed:unknown"
"codeRegex": "unknown",
"descriptionRegex": "unknown",
"mapping": "ResultUnknown"
}
]

View File

@ -0,0 +1,44 @@
package com.rbkmoney.adapter.bank.payout.spring.boot.starter.model;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.rbkmoney.adapter.common.mapper.SimpleObjectMapper;
import com.rbkmoney.adapter.common.model.PollingInfo;
import org.junit.Test;
import java.io.IOException;
import java.time.Instant;
import static org.junit.Assert.*;
public class AdapterStateTest {
@Test
public void testUnwrappedPollingInfo() throws IOException {
ObjectMapper om = new SimpleObjectMapper().createSimpleObjectMapperFactory();
AdapterState as = new AdapterState();
as.setStep(Step.CHECK);
PollingInfo pollingInfo = new PollingInfo();
pollingInfo.setMaxDateTimePolling(Instant.now());
as.setPollingInfo(pollingInfo);
String str = om.writeValueAsString(as);
assertTrue(str.startsWith("{\"step\":\"CHECK\",\"max_date_time_polling\":"));
AdapterState acRestored = om.readValue(str, AdapterState.class);
assertEquals(as.getStep(), acRestored.getStep());
assertNotNull(acRestored.getPollingInfo());
assertEquals(as.getPollingInfo().getMaxDateTimePolling(), acRestored.getPollingInfo().getMaxDateTimePolling());
}
@Test
public void testUnwrappedPollingInfoIsNull() throws IOException {
ObjectMapper om = new SimpleObjectMapper().createSimpleObjectMapperFactory();
AdapterState as = new AdapterState();
as.setStep(Step.CHECK);
as.setPollingInfo(null);
String str = om.writeValueAsString(as);
assertTrue(str.startsWith("{\"step\":\"CHECK\""));
AdapterState acRestored = om.readValue(str, AdapterState.class);
assertEquals(as.getStep(), acRestored.getStep());
assertNotNull(acRestored.getPollingInfo());
}
}

View File

@ -0,0 +1,89 @@
package com.rbkmoney.adapter.bank.payout.spring.boot.starter.service;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.rbkmoney.adapter.bank.payout.spring.boot.starter.config.properties.TimerProperties;
import com.rbkmoney.adapter.bank.payout.spring.boot.starter.model.AdapterState;
import com.rbkmoney.adapter.bank.payout.spring.boot.starter.model.EntryStateModel;
import com.rbkmoney.adapter.bank.payout.spring.boot.starter.model.ExitStateModel;
import com.rbkmoney.adapter.common.mapper.SimpleObjectMapper;
import com.rbkmoney.adapter.common.model.PollingInfo;
import com.rbkmoney.damsel.withdrawals.provider_adapter.Intent;
import com.rbkmoney.error.mapping.ErrorMapping;
import org.junit.Before;
import org.junit.Test;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.HashMap;
import static org.junit.Assert.assertTrue;
public class IntentServiceImplTest {
private static final String ERROR_MAPPING_FILE_PATH = "src/test/resources/fixture/errors.json";
private static final String ERROR_MAPPING_PATTERN = "'%s' - '%s'";
private IntentServiceImpl intentService;
@Before
public void setUp() throws IOException {
intentService = new IntentServiceImpl(prepareErrorMapping(), prepareTimerProperties());
}
@Test
public void getSleepIntentSuccess() {
ExitStateModel exitStateModel = new ExitStateModel();
EntryStateModel entryStateModel = new EntryStateModel();
entryStateModel.setOptions(new HashMap<>());
exitStateModel.setEntryStateModel(entryStateModel);
AdapterState adapterState = new AdapterState();
PollingInfo pollingInfo = new PollingInfo();
pollingInfo.setMaxDateTimePolling(Instant.now().plus(10000L, ChronoUnit.MINUTES));
adapterState.setPollingInfo(pollingInfo);
exitStateModel.setNextState(adapterState);
Intent intent = intentService.getSleep(exitStateModel);
assertTrue(intent.isSetSleep());
}
@Test
public void getSleepIntentFailure() {
ExitStateModel exitStateModel = new ExitStateModel();
AdapterState adapterState = new AdapterState();
PollingInfo pollingInfo = new PollingInfo();
pollingInfo.setMaxDateTimePolling(Instant.now().minus(1000L, ChronoUnit.MINUTES));
adapterState.setPollingInfo(pollingInfo);
exitStateModel.setNextState(adapterState);
Intent intent = intentService.getSleep(exitStateModel);
assertTrue(intent.getFinish().getStatus().isSetFailure());
}
@Test(expected = IllegalArgumentException.class)
public void getSleepException() {
ExitStateModel exitStateModel = new ExitStateModel();
AdapterState adapterState = new AdapterState();
adapterState.setPollingInfo(new PollingInfo());
exitStateModel.setNextState(adapterState);
intentService.getSleep(exitStateModel);
}
private ErrorMapping prepareErrorMapping() throws IOException {
ObjectMapper mapper = new SimpleObjectMapper().createSimpleObjectMapperFactory();
File file = new File(ERROR_MAPPING_FILE_PATH);
InputStream is = new FileInputStream(file);
ErrorMapping errorMapping = new ErrorMapping(is, ERROR_MAPPING_PATTERN, mapper);
errorMapping.validateMapping();
return errorMapping;
}
private TimerProperties prepareTimerProperties() {
TimerProperties timerProperties = new TimerProperties();
timerProperties.setMaxTimePolling(600);
timerProperties.setPollingDelay(10);
return timerProperties;
}
}

View File

@ -0,0 +1,12 @@
[
{
"codeRegex": "Sleep timeout",
"descriptionRegex": "Max time pool limit reached",
"mapping": "authorization_failed:operation_blocked"
},
{
"codeRegex": "unknown",
"descriptionRegex": "unknown",
"mapping": "ResultUnknown"
}
]