Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PAGOPA-1996] Paid Notice List: new path #87

Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3,747 changes: 1,889 additions & 1,858 deletions openapi/openapi.json

Large diffs are not rendered by default.

1,073 changes: 505 additions & 568 deletions openapi/openapi_ec.json

Large diffs are not rendered by default.

1,442 changes: 701 additions & 741 deletions openapi/openapi_helpdesk.json

Large diffs are not rendered by default.

1,636 changes: 771 additions & 865 deletions openapi/openapi_io.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,23 @@

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.headers.Header;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import it.gov.pagopa.bizeventsservice.model.ProblemJson;
import it.gov.pagopa.bizeventsservice.model.filterandorder.Order;
import it.gov.pagopa.bizeventsservice.model.response.paidnotice.NoticeListWrapResponse;
import org.springframework.data.domain.Sort;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import javax.validation.constraints.NotBlank;


Expand All @@ -22,6 +27,39 @@
@Validated
public interface IPaidNoticeController {
String X_FISCAL_CODE = "x-fiscal-code";
String X_CONTINUATION_TOKEN = "x-continuation-token";
String PAGE_SIZE = "size";

/**
* recovers biz-event data for the paid notices list
*
* @param fiscalCode tokenized user fiscal code
* @param continuationToken continuation token for paginated query
* @param size optional parameter defining page size, defaults to 10
* @return the paid notices list
*/
@GetMapping
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Obtained paid notices list.",
headers = @Header(name = X_CONTINUATION_TOKEN, description = "continuation token for paginated query", schema = @Schema(type = "string")),
content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(implementation = NoticeListWrapResponse.class))),
@ApiResponse(responseCode = "401", description = "Wrong or missing function key.", content = @Content(schema = @Schema())),
@ApiResponse(responseCode = "404", description = "Not found the fiscal code.", content = @Content(schema = @Schema(implementation = ProblemJson.class))),
@ApiResponse(responseCode = "429", description = "Too many requests.", content = @Content(schema = @Schema())),
@ApiResponse(responseCode = "500", description = "Service unavailable.", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(implementation = ProblemJson.class)))})
@Operation(summary = "Retrieve the paged transaction list from biz events.", description = "This operation is deprecated. Use Paid Notice APIs instead", security = {
@SecurityRequirement(name = "ApiKey")})
ResponseEntity<NoticeListWrapResponse> getPaidNotices(
@RequestHeader(name = X_FISCAL_CODE) String fiscalCode,
@RequestHeader(name = X_CONTINUATION_TOKEN, required = false) String continuationToken,
@RequestParam(name = PAGE_SIZE, required = false, defaultValue = "10") Integer size,
@Valid @Parameter(description = "Filter by payer") @RequestParam(value = "is_payer", required = false) Boolean isPayer,
@Valid @Parameter(description = "Filter by debtor") @RequestParam(value = "is_debtor", required = false) Boolean isDebtor,
@RequestParam(required = false, name = "orderby", defaultValue = "TRANSACTION_DATE") @Parameter(description = "Order by TRANSACTION_DATE") Order.TransactionListOrder orderBy,
@RequestParam(required = false, name = "ordering", defaultValue = "DESC") @Parameter(description = "Direction of ordering") Sort.Direction ordering);




@Operation(summary = "Disable the paid notice details given its id.", security = {
@SecurityRequirement(name = "ApiKey")}, operationId = "disablePaidNotice")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public interface ITransactionController {
String PAGE_NUMBER = "page";

/**
* @deprecated
* recovers biz-event data for the transaction list
*
* @param fiscalCode tokenized user fiscal code
Expand All @@ -60,8 +61,9 @@ public interface ITransactionController {
@ApiResponse(responseCode = "404", description = "Not found the transaction.", content = @Content(schema = @Schema(implementation = ProblemJson.class))),
@ApiResponse(responseCode = "429", description = "Too many requests.", content = @Content(schema = @Schema())),
@ApiResponse(responseCode = "500", description = "Service unavailable.", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(implementation = ProblemJson.class)))})
@Operation(summary = "Retrieve the paged transaction list from biz events.", security = {
@SecurityRequirement(name = "ApiKey")}, operationId = "getTransactionList")
@Operation(summary = "Retrieve the paged transaction list from biz events.", description = "This operation is deprecated. Use Paid Notice APIs instead", security = {
@SecurityRequirement(name = "ApiKey")}, deprecated = true, operationId = "getTransactionList")
@Deprecated(forRemoval = false)
ResponseEntity<TransactionListWrapResponse> getTransactionList(
@RequestHeader(name = X_FISCAL_CODE) String fiscalCode,
@Valid @Parameter(description = "Filter by payer") @RequestParam(value = "is_payer", required = false) Boolean isPayer,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
package it.gov.pagopa.bizeventsservice.controller.impl;

import it.gov.pagopa.bizeventsservice.controller.IPaidNoticeController;
import it.gov.pagopa.bizeventsservice.model.filterandorder.Order;
import it.gov.pagopa.bizeventsservice.model.response.paidnotice.NoticeListItem;
import it.gov.pagopa.bizeventsservice.model.response.paidnotice.NoticeListWrapResponse;
import it.gov.pagopa.bizeventsservice.model.response.transaction.TransactionListResponse;
import it.gov.pagopa.bizeventsservice.service.ITransactionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
* Implementation of {@link IPaidNoticeController} that contains the Rest Controller
* for events services
Expand All @@ -22,6 +29,36 @@ public PaidNoticeController(ITransactionService transactionService) {
}


@Override
public ResponseEntity<NoticeListWrapResponse> getPaidNotices(String fiscalCode,
String continuationToken,
Integer size,
Boolean isPayer,
Boolean isDebtor,
Order.TransactionListOrder orderBy,
Sort.Direction ordering) {
TransactionListResponse transactionListResponse = transactionService.getTransactionList(fiscalCode, isPayer, isDebtor,
continuationToken, size, orderBy, ordering);


List<NoticeListItem> noticeListItemList = transactionListResponse.getTransactionList().stream()
.map(elem -> NoticeListItem.builder()
.eventId(elem.getTransactionId())
.payeeName(elem.getPayeeName())
.payeeTaxCode(elem.getPayeeTaxCode())
.amount(elem.getAmount())
.noticeDate(elem.getTransactionDate())
.isCart(elem.getIsCart())
.isPayer(elem.getIsPayer())
.isDebtor(elem.getIsDebtor())
.build())
.toList();

return ResponseEntity.ok()
.header(X_CONTINUATION_TOKEN, transactionListResponse.getContinuationToken())
.body(NoticeListWrapResponse.builder().notices(noticeListItemList).build());
}

@Override
public ResponseEntity<Void> disablePaidNotice(String fiscalCode, String transactionId) {
transactionService.disableTransaction(fiscalCode, transactionId);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package it.gov.pagopa.bizeventsservice.model.response.paidnotice;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;

import javax.validation.constraints.NotNull;
import java.io.Serializable;

/**
* Response model for transaction list API
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@JsonInclude(Include.NON_NULL)
public class NoticeListItem implements Serializable {

@Schema(required = true)
@NotNull
private String eventId;

private String payeeName;

@Schema(required = true)
@NotNull
private String payeeTaxCode;

@Schema(required = true)
@NotNull
private String amount;

@Schema(required = true)
@NotNull
private String noticeDate;

@Schema(required = true)
@NotNull
private Boolean isCart;

@Schema(required = true)
@NotNull
private Boolean isPayer;

@Schema(required = true)
@NotNull
@Builder.Default
private Boolean isDebtor = false;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package it.gov.pagopa.bizeventsservice.model.response.paidnotice;

import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder;
import lombok.Data;

import javax.validation.constraints.NotNull;
import java.util.List;

@Builder
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class NoticeListWrapResponse {

@Schema(required = true)
@NotNull
private List<NoticeListItem> notices;
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
package it.gov.pagopa.bizeventsservice.controller;


import com.fasterxml.jackson.core.type.TypeReference;
import it.gov.pagopa.bizeventsservice.client.IReceiptGeneratePDFClient;
import it.gov.pagopa.bizeventsservice.client.IReceiptGetPDFClient;
import it.gov.pagopa.bizeventsservice.exception.AppError;
import it.gov.pagopa.bizeventsservice.exception.AppException;
import it.gov.pagopa.bizeventsservice.model.response.Attachment;
import it.gov.pagopa.bizeventsservice.model.response.AttachmentsDetailsResponse;
import it.gov.pagopa.bizeventsservice.model.response.transaction.TransactionDetailResponse;
import it.gov.pagopa.bizeventsservice.model.response.transaction.TransactionListItem;
import it.gov.pagopa.bizeventsservice.model.response.transaction.TransactionListResponse;
Expand All @@ -16,27 +21,45 @@
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest
@AutoConfigureMockMvc
public class PaidNoticeControllerTest {
public class NoticeListItemControllerTest {
jacopocarlini marked this conversation as resolved.
Show resolved Hide resolved

public static final String INVALID_FISCAL_CODE = "INVALID_TX_FISCAL_CODE";
public static final String VALID_FISCAL_CODE = "AAAAAA00A00A000A";
public static final String FISCAL_CODE_HEADER_KEY = "x-fiscal-code";
public static final String PAIDS_EVENT_ID_DISABLE_PATH = "/paids/1234321234/disable";
public static final String PAIDS_PATH = "/paids";
public static final String SIZE = "10";
public static final String SIZE_HEADER_KEY = "size";
private static final String CONTINUATION_TOKEN_HEADER_KEY = "x-continuation-token";
public static final String CONTINUATION_TOKEN = "continuationToken";


@Autowired
private MockMvc mvc;

@MockBean
private IReceiptGetPDFClient receiptClient;

@MockBean
private IReceiptGeneratePDFClient generateReceiptClient;

@MockBean
private ITransactionService transactionService;

Expand All @@ -46,13 +69,53 @@ public class PaidNoticeControllerTest {
@BeforeEach
void setUp() throws IOException {
// precondition
List<TransactionListItem> transactionListItems = Utility.readModelFromFile("biz-events/getTransactionList.json", List.class);
List<TransactionListItem> transactionListItems = Utility.readModelFromFile("biz-events/getTransactionList.json", new TypeReference<List<TransactionListItem>>(){});
TransactionListResponse transactionListResponse = TransactionListResponse.builder().transactionList(transactionListItems).build();
TransactionDetailResponse transactionDetailResponse = Utility.readModelFromFile("biz-events/transactionDetails.json", TransactionDetailResponse.class);
when(transactionService.getTransactionList(eq(VALID_FISCAL_CODE), any(), any(), anyString(), anyInt(), any(), any())).thenReturn(transactionListResponse);
when(transactionService.getCachedTransactionList(eq(VALID_FISCAL_CODE), any(), any(), anyInt(), anyInt(), any(), any())).thenReturn(transactionListResponse);
when(transactionService.getTransactionDetails(anyString(), anyString())).thenReturn(transactionDetailResponse);
when(transactionService.getPDFReceipt(anyString(), anyString())).thenReturn(receipt);
Attachment attachmentDetail = mock (Attachment.class);
AttachmentsDetailsResponse attachments = AttachmentsDetailsResponse.builder().attachments(Arrays.asList(attachmentDetail)).build();
when(receiptClient.getAttachments(anyString(), anyString())).thenReturn(attachments);
when(receiptClient.getReceipt(anyString(), anyString(), any())).thenReturn(receipt);
when(generateReceiptClient.generateReceipt(anyString(), anyString(), any())).thenReturn("OK");
}

@Test
void getPaidNoticesListShouldReturnData() throws Exception {
MvcResult result = mvc.perform(get(PAIDS_PATH)
.header(FISCAL_CODE_HEADER_KEY, VALID_FISCAL_CODE)
.header(CONTINUATION_TOKEN_HEADER_KEY, CONTINUATION_TOKEN)
.queryParam(SIZE_HEADER_KEY, SIZE)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andReturn();
assertNotNull(result.getResponse().getContentAsString());
assertTrue(result.getResponse().getContentAsString().contains("b77d4987-a3e4-48d4-a2fd-af504f8b79e9"));
assertTrue(result.getResponse().getContentAsString().contains("100.0"));
}

@Test
void getPaidNoticesListWithMissingFiscalCodeShouldReturnError() throws Exception {
mvc.perform(get(PAIDS_PATH)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isBadRequest())
.andReturn();
}

@Test
void getPaidNoticesListWithInvalidFiscalCodeShouldReturnError() throws Exception {
when(transactionService.getTransactionList(eq(INVALID_FISCAL_CODE), any(), any(), any(), anyInt(), any(), any())).thenAnswer(x -> {
throw new AppException(AppError.INVALID_FISCAL_CODE, INVALID_FISCAL_CODE);
});
mvc.perform(get(PAIDS_PATH)
.header(FISCAL_CODE_HEADER_KEY, INVALID_FISCAL_CODE)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isBadRequest())
.andReturn();
}

@Test
Expand Down
55 changes: 32 additions & 23 deletions src/test/java/it/gov/pagopa/bizeventsservice/util/Utility.java
Original file line number Diff line number Diff line change
@@ -1,36 +1,45 @@
package it.gov.pagopa.bizeventsservice.util;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Objects;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;

import lombok.experimental.UtilityClass;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Objects;

@UtilityClass
public class Utility {

public static <T> T readModelFromFile(String relativePath, Class<T> clazz) throws IOException {
ClassLoader classLoader = Utility.class.getClassLoader();
File file = new File(Objects.requireNonNull(classLoader.getResource(relativePath)).getPath());
var content = Files.readString(file.toPath());
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
return objectMapper.readValue(content, clazz);
}
public static <T> T readModelFromFile(String relativePath, Class<T> clazz) throws IOException {
ClassLoader classLoader = Utility.class.getClassLoader();
File file = new File(Objects.requireNonNull(classLoader.getResource(relativePath)).getPath());
var content = Files.readString(file.toPath());
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
return objectMapper.readValue(content, clazz);
}

public static <T> T readModelFromFile(String relativePath, TypeReference<T> typeReference) throws IOException {
ClassLoader classLoader = Utility.class.getClassLoader();
File file = new File(Objects.requireNonNull(classLoader.getResource(relativePath)).getPath());
var content = Files.readString(file.toPath());
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
return objectMapper.readValue(content, typeReference);
}

/**
* @param object to map into the Json string
* @return object as Json string
* @throws JsonProcessingException if there is an error during the parsing of
* the object
*/
public String toJson(Object object) throws JsonProcessingException {
return new ObjectMapper().writeValueAsString(object);
}
/**
* @param object to map into the Json string
* @return object as Json string
* @throws JsonProcessingException if there is an error during the parsing of
* the object
*/
public String toJson(Object object) throws JsonProcessingException {
return new ObjectMapper().writeValueAsString(object);
}

}
Loading