diff --git a/integration-test/src/features/organizationsreceipt.feature b/integration-test/src/features/organizationsreceipt.feature index 8fafec35..d44bc3f0 100644 --- a/integration-test/src/features/organizationsreceipt.feature +++ b/integration-test/src/features/organizationsreceipt.feature @@ -9,3 +9,17 @@ Feature: All about Organizations Receipt Then the organization gets the status code 200 And the details of the receipt are returned to the organization with receiptId "123456789" And the Biz-Event to test with id "123456789" is removed + + Scenario: An operator asks for a Biz-Event with fiscal code PA and iuv + Given Biz-Event to test with id "123456789" and save it on Cosmos DB + When the operator asks for a Biz-Event with fiscal code PA "fiscalCode-123456789" and iuv "iuv-123456789" + Then the operator gets the status code 200 + And the details of the Biz-Event are returned to the operator with id "123456789" + And the Biz-Event to test with id "123456789" is removed + + Scenario: An operator asks for a Biz-Event id + Given Biz-Event to test with id "123456789" and save it on Cosmos DB + When the operator asks for a Biz-Event with id "123456789" + Then the operator gets the status code 200 + And the details of the Biz-Event are returned to the operator with id "123456789" + And the Biz-Event to test with id "123456789" is removed diff --git a/integration-test/src/step_definitions/support/bizeventservice_client.js b/integration-test/src/step_definitions/support/bizeventservice_client.js index abe5fc1e..75d9c818 100644 --- a/integration-test/src/step_definitions/support/bizeventservice_client.js +++ b/integration-test/src/step_definitions/support/bizeventservice_client.js @@ -10,7 +10,17 @@ function getOrganizationReceipt(organizationfiscalcode, iur, iuv) { return get(bizevents_service_host+`organizations/${organizationfiscalcode}/receipts/${iur}/paymentoptions/${iuv}`, {}) } +function getBizEventById(id) { + return get(bizevents_service_host+`events/${id}`, {}) +} + +function getBizEventByOrgFiscalCodeAndIuv(organizationfiscalcode, iuv) { + return get(bizevents_service_host+`events/organizations/${organizationfiscalcode}/iuvs/${iuv}`, {}) +} + module.exports = { healthCheckInfo, - getOrganizationReceipt + getOrganizationReceipt, + getBizEventById, + getBizEventByOrgFiscalCodeAndIuv } diff --git a/integration-test/src/step_definitions/support/common.js b/integration-test/src/step_definitions/support/common.js index 3d22da0c..bd76cf75 100644 --- a/integration-test/src/step_definitions/support/common.js +++ b/integration-test/src/step_definitions/support/common.js @@ -1,7 +1,7 @@ const axios = require("axios"); const cryptojs = require("crypto-js"); -axios.defaults.headers.common['Ocp-Apim-Subscription-Key'] = process.env.SUBKEY // for all requests +axios.defaults.headers.common['Ocp-Apim-Subscription-Key'] = process.env.SUBKEY || "";// for all requests if (process.env.CANARY) { axios.defaults.headers.common['X-Canary'] = 'canary' // for all requests } diff --git a/integration-test/src/step_definitions/support/organizationsreceipt_steps.js b/integration-test/src/step_definitions/support/organizationsreceipt_steps.js index def661f7..867de861 100644 --- a/integration-test/src/step_definitions/support/organizationsreceipt_steps.js +++ b/integration-test/src/step_definitions/support/organizationsreceipt_steps.js @@ -1,13 +1,22 @@ const assert = require('assert') -const {Given, When, Then, setDefaultTimeout} = require('@cucumber/cucumber') -const {getOrganizationReceipt} = require("./bizeventservice_client"); +const {Given, When, Then, setDefaultTimeout, After} = require('@cucumber/cucumber') +const {getOrganizationReceipt, getBizEventById, getBizEventByOrgFiscalCodeAndIuv} = require("./bizeventservice_client"); const {createDocument, deleteDocument} = require("./cosmosdb_client"); let responseToCheck; let receipt; +let bizEvent; setDefaultTimeout(360 * 1000); +// After each Scenario +After(async function () { + // remove event + responseToCheck = null; + receipt = null; + bizEvent = null; +}); + // Given Given('Biz-Event to test with id {string} and save it on Cosmos DB', async function (id) { @@ -27,6 +36,17 @@ When('the organization asks for a receipt with fiscal code PA {string} and iur { receipt = responseToCheck.data; }); +When('the operator asks for a Biz-Event with fiscal code PA {string} and iuv {string}', async function (organizationFiscalCode, iuv) { + responseToCheck = await getBizEventByOrgFiscalCodeAndIuv(organizationFiscalCode, iuv); + // save data + bizEvent = responseToCheck.data; +}); + +When('the operator asks for a Biz-Event with id {string}', async function (id) { + responseToCheck = await getBizEventById(id); + // save data + bizEvent = responseToCheck.data; +}); // Then @@ -35,12 +55,19 @@ Then('the organization gets the status code {int}', async function (status) { }); Then('the details of the receipt are returned to the organization with receiptId {string}', async function (receiptId) { - assert.strictEqual(receipt.receiptId, receiptId); + assert.strictEqual(receipt.receiptId, receiptId); }); Then('the Biz-Event to test with id {string} is removed', async function (id) { - //post condition - delete test data + //post condition - delete test data responseToCheck = await deleteDocument(id); assert.strictEqual(responseToCheck.status, 204); }); +Then('the operator gets the status code {int}', async function (status) { + assert.strictEqual(responseToCheck.status, status); +}); + +Then('the details of the Biz-Event are returned to the operator with id {string}', async function (id) { + assert.strictEqual(bizEvent.id, id); +}); diff --git a/openapi/openapi.json b/openapi/openapi.json index 16cf973a..aa469989 100644 --- a/openapi/openapi.json +++ b/openapi/openapi.json @@ -237,6 +237,277 @@ } ] }, + "/events/organizations/{organization-fiscal-code}/iuvs/{iuv}": { + "get": { + "tags": [ + "get BizEvent APIs" + ], + "summary": "Retrieve the biz-event given the organization fiscal code and IUV.", + "operationId": "getBizEventByOrganizationFiscalCodeAndIuv", + "parameters": [ + { + "name": "organization-fiscal-code", + "in": "path", + "description": "The fiscal code of the Organization.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "iuv", + "in": "path", + "description": "The unique payment identification. Alphanumeric code that uniquely associates and identifies three key elements of a payment: reason, payer, amount", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Obtained biz-event.", + "headers": { + "X-Request-Id": { + "description": "This header identifies the call", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BizEvent" + } + } + } + }, + "401": { + "description": "Wrong or missing function key.", + "headers": { + "X-Request-Id": { + "description": "This header identifies the call", + "schema": { + "type": "string" + } + } + } + }, + "404": { + "description": "Not found the biz-event.", + "headers": { + "X-Request-Id": { + "description": "This header identifies the call", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemJson" + } + } + } + }, + "422": { + "description": "Unable to process the request.", + "headers": { + "X-Request-Id": { + "description": "This header identifies the call", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemJson" + } + } + } + }, + "429": { + "description": "Too many requests.", + "headers": { + "X-Request-Id": { + "description": "This header identifies the call", + "schema": { + "type": "string" + } + } + } + }, + "500": { + "description": "Service unavailable.", + "headers": { + "X-Request-Id": { + "description": "This header identifies the call", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemJson" + } + } + } + } + }, + "security": [ + { + "ApiKey": [] + } + ] + }, + "parameters": [ + { + "name": "X-Request-Id", + "in": "header", + "description": "This header identifies the call, if not passed it is self-generated. This ID is returned in the response.", + "schema": { + "type": "string" + } + } + ] + }, + "/events/{biz-event-id}": { + "get": { + "tags": [ + "get BizEvent APIs" + ], + "summary": "Retrieve the biz-event given its id.", + "operationId": "getBizEvent", + "parameters": [ + { + "name": "biz-event-id", + "in": "path", + "description": "The id of the biz-event.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Obtained biz-event.", + "headers": { + "X-Request-Id": { + "description": "This header identifies the call", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BizEvent" + } + } + } + }, + "401": { + "description": "Wrong or missing function key.", + "headers": { + "X-Request-Id": { + "description": "This header identifies the call", + "schema": { + "type": "string" + } + } + } + }, + "404": { + "description": "Not found the biz-event.", + "headers": { + "X-Request-Id": { + "description": "This header identifies the call", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemJson" + } + } + } + }, + "422": { + "description": "Unable to process the request.", + "headers": { + "X-Request-Id": { + "description": "This header identifies the call", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemJson" + } + } + } + }, + "429": { + "description": "Too many requests.", + "headers": { + "X-Request-Id": { + "description": "This header identifies the call", + "schema": { + "type": "string" + } + } + } + }, + "500": { + "description": "Service unavailable.", + "headers": { + "X-Request-Id": { + "description": "This header identifies the call", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProblemJson" + } + } + } + } + }, + "security": [ + { + "ApiKey": [] + } + ] + }, + "parameters": [ + { + "name": "X-Request-Id", + "in": "header", + "description": "This header identifies the call, if not passed it is self-generated. This ID is returned in the response.", + "schema": { + "type": "string" + } + } + ] + }, "/info": { "get": { "tags": [ @@ -739,6 +1010,7 @@ "required": [ "fiscalCodePA", "iban", + "mbdAttachment", "remittanceInformation", "transferAmount", "transferCategory" @@ -791,6 +1063,416 @@ } } }, + "AuthRequest": { + "type": "object", + "properties": { + "authOutcome": { + "type": "string" + }, + "guid": { + "type": "string" + }, + "correlationId": { + "type": "string" + }, + "error": { + "type": "string" + }, + "auth_code": { + "type": "string" + } + } + }, + "BizEvent": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "version": { + "type": "string" + }, + "idPaymentManager": { + "type": "string" + }, + "complete": { + "type": "string" + }, + "receiptId": { + "type": "string" + }, + "missingInfo": { + "type": "array", + "items": { + "type": "string" + } + }, + "debtorPosition": { + "$ref": "#/components/schemas/DebtorPosition" + }, + "creditor": { + "$ref": "#/components/schemas/Creditor" + }, + "psp": { + "$ref": "#/components/schemas/Psp" + }, + "debtor": { + "$ref": "#/components/schemas/Debtor" + }, + "payer": { + "$ref": "#/components/schemas/Payer" + }, + "paymentInfo": { + "$ref": "#/components/schemas/PaymentInfo" + }, + "transferList": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Transfer" + } + }, + "transactionDetails": { + "$ref": "#/components/schemas/TransactionDetails" + }, + "eventStatus": { + "type": "string", + "enum": [ + "NA", + "RETRY", + "FAILED", + "DONE" + ] + }, + "eventRetryEnrichmentCount": { + "type": "integer", + "format": "int32" + } + } + }, + "Creditor": { + "type": "object", + "properties": { + "idPA": { + "type": "string" + }, + "idBrokerPA": { + "type": "string" + }, + "idStation": { + "type": "string" + }, + "companyName": { + "type": "string" + }, + "officeName": { + "type": "string" + } + } + }, + "DebtorPosition": { + "type": "object", + "properties": { + "modelType": { + "type": "string" + }, + "noticeNumber": { + "type": "string" + }, + "iuv": { + "type": "string" + } + } + }, + "Details": { + "type": "object", + "properties": { + "blurredNumber": { + "type": "string" + }, + "holder": { + "type": "string" + }, + "circuit": { + "type": "string" + } + } + }, + "Info": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "blurredNumber": { + "type": "string" + }, + "holder": { + "type": "string" + }, + "expireMonth": { + "type": "string" + }, + "expireYear": { + "type": "string" + }, + "brand": { + "type": "string" + }, + "issuerAbi": { + "type": "string" + }, + "issuerName": { + "type": "string" + }, + "label": { + "type": "string" + } + } + }, + "MBD": { + "type": "object", + "properties": { + "IUBD": { + "type": "string" + }, + "oraAcquisto": { + "type": "string" + }, + "importo": { + "type": "string" + }, + "tipoBollo": { + "type": "string" + }, + "MBDAttachment": { + "type": "string" + } + } + }, + "PaymentAuthorizationRequest": { + "type": "object", + "properties": { + "authOutcome": { + "type": "string" + }, + "requestId": { + "type": "string" + }, + "correlationId": { + "type": "string" + }, + "authCode": { + "type": "string" + }, + "paymentMethodType": { + "type": "string" + }, + "details": { + "$ref": "#/components/schemas/Details" + } + } + }, + "PaymentInfo": { + "type": "object", + "properties": { + "paymentDateTime": { + "type": "string" + }, + "applicationDate": { + "type": "string" + }, + "transferDate": { + "type": "string" + }, + "dueDate": { + "type": "string" + }, + "paymentToken": { + "type": "string" + }, + "amount": { + "type": "string" + }, + "fee": { + "type": "string" + }, + "primaryCiIncurredFee": { + "type": "string" + }, + "idBundle": { + "type": "string" + }, + "idCiBundle": { + "type": "string" + }, + "totalNotice": { + "type": "string" + }, + "paymentMethod": { + "type": "string" + }, + "touchpoint": { + "type": "string" + }, + "remittanceInformation": { + "type": "string" + }, + "description": { + "type": "string" + }, + "metadata": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MapEntry" + } + } + } + }, + "Psp": { + "type": "object", + "properties": { + "idPsp": { + "type": "string" + }, + "idBrokerPsp": { + "type": "string" + }, + "idChannel": { + "type": "string" + }, + "psp": { + "type": "string" + }, + "pspPartitaIVA": { + "type": "string" + }, + "pspFiscalCode": { + "type": "string" + }, + "channelDescription": { + "type": "string" + } + } + }, + "TransactionDetails": { + "type": "object", + "properties": { + "user": { + "$ref": "#/components/schemas/User" + }, + "paymentAuthorizationRequest": { + "$ref": "#/components/schemas/PaymentAuthorizationRequest" + }, + "wallet": { + "$ref": "#/components/schemas/WalletItem" + } + } + }, + "Transfer": { + "type": "object", + "properties": { + "idTransfer": { + "type": "string" + }, + "fiscalCodePA": { + "type": "string" + }, + "companyName": { + "type": "string" + }, + "amount": { + "type": "string" + }, + "transferCategory": { + "type": "string" + }, + "remittanceInformation": { + "type": "string" + }, + "metadata": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MapEntry" + } + }, + "IBAN": { + "type": "string" + }, + "MBD": { + "$ref": "#/components/schemas/MBD" + } + } + }, + "User": { + "type": "object", + "properties": { + "fullName": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "F", + "G" + ] + }, + "fiscalCode": { + "type": "string" + }, + "notificationEmail": { + "type": "string" + }, + "userId": { + "type": "string" + }, + "userStatus": { + "type": "string" + }, + "userStatusDescription": { + "type": "string" + } + } + }, + "WalletItem": { + "type": "object", + "properties": { + "idWallet": { + "type": "string" + }, + "walletType": { + "type": "string", + "enum": [ + "CARD", + "PAYPAL", + "BANCOMATPAY" + ] + }, + "enableableFunctions": { + "type": "array", + "items": { + "type": "string" + } + }, + "pagoPa": { + "type": "boolean" + }, + "onboardingChannel": { + "type": "string" + }, + "favourite": { + "type": "boolean" + }, + "createDate": { + "type": "string" + }, + "info": { + "$ref": "#/components/schemas/Info" + }, + "authRequest": { + "$ref": "#/components/schemas/AuthRequest" + } + } + }, "Link": { "type": "object", "properties": { @@ -812,4 +1494,4 @@ } } } -} +} \ No newline at end of file diff --git a/src/main/java/it/gov/pagopa/bizeventsservice/controller/IBizEventController.java b/src/main/java/it/gov/pagopa/bizeventsservice/controller/IBizEventController.java new file mode 100644 index 00000000..f6b42ac8 --- /dev/null +++ b/src/main/java/it/gov/pagopa/bizeventsservice/controller/IBizEventController.java @@ -0,0 +1,54 @@ +package it.gov.pagopa.bizeventsservice.controller; + +import javax.validation.constraints.NotBlank; + +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +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.entity.BizEvent; +import it.gov.pagopa.bizeventsservice.model.ProblemJson; + +@Tag(name = "get BizEvent APIs") +@RequestMapping +@Validated +public interface IBizEventController { + + @Operation(summary = "Retrieve the biz-event given its id.", security = { + @SecurityRequirement(name = "ApiKey") }, operationId = "getBizEvent") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Obtained biz-event.", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(name = "BizEvent", implementation = BizEvent.class))), + @ApiResponse(responseCode = "401", description = "Wrong or missing function key.", content = @Content(schema = @Schema())), + @ApiResponse(responseCode = "404", description = "Not found the biz-event.", content = @Content(schema = @Schema(implementation = ProblemJson.class))), + @ApiResponse(responseCode = "422", description = "Unable to process the request.", 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))) }) + @GetMapping(value = "/events/{biz-event-id}", produces = MediaType.APPLICATION_JSON_VALUE) + ResponseEntity getBizEvent( + @Parameter(description = "The id of the biz-event.", required = true) @NotBlank @PathVariable("biz-event-id") String bizEventId); + + @Operation(summary = "Retrieve the biz-event given the organization fiscal code and IUV.", security = { + @SecurityRequirement(name = "ApiKey") }, operationId = "getBizEventByOrganizationFiscalCodeAndIuv") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Obtained biz-event.", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(name = "BizEvent", implementation = BizEvent.class))), + @ApiResponse(responseCode = "401", description = "Wrong or missing function key.", content = @Content(schema = @Schema())), + @ApiResponse(responseCode = "404", description = "Not found the biz-event.", content = @Content(schema = @Schema(implementation = ProblemJson.class))), + @ApiResponse(responseCode = "422", description = "Unable to process the request.", 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))) }) + @GetMapping(value = "/events/organizations/{organization-fiscal-code}/iuvs/{iuv}", produces = MediaType.APPLICATION_JSON_VALUE) + ResponseEntity getBizEventByOrganizationFiscalCodeAndIuv( + @Parameter(description = "The fiscal code of the Organization.", required = true) @NotBlank @PathVariable("organization-fiscal-code") String organizationFiscalCode, + @Parameter(description = "The unique payment identification. Alphanumeric code that uniquely associates and identifies three key elements of a payment: reason, payer, amount", required = true) @NotBlank @PathVariable("iuv") String iuv); +} diff --git a/src/main/java/it/gov/pagopa/bizeventsservice/controller/impl/BizEventController.java b/src/main/java/it/gov/pagopa/bizeventsservice/controller/impl/BizEventController.java new file mode 100644 index 00000000..f27e5eab --- /dev/null +++ b/src/main/java/it/gov/pagopa/bizeventsservice/controller/impl/BizEventController.java @@ -0,0 +1,35 @@ +package it.gov.pagopa.bizeventsservice.controller.impl; + +import javax.validation.constraints.NotBlank; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RestController; + +import it.gov.pagopa.bizeventsservice.controller.IBizEventController; +import it.gov.pagopa.bizeventsservice.entity.BizEvent; +import it.gov.pagopa.bizeventsservice.service.IBizEventsService; + +@RestController +public class BizEventController implements IBizEventController { + + private final IBizEventsService bizEventsService; + + @Autowired + public BizEventController(IBizEventsService bizEventsService) { + this.bizEventsService = bizEventsService; + } + + @Override + public ResponseEntity getBizEvent(@NotBlank String bizEventId) { + return new ResponseEntity<>(bizEventsService.getBizEvent(bizEventId), HttpStatus.OK); + } + + @Override + public ResponseEntity getBizEventByOrganizationFiscalCodeAndIuv( + @NotBlank String organizationFiscalCode, @NotBlank String iuv) { + return new ResponseEntity<>(bizEventsService.getBizEventByOrgFiscalCodeAndIuv(organizationFiscalCode, iuv), + HttpStatus.OK); + } +} diff --git a/src/main/java/it/gov/pagopa/bizeventsservice/controller/impl/PaymentsController.java b/src/main/java/it/gov/pagopa/bizeventsservice/controller/impl/PaymentsController.java index be0a428b..6bf7a636 100644 --- a/src/main/java/it/gov/pagopa/bizeventsservice/controller/impl/PaymentsController.java +++ b/src/main/java/it/gov/pagopa/bizeventsservice/controller/impl/PaymentsController.java @@ -9,13 +9,13 @@ import it.gov.pagopa.bizeventsservice.controller.IPaymentsController; import it.gov.pagopa.bizeventsservice.model.response.CtReceiptModelResponse; -import it.gov.pagopa.bizeventsservice.service.BizEventsService; +import it.gov.pagopa.bizeventsservice.service.IBizEventsService; @RestController public class PaymentsController implements IPaymentsController { @Autowired - private BizEventsService bizEventsService; + private IBizEventsService bizEventsService; @Override public ResponseEntity getOrganizationReceipt(@NotBlank String organizationFiscalCode, diff --git a/src/main/java/it/gov/pagopa/bizeventsservice/exception/AppError.java b/src/main/java/it/gov/pagopa/bizeventsservice/exception/AppError.java index c3d8af07..fcf51a58 100644 --- a/src/main/java/it/gov/pagopa/bizeventsservice/exception/AppError.java +++ b/src/main/java/it/gov/pagopa/bizeventsservice/exception/AppError.java @@ -7,7 +7,10 @@ @Getter public enum AppError { BIZ_EVENT_NOT_FOUND(HttpStatus.NOT_FOUND, "Biz Event not found", "Not found a biz event for the Organization Fiscal Code %s and IUR %s and IUV %s"), + BIZ_EVENT_NOT_FOUND_WITH_ID(HttpStatus.NOT_FOUND, "Biz Event not found", "Not found a biz event with id %s"), + BIZ_EVENT_NOT_FOUND_WITH_ORG_CF_AND_IUV(HttpStatus.NOT_FOUND, "Biz Event not found", "Not found a biz event for the Organization Fiscal Code %s and IUV %s"), BIZ_EVENT_NOT_UNIQUE(HttpStatus.UNPROCESSABLE_ENTITY, "Biz Event is not unique", "More than one biz event was found for the Organization Fiscal Code %s and IUR %s and IUV %s"), + BIZ_EVENT_NOT_UNIQUE_WITH_ORG_CF_AND_IUV(HttpStatus.UNPROCESSABLE_ENTITY, "Biz Event is not unique", "More than one biz event was found for the Organization Fiscal Code %s and IUV %s"), INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "Internal Server Error", "Something was wrong"); public final HttpStatus httpStatus; diff --git a/src/main/java/it/gov/pagopa/bizeventsservice/repository/BizEventsRepository.java b/src/main/java/it/gov/pagopa/bizeventsservice/repository/BizEventsRepository.java index 9d155291..4666896c 100644 --- a/src/main/java/it/gov/pagopa/bizeventsservice/repository/BizEventsRepository.java +++ b/src/main/java/it/gov/pagopa/bizeventsservice/repository/BizEventsRepository.java @@ -13,8 +13,12 @@ @Repository public interface BizEventsRepository extends CosmosRepository { - // TODO when available replace idPa with the paFiscalCode field + // TODO when available replace idPa with the paFiscalCode field @Query("select * from c where c.creditor.idPA = @organizationFiscalCode and c.paymentInfo.paymentToken = @iur and c.debtorPosition.iuv = @iuv and StringToNumber(c.debtorPosition.modelType) > 1") - List getBizEventByOrgFiscCodeAndIur(@Param("organizationFiscalCode") String organizationFiscalCode, @Param("iur") String iur, @Param("iuv") String iuv); + List getBizEventByOrgFiscCodeAndIur(@Param("organizationFiscalCode") String organizationFiscalCode, + @Param("iur") String iur, @Param("iuv") String iuv); + @Query("select * from c where c.creditor.idPA = @organizationFiscalCode and c.debtorPosition.iuv = @iuv") + List getBizEventByOrgFiscalCodeAndIuv(@Param("organizationFiscalCode") String organizationFiscalCode, + @Param("iuv") String iuv); } diff --git a/src/main/java/it/gov/pagopa/bizeventsservice/service/BizEventsService.java b/src/main/java/it/gov/pagopa/bizeventsservice/service/BizEventsService.java deleted file mode 100644 index 70be1dbf..00000000 --- a/src/main/java/it/gov/pagopa/bizeventsservice/service/BizEventsService.java +++ /dev/null @@ -1,47 +0,0 @@ -package it.gov.pagopa.bizeventsservice.service; - -import java.util.List; - -import org.modelmapper.ModelMapper; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import it.gov.pagopa.bizeventsservice.entity.BizEvent; -import it.gov.pagopa.bizeventsservice.exception.AppError; -import it.gov.pagopa.bizeventsservice.exception.AppException; -import it.gov.pagopa.bizeventsservice.model.response.CtReceiptModelResponse; -import it.gov.pagopa.bizeventsservice.repository.BizEventsRepository; -import lombok.AllArgsConstructor; -import lombok.NoArgsConstructor; - -@Service -@AllArgsConstructor -@NoArgsConstructor -public class BizEventsService { - - @Autowired - private BizEventsRepository bizEventsRepository; - - @Autowired - private ModelMapper modelMapper; - - public CtReceiptModelResponse getOrganizationReceipt(String organizationFiscalCode, - String iur, String iuv) { - // get biz event - List bizEventEntityList = bizEventsRepository.getBizEventByOrgFiscCodeAndIur(organizationFiscalCode, iur, iuv); - - if (bizEventEntityList.isEmpty()) { - throw new AppException(AppError.BIZ_EVENT_NOT_FOUND, organizationFiscalCode, iur, iuv); - } - // the query should always return only one element - else if (bizEventEntityList.size() > 1) { - throw new AppException(AppError.BIZ_EVENT_NOT_UNIQUE, organizationFiscalCode, iur, iuv); - } - - // the bizEventEntityList has only one element - return modelMapper.map(bizEventEntityList.get(0), CtReceiptModelResponse.class); - } - - - -} diff --git a/src/main/java/it/gov/pagopa/bizeventsservice/service/IBizEventsService.java b/src/main/java/it/gov/pagopa/bizeventsservice/service/IBizEventsService.java new file mode 100644 index 00000000..6a908948 --- /dev/null +++ b/src/main/java/it/gov/pagopa/bizeventsservice/service/IBizEventsService.java @@ -0,0 +1,16 @@ +package it.gov.pagopa.bizeventsservice.service; + +import it.gov.pagopa.bizeventsservice.entity.BizEvent; +import it.gov.pagopa.bizeventsservice.model.response.CtReceiptModelResponse; + +public interface IBizEventsService { + + CtReceiptModelResponse getOrganizationReceipt(String organizationFiscalCode, + String iur, String iuv); + + BizEvent getBizEvent(String id); + + BizEvent getBizEventByOrgFiscalCodeAndIuv(String organizationFiscalCode, + String iuv); + +} \ No newline at end of file diff --git a/src/main/java/it/gov/pagopa/bizeventsservice/service/impl/BizEventsService.java b/src/main/java/it/gov/pagopa/bizeventsservice/service/impl/BizEventsService.java new file mode 100644 index 00000000..b1cb0411 --- /dev/null +++ b/src/main/java/it/gov/pagopa/bizeventsservice/service/impl/BizEventsService.java @@ -0,0 +1,75 @@ +package it.gov.pagopa.bizeventsservice.service.impl; + +import com.azure.cosmos.models.PartitionKey; +import it.gov.pagopa.bizeventsservice.entity.BizEvent; +import it.gov.pagopa.bizeventsservice.exception.AppError; +import it.gov.pagopa.bizeventsservice.exception.AppException; +import it.gov.pagopa.bizeventsservice.model.response.CtReceiptModelResponse; +import it.gov.pagopa.bizeventsservice.repository.BizEventsRepository; +import it.gov.pagopa.bizeventsservice.service.IBizEventsService; +import org.modelmapper.ModelMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Optional; + +@Service +public class BizEventsService implements IBizEventsService { + + private final BizEventsRepository bizEventsRepository; + + private final ModelMapper modelMapper; + + @Autowired + public BizEventsService(BizEventsRepository bizEventsRepository, ModelMapper modelMapper) { + this.bizEventsRepository = bizEventsRepository; + this.modelMapper = modelMapper; + } + + @Override + public CtReceiptModelResponse getOrganizationReceipt(String organizationFiscalCode, + String iur, String iuv) { + // get biz event + List bizEventEntityList = bizEventsRepository.getBizEventByOrgFiscCodeAndIur(organizationFiscalCode, + iur, iuv); + + if (bizEventEntityList.isEmpty()) { + throw new AppException(AppError.BIZ_EVENT_NOT_FOUND, organizationFiscalCode, iur, iuv); + } + // the query should always return only one element + else if (bizEventEntityList.size() > 1) { + throw new AppException(AppError.BIZ_EVENT_NOT_UNIQUE, organizationFiscalCode, iur, iuv); + } + + // the bizEventEntityList has only one element + return modelMapper.map(bizEventEntityList.get(0), CtReceiptModelResponse.class); + } + + @Override + public BizEvent getBizEvent(String id) { + // get biz event + Optional optionalBizEvent = bizEventsRepository.findById(id, new PartitionKey(id)); + + if (optionalBizEvent.isEmpty()) { + throw new AppException(AppError.BIZ_EVENT_NOT_FOUND_WITH_ID, id); + } + return optionalBizEvent.get(); + } + + @Override + public BizEvent getBizEventByOrgFiscalCodeAndIuv(String organizationFiscalCode, String iuv) { + // get biz event + List bizEventEntityList = bizEventsRepository.getBizEventByOrgFiscalCodeAndIuv(organizationFiscalCode, + iuv); + + if (bizEventEntityList.isEmpty()) { + throw new AppException(AppError.BIZ_EVENT_NOT_FOUND_WITH_ORG_CF_AND_IUV, organizationFiscalCode, iuv); + } + // the query should always return only one element + else if (bizEventEntityList.size() > 1) { + throw new AppException(AppError.BIZ_EVENT_NOT_UNIQUE_WITH_ORG_CF_AND_IUV, organizationFiscalCode, iuv); + } + return bizEventEntityList.get(0); + } +} diff --git a/src/test/java/it/gov/pagopa/bizeventsservice/controller/PaymentsControllerTest.java b/src/test/java/it/gov/pagopa/bizeventsservice/controller/PaymentsControllerTest.java index 9bdee2fc..4dd86783 100644 --- a/src/test/java/it/gov/pagopa/bizeventsservice/controller/PaymentsControllerTest.java +++ b/src/test/java/it/gov/pagopa/bizeventsservice/controller/PaymentsControllerTest.java @@ -21,7 +21,7 @@ import org.springframework.test.web.servlet.MvcResult; import it.gov.pagopa.bizeventsservice.model.response.CtReceiptModelResponse; -import it.gov.pagopa.bizeventsservice.service.BizEventsService; +import it.gov.pagopa.bizeventsservice.service.IBizEventsService; import it.gov.pagopa.bizeventsservice.util.TestUtil; @SpringBootTest @@ -32,7 +32,7 @@ class PaymentsControllerTest { private MockMvc mvc; @MockBean - private BizEventsService bizEventsService; + private IBizEventsService bizEventsService; @BeforeEach void setUp() throws IOException { diff --git a/src/test/java/it/gov/pagopa/bizeventsservice/service/BizEventsServiceTest.java b/src/test/java/it/gov/pagopa/bizeventsservice/service/BizEventsServiceTest.java index c6707b49..9ebfe15e 100644 --- a/src/test/java/it/gov/pagopa/bizeventsservice/service/BizEventsServiceTest.java +++ b/src/test/java/it/gov/pagopa/bizeventsservice/service/BizEventsServiceTest.java @@ -1,15 +1,18 @@ package it.gov.pagopa.bizeventsservice.service; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import java.io.IOException; +import java.util.Collections; import java.util.List; +import java.util.Optional; +import com.azure.cosmos.models.PartitionKey; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; @@ -24,6 +27,7 @@ import it.gov.pagopa.bizeventsservice.exception.AppException; import it.gov.pagopa.bizeventsservice.model.response.CtReceiptModelResponse; import it.gov.pagopa.bizeventsservice.repository.BizEventsRepository; +import it.gov.pagopa.bizeventsservice.service.impl.BizEventsService; import it.gov.pagopa.bizeventsservice.util.TestUtil; @TestMethodOrder(OrderAnnotation.class) @@ -31,69 +35,108 @@ @SpringBootTest class BizEventsServiceTest { + private static final String ORGANIZATION_FISCAL_CODE = "66666666666"; + private static final String IUV = "112006686812600"; + private static final String BIZ_EVENT_ID = "b77d4987-a3e4-48d4-a2fd-af504f8b79e9"; + private static final String IUR = "CCD01"; - @Mock + @Mock private BizEventsRepository bizEventsRepository; - - @Autowired + + @Autowired private ModelMapper modelMapper; - private static BizEventsService bizEventsService; + private IBizEventsService bizEventsService; + + private BizEvent bizEventEntity; + + private BizEvent bizEventEntityDuplicated; + + @BeforeAll + public void beforeAll() throws IOException { + bizEventEntity = TestUtil.readModelFromFile("biz-events/bizEvent.json", BizEvent.class); + bizEventEntityDuplicated = TestUtil.readModelFromFile("biz-events/bizEvent_duplicate.json", BizEvent.class); + } + + @BeforeEach + void setUp() { + bizEventsService = spy(new BizEventsService(bizEventsRepository, modelMapper)); + } + + @Test + void getOrganizationReceipt() { + when(bizEventsRepository.getBizEventByOrgFiscCodeAndIur(ORGANIZATION_FISCAL_CODE, IUR, IUV)) + .thenReturn(List.of(bizEventEntity)); + + CtReceiptModelResponse ctReceipt = bizEventsService.getOrganizationReceipt(ORGANIZATION_FISCAL_CODE, IUR, + IUV); + assertEquals("85570ffebb13411b80d79f415641ec55", ctReceipt.getReceiptId()); + assertEquals("cash", ctReceipt.getPaymentMethod()); + } + + @Test + void getOrganizationReceipt_404() throws IOException { + when(bizEventsRepository.getBizEventByOrgFiscCodeAndIur(ORGANIZATION_FISCAL_CODE, IUR, IUV)) + .thenReturn(List.of(bizEventEntity)); + + AppException e = assertThrows(AppException.class, () -> bizEventsService.getOrganizationReceipt(ORGANIZATION_FISCAL_CODE, IUR, "fake_iuv")); + assertEquals(HttpStatus.NOT_FOUND, e.getHttpStatus()); + } - private BizEvent bizEventEntity; + @Test + void getOrganizationReceipt_422() throws IOException { + // mocking a fake save for duplicated entity + when(bizEventsRepository.getBizEventByOrgFiscCodeAndIur(ORGANIZATION_FISCAL_CODE, IUR, IUV)) + .thenReturn(List.of(bizEventEntity, bizEventEntityDuplicated)); + + AppException e = assertThrows(AppException.class, () -> bizEventsService.getOrganizationReceipt(ORGANIZATION_FISCAL_CODE, IUR, IUV)); + assertEquals(HttpStatus.UNPROCESSABLE_ENTITY, e.getHttpStatus()); + } - private BizEvent bizEventEntityDuplicated; + @Test + void getBizEventSuccess() { + when(bizEventsRepository.findById(BIZ_EVENT_ID, new PartitionKey(BIZ_EVENT_ID))) + .thenReturn(Optional.of(bizEventEntity)); - @BeforeAll - public void setUp() throws IOException { - bizEventEntity = TestUtil.readModelFromFile("biz-events/bizEvent.json", BizEvent.class); - bizEventEntityDuplicated = TestUtil.readModelFromFile("biz-events/bizEvent_duplicate.json", BizEvent.class); - } + BizEvent bizEvent = bizEventsService.getBizEvent(BIZ_EVENT_ID); + assertEquals(bizEvent, bizEventEntity); + } @Test - void getOrganizationReceipt() { - bizEventsService = spy(new BizEventsService(bizEventsRepository, modelMapper)); - when(bizEventsRepository.getBizEventByOrgFiscCodeAndIur("66666666666", "CCD01", "112006686812600")).thenReturn(List.of(bizEventEntity)); - - CtReceiptModelResponse ctReceipt = bizEventsService.getOrganizationReceipt("66666666666", "CCD01", "112006686812600"); - assertEquals("85570ffebb13411b80d79f415641ec55", ctReceipt.getReceiptId()); - assertEquals("cash", ctReceipt.getPaymentMethod()); - } - + void getBizEventFailNotFound() { + when(bizEventsRepository.findById("fake id", new PartitionKey("fake id"))) + .thenReturn(Optional.empty()); + + AppException e = assertThrows(AppException.class, () -> bizEventsService.getBizEvent("fake id")); + assertEquals(HttpStatus.NOT_FOUND, e.getHttpStatus()); + } + @Test - void getOrganizationReceipt_404() throws IOException { - bizEventsService = spy(new BizEventsService(bizEventsRepository, modelMapper)); - when(bizEventsRepository.getBizEventByOrgFiscCodeAndIur("66666666666", "CCD01", "112006686812600")).thenReturn(List.of(bizEventEntity)); - - try { - // non-existent iuv -> raise a 404 exception - bizEventsService.getOrganizationReceipt("66666666666", "CCD01", "fake_iuv"); - fail(); - } catch (AppException e) { - assertEquals(HttpStatus.NOT_FOUND, e.getHttpStatus()); - } catch (Exception e) { - fail(); - } - } - + void getBizEventByOrgFiscalCodeAndIuvSuccess() { + when(bizEventsRepository.getBizEventByOrgFiscalCodeAndIuv(ORGANIZATION_FISCAL_CODE, IUV)) + .thenReturn(List.of(bizEventEntity)); + + BizEvent bizEvent = bizEventsService.getBizEventByOrgFiscalCodeAndIuv(ORGANIZATION_FISCAL_CODE, IUV); + assertEquals(bizEvent.getId(), bizEventEntity.getId()); + assertEquals(bizEvent.getCreditor().getIdPA(), bizEventEntity.getCreditor().getIdPA()); + assertEquals(bizEvent.getDebtorPosition().getIuv(), bizEventEntity.getDebtorPosition().getIuv()); + } + @Test - void getOrganizationReceipt_422() throws IOException { + void getBizEventByOrgFiscalCodeAndIuvFailNotFound() { + when(bizEventsRepository.getBizEventByOrgFiscalCodeAndIuv(ORGANIZATION_FISCAL_CODE, IUV)) + .thenReturn(Collections.emptyList()); - // mocking a fake save for duplicated entity - bizEventsService = spy(new BizEventsService(bizEventsRepository, modelMapper)); - when(bizEventsRepository.getBizEventByOrgFiscCodeAndIur("66666666666", "CCD01", "112006686812600")).thenReturn(List.of(bizEventEntity, bizEventEntityDuplicated)); - - try { - // more than one records, the payment must be unique -> raise a 422 exception - bizEventsService.getOrganizationReceipt("66666666666", "CCD01", "112006686812600"); - fail(); - } catch (AppException e) { - assertEquals(HttpStatus.UNPROCESSABLE_ENTITY, e.getHttpStatus()); - } catch (Exception e) { - fail(); - } - } - - + AppException e = assertThrows(AppException.class, () -> bizEventsService.getBizEventByOrgFiscalCodeAndIuv(ORGANIZATION_FISCAL_CODE, "fake iuv")); + assertEquals(HttpStatus.NOT_FOUND, e.getHttpStatus()); + } + + @Test + void getBizEventByOrgFiscalCodeAndIuvFailNotUnique() { + when(bizEventsRepository.getBizEventByOrgFiscalCodeAndIuv(ORGANIZATION_FISCAL_CODE, IUV)) + .thenReturn(List.of(bizEventEntity, bizEventEntityDuplicated)); + AppException e = assertThrows(AppException.class, () -> bizEventsService.getBizEventByOrgFiscalCodeAndIuv(ORGANIZATION_FISCAL_CODE, IUV)); + assertEquals(HttpStatus.UNPROCESSABLE_ENTITY, e.getHttpStatus()); + } }