PROX-458: add polling info (#5)

This commit is contained in:
Anatoly Cherkasov 2020-10-27 14:09:51 +03:00 committed by GitHub
parent 73e924d93c
commit 52999c62ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 196 additions and 19 deletions

@ -1 +1 @@
Subproject commit 34f432a1e8e0adedbba23ecd29c1eb2412a8d416
Subproject commit f42e059d9ec93826ba4ad23232eed8ce67bd5486

View File

@ -12,7 +12,7 @@
</parent>
<artifactId>adapter-cashreg-spring-boot-starter</artifactId>
<version>1.0.2</version>
<version>1.0.3</version>
<packaging>jar</packaging>
<properties>
@ -40,6 +40,12 @@
<artifactId>common</artifactId>
<version>0.6.11</version>
</dependency>
<dependency>
<groupId>com.rbkmoney.geck</groupId>
<artifactId>serializer</artifactId>
<version>0.6.11</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>

View File

@ -3,12 +3,12 @@ package com.rbkmoney.adapter.cashreg.spring.boot.starter.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
import com.rbkmoney.adapter.common.model.PollingInfo;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.time.Instant;
@Getter
@Setter
@ToString
@ -18,13 +18,13 @@ public class AdapterState {
private Step nextStep;
@JsonProperty(value = "max_date_time_polling")
private Instant maxDateTimePolling;
@JsonProperty(value = "cashreg_id")
private String cashregId;
@JsonProperty(value = "receipt_id")
private String receiptId;
@JsonUnwrapped
private PollingInfo pollingInfo;
}

View File

@ -1,9 +1,7 @@
package com.rbkmoney.adapter.cashreg.spring.boot.starter.model;
import com.rbkmoney.adapter.cashreg.spring.boot.starter.constant.TargetType;
import lombok.Builder;
import lombok.Data;
import lombok.ToString;
import lombok.*;
import java.math.BigDecimal;
import java.util.List;
@ -11,6 +9,8 @@ import java.util.Map;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class EntryStateModel {
private String cashRegId;

View File

@ -1,8 +1,11 @@
package com.rbkmoney.adapter.cashreg.spring.boot.starter.service;
import com.rbkmoney.adapter.cashreg.spring.boot.starter.model.EntryStateModel;
import com.rbkmoney.adapter.cashreg.spring.boot.starter.model.ExitStateModel;
import com.rbkmoney.damsel.cashreg.adapter.Intent;
import java.time.Instant;
public interface IntentService {
Intent getFailureByCode(ExitStateModel exitStateModel);
@ -11,4 +14,6 @@ public interface IntentService {
Intent getSuccess(ExitStateModel exitStateModel);
Intent getSleep(ExitStateModel exitStateModel);
Instant extractMaxDateTimeInstant(EntryStateModel entryStateModel);
}

View File

@ -1,16 +1,19 @@
package com.rbkmoney.adapter.cashreg.spring.boot.starter.service;
import com.rbkmoney.adapter.cashreg.spring.boot.starter.config.properties.TimerProperties;
import com.rbkmoney.adapter.cashreg.spring.boot.starter.model.EntryStateModel;
import com.rbkmoney.adapter.cashreg.spring.boot.starter.model.ExitStateModel;
import com.rbkmoney.adapter.common.model.PollingInfo;
import com.rbkmoney.adapter.common.utils.times.ExponentialBackOffPollingService;
import com.rbkmoney.damsel.cashreg.adapter.*;
import com.rbkmoney.damsel.cashreg.base.Timer;
import com.rbkmoney.damsel.domain.Failure;
import com.rbkmoney.error.mapping.ErrorMapping;
import com.rbkmoney.java.damsel.utils.extractors.OptionsExtractors;
import lombok.RequiredArgsConstructor;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import static com.rbkmoney.java.damsel.utils.extractors.OptionsExtractors.extractMaxTimePolling;
import static com.rbkmoney.adapter.cashreg.spring.boot.starter.constant.Error.SLEEP_TIMEOUT;
/**
@ -42,16 +45,33 @@ public class IntentServiceImpl implements IntentService {
}
public Intent getSleep(ExitStateModel exitStateModel) {
if (exitStateModel.getAdapterContext().getMaxDateTimePolling() == null) {
throw new IllegalArgumentException("Need to specify 'maxTimePoolingMillis' before sleep");
Instant maxDateTimePolling = exitStateModel.getAdapterContext().getPollingInfo().getMaxDateTimePolling();
if (maxDateTimePolling == null) {
throw new IllegalArgumentException("Need to specify 'maxDateTimePolling' before sleep");
}
if (exitStateModel.getAdapterContext().getMaxDateTimePolling().getEpochSecond() < Instant.now().getEpochSecond()) {
Failure failure = errorMapping.mapFailure(SLEEP_TIMEOUT.getCode(), SLEEP_TIMEOUT.getMessage());
return Intent.finish(new FinishIntent(FinishStatus.failure(failure)));
if (maxDateTimePolling.toEpochMilli() < Instant.now().toEpochMilli()) {
return prepareFailureIntent();
}
int timerPollingDelay = OptionsExtractors.extractPollingDelay(exitStateModel.getEntryStateModel().getOptions(), timerProperties.getPollingDelay());
int timerPollingDelay = computePollingInterval(exitStateModel);
return Intent.sleep(new SleepIntent(new Timer(Timer.timeout(timerPollingDelay))));
}
private int computePollingInterval(ExitStateModel exitStateModel) {
ExponentialBackOffPollingService<PollingInfo> pollingService = new ExponentialBackOffPollingService<>();
return pollingService.prepareNextPollingInterval(
exitStateModel.getAdapterContext().getPollingInfo(),
exitStateModel.getEntryStateModel().getOptions()
);
}
private Intent prepareFailureIntent() {
String code = SLEEP_TIMEOUT.getCode();
String reason = SLEEP_TIMEOUT.getMessage();
return Intent.finish(new FinishIntent(FinishStatus.failure(errorMapping.mapFailure(code, reason))));
}
public Instant extractMaxDateTimeInstant(EntryStateModel entryStateModel) {
int maxTimePolling = extractMaxTimePolling(entryStateModel.getOptions(), timerProperties.getMaxTimePolling());
return Instant.now().plus(maxTimePolling, ChronoUnit.MINUTES);
}
}

View File

@ -0,0 +1,45 @@
package com.rbkmoney.adapter.cashreg.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.setNextStep(Step.CHECK_STATUS);
PollingInfo pollingInfo = new PollingInfo();
pollingInfo.setMaxDateTimePolling(Instant.now());
as.setPollingInfo(pollingInfo);
String str = om.writeValueAsString(as);
assertTrue(str.startsWith("{\"nextStep\":\"CHECK_STATUS\",\"max_date_time_polling\":"));
AdapterState acRestored = om.readValue(str, AdapterState.class);
assertEquals(as.getNextStep(), acRestored.getNextStep());
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.setNextStep(Step.CHECK_STATUS);
as.setPollingInfo(null);
String str = om.writeValueAsString(as);
assertTrue(str.startsWith("{\"nextStep\":\"CHECK_STATUS\""));
AdapterState acRestored = om.readValue(str, AdapterState.class);
assertEquals(as.getNextStep(), acRestored.getNextStep());
assertNotNull(acRestored.getPollingInfo());
}
}

View File

@ -0,0 +1,89 @@
package com.rbkmoney.adapter.cashreg.spring.boot.starter.service;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.rbkmoney.adapter.cashreg.spring.boot.starter.config.properties.TimerProperties;
import com.rbkmoney.adapter.cashreg.spring.boot.starter.model.AdapterState;
import com.rbkmoney.adapter.cashreg.spring.boot.starter.model.EntryStateModel;
import com.rbkmoney.adapter.cashreg.spring.boot.starter.model.ExitStateModel;
import com.rbkmoney.adapter.common.mapper.SimpleObjectMapper;
import com.rbkmoney.adapter.common.model.PollingInfo;
import com.rbkmoney.damsel.cashreg.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.setAdapterContext(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.setAdapterContext(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.setAdapterContext(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"
}
]