Skip to content

Commit

Permalink
Merge pull request #47 from pagopa/VAS-1114-feat-add-idempotency-check
Browse files Browse the repository at this point in the history
[VAS-1114] feat: add idempotency check
  • Loading branch information
alessio-cialini authored Jul 3, 2024
2 parents 9c847b4 + fab0fc2 commit edb923f
Show file tree
Hide file tree
Showing 13 changed files with 2,866 additions and 3,418 deletions.
1,376 changes: 516 additions & 860 deletions integration-test/src/yarn.lock

Large diffs are not rendered by default.

2,580 changes: 1,224 additions & 1,356 deletions openapi/openapi.json

Large diffs are not rendered by default.

2,163 changes: 993 additions & 1,170 deletions openapi/openapi_external.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@
@Configuration
public class OpenApiConfig {

public static final String BASE_PATH = "/print-payment-notice-service/v1";
public static final String INTERNAL_PATH = "/print-payment-notice-service/internal/v1";

public static final String EXTERNAL_PATH = "/print-payment-notice-service/external/v1";

public static final String LOCAL_PATH = "http://localhost:8080";
public static final String APIM_DEV = "https://api.dev.platform.pagopa.it";
public static final String APIM_UAT = "https://api.uat.platform.pagopa.it";
Expand All @@ -47,7 +50,7 @@ OpenAPI customOpenAPI(
List.of(APIM_DEV, APIM_UAT, APIM_PROD))
._default(APIM_PROD))
.addServerVariable("basePath",
new ServerVariable()._enum(List.of(BASE_PATH)))
new ServerVariable()._enum(List.of(INTERNAL_PATH))._default(INTERNAL_PATH))
)))
.components(new Components().addSecuritySchemes("ApiKey",
new SecurityScheme()
Expand Down Expand Up @@ -130,8 +133,8 @@ public Map<String, GroupedOpenApi> configureGroupedOpenApi(Map<String, GroupedOp
var baseTitle = openApi.getInfo().getTitle();
var group = groupedOpenApi.getDisplayName();
openApi.getInfo().setTitle(baseTitle + " - " + group);
if("apim".equals(id)) {
openApi.setServers(Collections.singletonList(new Server().url(APIM_PROD + BASE_PATH)));
if("external".equals(id)) {
openApi.setServers(Collections.singletonList(new Server().url(APIM_PROD + EXTERNAL_PATH)));
openApi.getPaths().forEach((key, value) -> {
if (value.getPost() != null && value.getPost().getParameters() != null) {
value.getPost().getParameters().removeIf(parameter ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,11 @@ public NoticeGenerationMassiveResource generateNoticeMassiveRequest(
@Parameter(description = "massive notice generation request data")
@Valid @NotNull @RequestBody NoticeGenerationMassiveRequest noticeGenerationMassiveRequest,
@Parameter(description = "userId to use for request status retrieval")
@Valid @NotNull @RequestHeader(Constants.X_USER_ID) String userId) {
@Valid @NotNull @RequestHeader(Constants.X_USER_ID) String userId,
@Parameter(description = "key to be used for idempotency checks of pre-existing requests having the same key")
@Valid @NotNull @RequestHeader(Constants.IDEMPOTENCY_KEY) String idempotencyKey) {
return NoticeGenerationMassiveResource.builder().folderId(
noticeGenerationService.generateMassive(noticeGenerationMassiveRequest, userId))
noticeGenerationService.generateMassive(noticeGenerationMassiveRequest, userId, idempotencyKey))
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ public class PaymentNoticeGenerationRequest {
@Indexed()
private String userId;

@Indexed()
private String idempotencyKey;

@CreatedDate
private Instant createdAt;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package it.gov.pagopa.payment.notices.service.model.notice;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
Expand All @@ -21,6 +22,7 @@ public class InstallmentData {

@Schema(description = "Installment amount", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull
@Max(value = 99999999999L)
private Long amount;

@Schema(description = "Installment dueDate", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ public interface PaymentGenerationRequestRepository extends MongoRepository<Paym
@Update("{ '$inc' : { 'numberOfElementsFailed' : 1 } }")
long findAndIncrementNumberOfElementsFailedById(String folderId);

Optional<PaymentNoticeGenerationRequest> findByIdempotencyKeyAndUserId(String idempotencyKey, String userId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public interface NoticeGenerationService {
* the folder is executed
* @return folderId produced when inserting the request
*/
String generateMassive(NoticeGenerationMassiveRequest noticeGenerationMassiveRequest, String userId);
String generateMassive(NoticeGenerationMassiveRequest noticeGenerationMassiveRequest, String userId, String idempotencyKey);

/**
* Kickstarts and returns the notice using the services provided by the generator API,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import static it.gov.pagopa.payment.notices.service.util.WorkingDirectoryUtils.createWorkingDirectory;

Expand Down Expand Up @@ -84,21 +85,31 @@ public GetGenerationRequestStatusResource getFolderStatus(String folderId, Strin

@Override
@Transactional
public String generateMassive(NoticeGenerationMassiveRequest noticeGenerationMassiveRequest, String userId) {

try {

String folderId = paymentGenerationRequestRepository.save(PaymentNoticeGenerationRequest.builder()
.status(PaymentGenerationRequestStatus.INSERTED)
.createdAt(Instant.now())
.items(new ArrayList<>())
.userId(userId)
.numberOfElementsTotal(noticeGenerationMassiveRequest.getNotices().size())
.numberOfElementsFailed(0)
.requestDate(Instant.now())
.build()).getId();

asyncService.sendNotices(noticeGenerationMassiveRequest, folderId, userId);
public String generateMassive(
NoticeGenerationMassiveRequest noticeGenerationMassiveRequest, String userId, String idempotencyKey) {

try {

String folderId;

Optional<PaymentNoticeGenerationRequest> existingRequest = paymentGenerationRequestRepository
.findByIdempotencyKeyAndUserId(idempotencyKey, userId);

if (existingRequest.isEmpty()) {
folderId = paymentGenerationRequestRepository.save(PaymentNoticeGenerationRequest.builder()
.status(PaymentGenerationRequestStatus.INSERTED)
.createdAt(Instant.now())
.items(new ArrayList<>())
.userId(userId)
.numberOfElementsTotal(noticeGenerationMassiveRequest.getNotices().size())
.numberOfElementsFailed(0)
.requestDate(Instant.now())
.build()).getId();

asyncService.sendNotices(noticeGenerationMassiveRequest, folderId, userId);
} else {
folderId = existingRequest.get().getId();
}

return folderId;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ public class Constants {

public static final String APIM_SUBSCRIPTION_KEY = "Ocp-Apim-Subscription-Key";

public static final String IDEMPOTENCY_KEY = "Idempotency-Key";
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import it.gov.pagopa.payment.notices.service.model.notice.Notice;
import it.gov.pagopa.payment.notices.service.model.notice.NoticeRequestData;
import it.gov.pagopa.payment.notices.service.service.NoticeGenerationService;
import it.gov.pagopa.payment.notices.service.util.Constants;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -93,7 +94,7 @@ void getFolderStatusShouldReturnDataOn400() throws Exception {
});
String url = "/notices/folder/folderTest/status";
mvc.perform(get(url)
.header("X-User-Id", "userTest")
.header(Constants.X_USER_ID, "userTest")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().is4xxClientError())
.andExpect(content().contentType("application/json"));
Expand All @@ -108,7 +109,7 @@ void getFolderStatusShouldReturnDataOn500() throws Exception {
});
String url = "/notices/folder/folderTest/status";
mvc.perform(get(url)
.header("X-User-Id", "userTest")
.header(Constants.X_USER_ID, "userTest")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().is5xxServerError())
.andExpect(content().contentType("application/json"));
Expand All @@ -117,7 +118,7 @@ void getFolderStatusShouldReturnDataOn500() throws Exception {

@Test
void generateMassiveShouldReturnFolderIdOn200() throws Exception {
when(noticeGenerationService.generateMassive(any(), any()))
when(noticeGenerationService.generateMassive(any(), any(), any()))
.thenReturn("folderTests");
String url = "/notices/generate-massive";
String bodyStr = mvc.perform(post(url)
Expand All @@ -127,7 +128,8 @@ void generateMassiveShouldReturnFolderIdOn200() throws Exception {
NoticeGenerationRequestItem.builder().build()
)).build()
))
.header("X-User-Id", "userTest")
.header(Constants.X_USER_ID, "userTest")
.header(Constants.IDEMPOTENCY_KEY, "key")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().is2xxSuccessful())
.andExpect(content().contentType("application/json"))
Expand All @@ -137,7 +139,7 @@ void generateMassiveShouldReturnFolderIdOn200() throws Exception {
bodyStr, NoticeGenerationMassiveResource.class);
Assertions.assertNotNull(resource.getFolderId());
Assertions.assertEquals("folderTests", resource.getFolderId());
verify(noticeGenerationService).generateMassive(any(), any());
verify(noticeGenerationService).generateMassive(any(), any(), any());
}

@Test
Expand Down Expand Up @@ -166,7 +168,7 @@ void generateMassiveShouldReturn400OnMissingBody() throws Exception {

@Test
void generateMassiveShouldReturn400OnMissingUserId() throws Exception {
when(noticeGenerationService.generateMassive(any(), any()))
when(noticeGenerationService.generateMassive(any(), any(), any()))
.thenReturn("folderTests");
String url = "/notices/generate-massive";
mvc.perform(post(url)
Expand Down
Loading

0 comments on commit edb923f

Please sign in to comment.