diff --git a/README.md b/README.md index ec121700..667f112e 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ execute the following command: `sh update-specs.sh` -After the execution, in `target/generated-sources/jaxb` folder there will be the newly generated classes. +After the execution, the newly generated classes are added in `target/generated-sources/jaxb` folder. The application now can be run and all the class references are correctly resolved. ### Run the project diff --git a/infra/.terraform.lock.hcl b/infra/.terraform.lock.hcl new file mode 100644 index 00000000..2ae265b9 --- /dev/null +++ b/infra/.terraform.lock.hcl @@ -0,0 +1,62 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/azure/azapi" { + version = "1.3.0" + constraints = "1.3.0" + hashes = [ + "h1:OWZNYEGEIunmpxEcbGveH+kkdELQfMCUYxLt1b25UOc=", + "zh:0923b297c5b71ed584e5f3a0b2393e80244076e85102a90438159833353274b0", + "zh:11fa2922aa98ca55beaf7cc33c7edbde81bbd405fdfea2955276c7f5a8537240", + "zh:14af830fb6091d084bfc2711c8e9c7bf05aa3c56fe8fd8e2fb4eddeb345be88d", + "zh:25258425ecbffbdf09b0c8131d2c680cddd19b504e0036ee5f83972dcae7df0a", + "zh:2922b535fe4d4f0963189548f2f8360a0aaf951fd411354f2269a111d8a0c1ad", + "zh:32c9360305e00c25d0f9d0a84dfbdbad8da2465be769a9c1f11f132c0225358e", + "zh:4ddd3ee23c340d5000839d8d30ba7f94e695476d63075f95cfb041e67d8f6ef6", + "zh:5c1514392a5c3dd51084aa70cb6c4dcc8b027c4508b5e4eb9f8c3990fd403213", + "zh:6b3ecac7099ab86c007b5ad636bd029f5e5f3e9bd06b0f74c82f0451a7995ecc", + "zh:6cb7081745b378e910e0cf09fb5717a2ad35e629ce3e07415d6682c1c1407872", + "zh:7107eda5125c1b983380f1f6418c592fb7fb2eb5b589ad0e08f6c47341f36318", + "zh:c6fa7af32a7a47d23a85e0eea4d4cbb065378ae75aed8c9c628fb625b04bc619", + ] +} + +provider "registry.terraform.io/hashicorp/azuread" { + version = "2.30.0" + constraints = "2.30.0" + hashes = [ + "h1:Uw4TcmJBEJ71h+oCwwidlkk5jFpyFRDPAFCMs/bT/cw=", + "zh:1c3e89cf19118fc07d7b04257251fc9897e722c16e0a0df7b07fcd261f8c12e7", + "zh:2e62c193030e04ebb10cc0526119cf69824bf2d7e4ea5a2f45bd5d5fb7221d36", + "zh:2f3c7a35257332d68b778cefc5201a5f044e4914dd03794a4da662ddfe756483", + "zh:35d0d3a1b58fdb8b8c4462d6b7e7016042da43ea9cc734ce897f52a73407d9b0", + "zh:47ede0cd0206ec953d40bf4a80aa6e59af64e26cbbd877614ac424533dbb693b", + "zh:48c190307d4d42ea67c9b8cc544025024753f46cef6ea64db84735e7055a72da", + "zh:6fff9b2c6a962252a70a15b400147789ab369b35a781e9d21cce3804b04d29af", + "zh:7646980cf3438bff29c91ffedb74458febbb00a996638751fbd204ab1c628c9b", + "zh:77aa2fa7ca6d5446afa71d4ff83cb87b70a2f3b72110fc442c339e8e710b2928", + "zh:e20b2b2c37175b89dd0db058a096544d448032e28e3b56e2db368343533a9684", + "zh:eab175b1dfe9865ad9404dccb6d5542899f8c435095aa7c679314b811c717ce7", + "zh:efc862bd78c55d2ff089729e2a34c1831ab4b0644fc11b36ee4ebed00a4797ba", + ] +} + +provider "registry.terraform.io/hashicorp/azurerm" { + version = "3.44.1" + constraints = ">= 3.30.0, <= 3.44.1, <= 3.53.0" + hashes = [ + "h1:dq7s/3sZrI4oLWL/NUlOcOD3HGkzimRmEvFiWX+ENRw=", + "zh:0a1761b5aeec47d5019114976de5eb9832dea1d57d632ca6fa464b99b782d1c1", + "zh:0e9c96fa7ed6d55a3f3a646ff346298c8b7728331bb3a74875f78ecb7d245c16", + "zh:1aa953a692c7b5b10219343f0238f4624ac988e247721b6ec6b1bed2b81f7ceb", + "zh:237258af1a1ce8a0aed8f6cdb03c69ea83ff4f3a46d5bd1466cd503f0b5aded8", + "zh:542067eeeb3b4e286e92d646e0f40426e204ed268973343e585aa521f075f8dc", + "zh:8326d52460252fd335ae97d0fabd9f5d90061a4fbeb273618f4067be3eb4e75a", + "zh:97a2b802bf6e204476131ddb7a91e832568ee8da3b0515ed23361c9f72ca9706", + "zh:9ae5a52ec85e0ad218e2ce9d33859f17afbb2fb2a690bf60d5f48fc7680e7fb0", + "zh:b17e77aff310e232f541334ba1858b5125ea0e527a5d6824de017192d8d8a3a2", + "zh:c469ba6681535c07c58dad6c1b59b056912300a7c91137ddc0103ef16b1d5697", + "zh:cea6026ef8fb5512d14c1ba6fdf36b90a09de536d4e4afad96b926af39114f74", + "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", + ] +} diff --git a/infra/04_apim_api.tf b/infra/04_apim_api.tf index b5b493c1..58be9da5 100644 --- a/infra/04_apim_api.tf +++ b/infra/04_apim_api.tf @@ -1,17 +1,7 @@ locals { - apim_wisp_converter_soap_api = { - project_name = "wisp-converter-soap" - repo_name = "pagopa-wisp-converter" - display_name = "WISP Converter - SOAP" - description = "API SOAP for adapting WISP-made requests for GPD system" - path = "wisp-converter/service" - subscription_required = true - host = "api.${var.apim_dns_zone_prefix}.${var.external_domain}" - hostname = var.hostname - } apim_wisp_converter_rest_api = { - project_name = "wisp-converter-rest" + project_name = "wisp-converter" repo_name = "pagopa-wisp-converter" display_name = "WISP Converter" description = "API for WISP Converter" @@ -20,52 +10,13 @@ locals { host = "api.${var.apim_dns_zone_prefix}.${var.external_domain}" hostname = var.hostname } -} - - -# WISP Converter - SOAP APIs - -resource "azurerm_api_management_api_version_set" "api_version_set_wisp_converter_soap" { - name = "${var.prefix}-${var.env_short}-${var.location_short}-${local.apim_wisp_converter_soap_api.project_name}" - resource_group_name = local.apim.rg - api_management_name = local.apim.name - display_name = local.apim_wisp_converter_soap_api.display_name - versioning_scheme = "Segment" -} - - -module "wisp_converter_soap_api_v1" { - source = "git::https://github.com/pagopa/terraform-azurerm-v3.git//api_management_api?ref=v6.7.0" - - name = format("%s-api-gpd-payments-soap-api", var.env_short) - api_management_name = local.apim.name - resource_group_name = local.apim.rg - subscription_required = local.apim_wisp_converter_soap_api.subscription_required - version_set_id = azurerm_api_management_api_version_set.api_version_set_wisp_converter_soap.id - version = "v1" - - description = local.apim_wisp_converter_soap_api.description - display_name = local.apim_wisp_converter_soap_api.display_name - path = local.apim_wisp_converter_soap_api.path - protocols = ["https"] - service_url = format("https://%s/pagopa-wisp-converter/service", local.apim_wisp_converter_soap_api.hostname) - soap_pass_through = true - - import { - content_format = "wsdl" - content_value = file("./soap/TODO.wsdl") // TODO set WSDL - wsdl_selector { - service_name = "TODO_Service" - endpoint_name = "TODO_Port" - } - } } # WISP Converter - REST APIs -resource "azurerm_api_management_api_version_set" "api_version_set_wisp_converter_rest" { +resource "azurerm_api_management_api_version_set" "api_version_set_wisp_converter" { name = "${var.prefix}-${var.env_short}-${var.location_short}-${local.apim_wisp_converter_rest_api.project_name}" resource_group_name = local.apim.rg api_management_name = local.apim.name @@ -73,16 +24,16 @@ resource "azurerm_api_management_api_version_set" "api_version_set_wisp_converte versioning_scheme = "Segment" } -module "wisp_converter_rest_api_v1" { +module "wisp_converter_api_v1" { source = "git::https://github.com/pagopa/terraform-azurerm-v3.git//api_management_api?ref=v6.7.0" - name = format("%s-wisp-converter-rest-api", var.env_short) + name = format("%s-wisp-converter-api", var.env_short) api_management_name = local.apim.name resource_group_name = local.apim.rg product_ids = [local.apim.product_id] subscription_required = true - version_set_id = azurerm_api_management_api_version_set.api_version_set_wisp_converter_rest.id + version_set_id = azurerm_api_management_api_version_set.api_version_set_wisp_converter.id api_version = "v1" description = local.apim_wisp_converter_rest_api.description @@ -100,4 +51,4 @@ module "wisp_converter_rest_api_v1" { xml_content = templatefile("./policy/_base_policy.xml", { hostname = var.hostname }) -} +} \ No newline at end of file diff --git a/infra/env/weu-dev/terraform.tfvars b/infra/env/weu-dev/terraform.tfvars index d4f691eb..9b50e07e 100644 --- a/infra/env/weu-dev/terraform.tfvars +++ b/infra/env/weu-dev/terraform.tfvars @@ -2,6 +2,9 @@ prefix = "pagopa" env = "dev" env_short = "d" +domain = "nodo" +location_short = "weu" + tags = { CreatedBy = "Terraform" Environment = "Dev" diff --git a/openapi-client/apiconfig-cache/openapi.json b/openapi-client/apiconfig-cache/openapi.json new file mode 100644 index 00000000..928dc807 --- /dev/null +++ b/openapi-client/apiconfig-cache/openapi.json @@ -0,0 +1,1019 @@ +{ + "openapi" : "3.0.1", + "info" : { + "title" : "API-Config Cacher", + "description" : "Generate cache for regarding Nodo dei Pagamenti configuration", + "termsOfService" : "https://www.pagopa.gov.it/", + "version" : "0.8.6" + }, + "servers" : [ { + "url" : "http://localhost:8080" + }, { + "url" : "https://{host}/{basePath}", + "variables" : { + "host" : { + "default" : "api.dev.platform.pagopa.it", + "enum" : [ "api.dev.platform.pagopa.it", "api.uat.platform.pagopa.it", "api.platform.pagopa.it" ] + }, + "basePath" : { + "default" : "api-config-cache/o/v1", + "enum" : [ "api-config-cache/o/v1", "api-config-cache/p/v1", "api-config-cache/odev/v1" ] + } + } + } ], + "paths" : { + "/stakeholders/standin/cache/schemas/v1" : { + "get" : { + "tags" : [ "Cache" ], + "summary" : "Get selected key of cache v1 config", + "operationId" : "cache", + "responses" : { + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ProblemJson" + } + } + } + }, + "401" : { + "description" : "Unauthorized" + }, + "403" : { + "description" : "Forbidden" + }, + "500" : { + "description" : "Service unavailable", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ProblemJson" + } + } + } + }, + "429" : { + "description" : "Too many requests" + }, + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ConfigDataV1" + } + } + } + } + }, + "security" : [ { + "ApiKey" : [ ] + } ] + } + }, + "/stakeholders/standin/cache/schemas/v1/id" : { + "get" : { + "tags" : [ "Cache" ], + "summary" : "Get last v1 cache version", + "operationId" : "idV1", + "responses" : { + "400" : { + "description" : "Bad Request", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ProblemJson" + } + } + } + }, + "401" : { + "description" : "Unauthorized" + }, + "403" : { + "description" : "Forbidden" + }, + "404" : { + "description" : "Not Found" + }, + "500" : { + "description" : "Service unavailable", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ProblemJson" + } + } + } + }, + "429" : { + "description" : "Too many requests" + }, + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/CacheVersion" + } + } + } + } + }, + "security" : [ { + "ApiKey" : [ ] + } ] + } + } + }, + "components" : { + "schemas" : { + "ProblemJson" : { + "type" : "object", + "properties" : { + "title" : { + "type" : "string", + "description" : "A short, summary of the problem type. Written in english and readable for engineers (usually not suited for non technical stakeholders and not localized); example: Service Unavailable" + }, + "status" : { + "maximum" : 600, + "minimum" : 100, + "type" : "integer", + "description" : "The HTTP status code generated by the origin server for this occurrence of the problem.", + "format" : "int32", + "example" : 200 + }, + "detail" : { + "type" : "string", + "description" : "A human readable explanation specific to this occurrence of the problem.", + "example" : "There was an error processing the request" + } + } + }, + "BrokerCreditorInstitution" : { + "required" : [ "broker_code", "enabled", "extended_fault_bean" ], + "type" : "object", + "properties" : { + "broker_code" : { + "type" : "string" + }, + "enabled" : { + "type" : "boolean" + }, + "description" : { + "type" : "string" + }, + "extended_fault_bean" : { + "type" : "boolean" + } + } + }, + "BrokerPsp" : { + "required" : [ "broker_psp_code", "enabled", "extended_fault_bean" ], + "type" : "object", + "properties" : { + "broker_psp_code" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "enabled" : { + "type" : "boolean" + }, + "extended_fault_bean" : { + "type" : "boolean" + } + } + }, + "CdsCategory" : { + "required" : [ "description" ], + "type" : "object", + "properties" : { + "description" : { + "type" : "string" + } + } + }, + "CdsService" : { + "required" : [ "category", "description", "id", "reference_xsd", "version" ], + "type" : "object", + "properties" : { + "id" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "reference_xsd" : { + "type" : "string" + }, + "version" : { + "type" : "integer", + "format" : "int64" + }, + "category" : { + "type" : "string" + } + } + }, + "CdsSubject" : { + "required" : [ "creditor_institution_code", "creditor_institution_description" ], + "type" : "object", + "properties" : { + "creditor_institution_code" : { + "type" : "string" + }, + "creditor_institution_description" : { + "type" : "string" + } + } + }, + "CdsSubjectService" : { + "required" : [ "fee", "service", "start_date", "subject", "subject_service_id" ], + "type" : "object", + "properties" : { + "subject" : { + "type" : "string" + }, + "service" : { + "type" : "string" + }, + "subject_service_id" : { + "type" : "string" + }, + "start_date" : { + "type" : "string", + "format" : "date-time" + }, + "end_date" : { + "type" : "string", + "format" : "date-time" + }, + "fee" : { + "type" : "boolean" + }, + "station_code" : { + "type" : "string" + }, + "service_description" : { + "type" : "string" + } + } + }, + "Channel" : { + "required" : [ "agid", "broker_psp_code", "channel_code", "connection", "digital_stamp", "enabled", "flag_io", "flag_travaso", "new_fault_code", "password", "payment_model", "primitive_version", "recovery", "redirect", "rt_push", "thread_number", "timeouts" ], + "type" : "object", + "properties" : { + "channel_code" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "enabled" : { + "type" : "boolean" + }, + "password" : { + "type" : "string" + }, + "connection" : { + "$ref" : "#/components/schemas/Connection" + }, + "broker_psp_code" : { + "type" : "string" + }, + "proxy" : { + "$ref" : "#/components/schemas/Proxy" + }, + "service" : { + "$ref" : "#/components/schemas/Service" + }, + "service_nmp" : { + "$ref" : "#/components/schemas/Service" + }, + "thread_number" : { + "type" : "integer", + "format" : "int64" + }, + "timeouts" : { + "$ref" : "#/components/schemas/Timeouts" + }, + "new_fault_code" : { + "type" : "boolean" + }, + "redirect" : { + "$ref" : "#/components/schemas/Redirect" + }, + "payment_model" : { + "type" : "string" + }, + "serv_plugin" : { + "type" : "string" + }, + "rt_push" : { + "type" : "boolean" + }, + "recovery" : { + "type" : "boolean" + }, + "digital_stamp" : { + "type" : "boolean" + }, + "flag_io" : { + "type" : "boolean" + }, + "agid" : { + "type" : "boolean" + }, + "primitive_version" : { + "type" : "integer", + "format" : "int32" + }, + "flag_travaso" : { + "type" : "boolean" + } + } + }, + "ConfigDataV1" : { + "required" : [ "cdsCategories", "cdsServices", "cdsSubjectServices", "cdsSubjects", "channels", "configurations", "creditorInstitutionBrokers", "creditorInstitutionEncodings", "creditorInstitutionInformations", "creditorInstitutionStations", "creditorInstitutions", "encodings", "ftpServers", "gdeConfigurations", "ibans", "languages", "metadataDict", "paymentTypes", "plugins", "pspBrokers", "pspChannelPaymentTypes", "pspInformationTemplates", "pspInformations", "psps", "stations", "version" ], + "type" : "object", + "properties" : { + "version" : { + "type" : "string" + }, + "creditorInstitutions" : { + "type" : "object", + "additionalProperties" : { + "$ref" : "#/components/schemas/CreditorInstitution" + } + }, + "creditorInstitutionBrokers" : { + "type" : "object", + "additionalProperties" : { + "$ref" : "#/components/schemas/BrokerCreditorInstitution" + } + }, + "stations" : { + "type" : "object", + "additionalProperties" : { + "$ref" : "#/components/schemas/Station" + } + }, + "creditorInstitutionStations" : { + "type" : "object", + "additionalProperties" : { + "$ref" : "#/components/schemas/StationCreditorInstitution" + } + }, + "encodings" : { + "type" : "object", + "additionalProperties" : { + "$ref" : "#/components/schemas/Encoding" + } + }, + "creditorInstitutionEncodings" : { + "type" : "object", + "additionalProperties" : { + "$ref" : "#/components/schemas/CreditorInstitutionEncoding" + } + }, + "ibans" : { + "type" : "object", + "additionalProperties" : { + "$ref" : "#/components/schemas/Iban" + } + }, + "creditorInstitutionInformations" : { + "type" : "object", + "additionalProperties" : { + "$ref" : "#/components/schemas/CreditorInstitutionInformation" + } + }, + "psps" : { + "type" : "object", + "additionalProperties" : { + "$ref" : "#/components/schemas/PaymentServiceProvider" + } + }, + "pspBrokers" : { + "type" : "object", + "additionalProperties" : { + "$ref" : "#/components/schemas/BrokerPsp" + } + }, + "paymentTypes" : { + "type" : "object", + "additionalProperties" : { + "$ref" : "#/components/schemas/PaymentType" + } + }, + "pspChannelPaymentTypes" : { + "type" : "object", + "additionalProperties" : { + "$ref" : "#/components/schemas/PspChannelPaymentType" + } + }, + "plugins" : { + "type" : "object", + "additionalProperties" : { + "$ref" : "#/components/schemas/Plugin" + } + }, + "pspInformationTemplates" : { + "type" : "object", + "additionalProperties" : { + "$ref" : "#/components/schemas/PspInformation" + } + }, + "pspInformations" : { + "type" : "object", + "additionalProperties" : { + "$ref" : "#/components/schemas/PspInformation" + } + }, + "channels" : { + "type" : "object", + "additionalProperties" : { + "$ref" : "#/components/schemas/Channel" + } + }, + "cdsServices" : { + "type" : "object", + "additionalProperties" : { + "$ref" : "#/components/schemas/CdsService" + } + }, + "cdsSubjects" : { + "type" : "object", + "additionalProperties" : { + "$ref" : "#/components/schemas/CdsSubject" + } + }, + "cdsSubjectServices" : { + "type" : "object", + "additionalProperties" : { + "$ref" : "#/components/schemas/CdsSubjectService" + } + }, + "cdsCategories" : { + "type" : "object", + "additionalProperties" : { + "$ref" : "#/components/schemas/CdsCategory" + } + }, + "configurations" : { + "type" : "object", + "additionalProperties" : { + "$ref" : "#/components/schemas/ConfigurationKey" + } + }, + "ftpServers" : { + "type" : "object", + "additionalProperties" : { + "$ref" : "#/components/schemas/FtpServer" + } + }, + "languages" : { + "type" : "object", + "additionalProperties" : { + "type" : "string" + } + }, + "gdeConfigurations" : { + "type" : "object", + "additionalProperties" : { + "$ref" : "#/components/schemas/GdeConfiguration" + } + }, + "metadataDict" : { + "type" : "object", + "additionalProperties" : { + "$ref" : "#/components/schemas/MetadataDict" + } + } + } + }, + "ConfigurationKey" : { + "required" : [ "category", "key", "value" ], + "type" : "object", + "properties" : { + "category" : { + "type" : "string" + }, + "key" : { + "type" : "string" + }, + "value" : { + "type" : "string" + }, + "description" : { + "type" : "string" + } + } + }, + "Connection" : { + "required" : [ "ip", "port", "protocol" ], + "type" : "object", + "properties" : { + "protocol" : { + "type" : "string", + "enum" : [ "HTTPS", "HTTP" ] + }, + "ip" : { + "type" : "string" + }, + "port" : { + "type" : "integer", + "format" : "int64" + } + } + }, + "CreditorInstitution" : { + "required" : [ "creditor_institution_code", "enabled", "psp_payment", "reporting_ftp", "reporting_zip" ], + "type" : "object", + "properties" : { + "creditor_institution_code" : { + "type" : "string" + }, + "enabled" : { + "type" : "boolean" + }, + "business_name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "address" : { + "$ref" : "#/components/schemas/CreditorInstitutionAddress" + }, + "psp_payment" : { + "type" : "boolean" + }, + "reporting_ftp" : { + "type" : "boolean" + }, + "reporting_zip" : { + "type" : "boolean" + } + } + }, + "CreditorInstitutionAddress" : { + "type" : "object", + "properties" : { + "location" : { + "type" : "string" + }, + "city" : { + "type" : "string" + }, + "zip_code" : { + "type" : "string" + }, + "country_code" : { + "type" : "string" + }, + "tax_domicile" : { + "type" : "string" + } + } + }, + "CreditorInstitutionEncoding" : { + "required" : [ "code_type", "creditor_institution_code", "encoding_code" ], + "type" : "object", + "properties" : { + "code_type" : { + "type" : "string" + }, + "encoding_code" : { + "type" : "string" + }, + "creditor_institution_code" : { + "type" : "string" + } + } + }, + "CreditorInstitutionInformation" : { + "required" : [ "informativa" ], + "type" : "object", + "properties" : { + "informativa" : { + "type" : "string" + } + } + }, + "Encoding" : { + "required" : [ "code_type", "description" ], + "type" : "object", + "properties" : { + "code_type" : { + "type" : "string" + }, + "description" : { + "type" : "string" + } + } + }, + "FtpServer" : { + "required" : [ "enabled", "history_path", "host", "id", "in_path", "out_path", "password", "port", "root_path", "service", "type", "username" ], + "type" : "object", + "properties" : { + "host" : { + "type" : "string" + }, + "port" : { + "type" : "integer", + "format" : "int32" + }, + "enabled" : { + "type" : "boolean" + }, + "username" : { + "type" : "string" + }, + "password" : { + "type" : "string" + }, + "root_path" : { + "type" : "string" + }, + "service" : { + "type" : "string" + }, + "type" : { + "type" : "string" + }, + "in_path" : { + "type" : "string" + }, + "out_path" : { + "type" : "string" + }, + "history_path" : { + "type" : "string" + }, + "id" : { + "type" : "integer", + "format" : "int64" + } + } + }, + "GdeConfiguration" : { + "required" : [ "event_hub_enabled", "event_hub_payload_enabled", "primitive", "type" ], + "type" : "object", + "properties" : { + "primitive" : { + "type" : "string" + }, + "type" : { + "type" : "string" + }, + "event_hub_enabled" : { + "type" : "boolean" + }, + "event_hub_payload_enabled" : { + "type" : "boolean" + } + } + }, + "Iban" : { + "required" : [ "creditor_institution_code", "iban", "publication_date", "validity_date" ], + "type" : "object", + "properties" : { + "iban" : { + "type" : "string" + }, + "creditor_institution_code" : { + "type" : "string" + }, + "validity_date" : { + "type" : "string", + "format" : "date-time" + }, + "publication_date" : { + "type" : "string", + "format" : "date-time" + }, + "shop_id" : { + "type" : "string" + }, + "seller_bank_id" : { + "type" : "string" + }, + "avvio_key" : { + "type" : "string" + }, + "esito_key" : { + "type" : "string" + } + } + }, + "MetadataDict" : { + "required" : [ "key", "start_date" ], + "type" : "object", + "properties" : { + "key" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "start_date" : { + "type" : "string", + "format" : "date-time" + }, + "end_date" : { + "type" : "string", + "format" : "date-time" + } + } + }, + "PaymentServiceProvider" : { + "required" : [ "agid_psp", "digital_stamp", "enabled", "psp_code" ], + "type" : "object", + "properties" : { + "psp_code" : { + "type" : "string" + }, + "enabled" : { + "type" : "boolean" + }, + "description" : { + "type" : "string" + }, + "business_name" : { + "type" : "string" + }, + "abi" : { + "type" : "string" + }, + "bic" : { + "type" : "string" + }, + "my_bank_code" : { + "type" : "string" + }, + "digital_stamp" : { + "type" : "boolean" + }, + "agid_psp" : { + "type" : "boolean" + }, + "tax_code" : { + "type" : "string" + }, + "vat_number" : { + "type" : "string" + } + } + }, + "PaymentType" : { + "required" : [ "payment_type" ], + "type" : "object", + "properties" : { + "payment_type" : { + "type" : "string" + }, + "description" : { + "type" : "string" + } + } + }, + "Plugin" : { + "required" : [ "id_serv_plugin" ], + "type" : "object", + "properties" : { + "id_serv_plugin" : { + "type" : "string" + }, + "pag_const_string_profile" : { + "type" : "string" + }, + "pag_soap_rule_profile" : { + "type" : "string" + }, + "pag_rpt_xpath_profile" : { + "type" : "string" + }, + "id_bean" : { + "type" : "string" + } + } + }, + "Proxy" : { + "type" : "object", + "properties" : { + "proxy_host" : { + "type" : "string" + }, + "proxy_port" : { + "type" : "integer", + "format" : "int64" + }, + "proxy_username" : { + "type" : "string" + }, + "proxy_password" : { + "type" : "string" + } + } + }, + "PspChannelPaymentType" : { + "required" : [ "channel_code", "payment_type", "psp_code" ], + "type" : "object", + "properties" : { + "psp_code" : { + "type" : "string" + }, + "channel_code" : { + "type" : "string" + }, + "payment_type" : { + "type" : "string" + } + } + }, + "PspInformation" : { + "required" : [ "informativa" ], + "type" : "object", + "properties" : { + "informativa" : { + "type" : "string" + } + } + }, + "Redirect" : { + "type" : "object", + "properties" : { + "protocol" : { + "type" : "string", + "enum" : [ "HTTPS", "HTTP" ] + }, + "ip" : { + "type" : "string" + }, + "path" : { + "type" : "string" + }, + "port" : { + "type" : "integer", + "format" : "int64" + }, + "query_string" : { + "type" : "string" + } + } + }, + "Service" : { + "type" : "object", + "properties" : { + "path" : { + "type" : "string" + }, + "target_host" : { + "type" : "string" + }, + "target_port" : { + "type" : "integer", + "format" : "int64" + }, + "target_path" : { + "type" : "string" + } + } + }, + "Station" : { + "required" : [ "broker_code", "connection", "enabled", "invio_rt_istantaneo", "password", "primitive_version", "redirect", "station_code", "thread_number", "timeouts", "version" ], + "type" : "object", + "properties" : { + "station_code" : { + "type" : "string" + }, + "enabled" : { + "type" : "boolean" + }, + "version" : { + "type" : "integer", + "format" : "int64" + }, + "connection" : { + "$ref" : "#/components/schemas/Connection" + }, + "connection_mod4" : { + "$ref" : "#/components/schemas/Connection" + }, + "password" : { + "type" : "string" + }, + "redirect" : { + "$ref" : "#/components/schemas/Redirect" + }, + "service" : { + "$ref" : "#/components/schemas/Service" + }, + "service_pof" : { + "$ref" : "#/components/schemas/Service" + }, + "service_mod4" : { + "$ref" : "#/components/schemas/Service" + }, + "broker_code" : { + "type" : "string" + }, + "proxy" : { + "$ref" : "#/components/schemas/Proxy" + }, + "thread_number" : { + "type" : "integer", + "format" : "int64" + }, + "timeouts" : { + "$ref" : "#/components/schemas/Timeouts" + }, + "invio_rt_istantaneo" : { + "type" : "boolean" + }, + "primitive_version" : { + "type" : "integer", + "format" : "int32" + } + } + }, + "StationCreditorInstitution" : { + "required" : [ "broadcast", "creditor_institution_code", "mod4", "primitive_version", "spontaneous_payment", "station_code" ], + "type" : "object", + "properties" : { + "creditor_institution_code" : { + "type" : "string" + }, + "station_code" : { + "type" : "string" + }, + "application_code" : { + "type" : "integer", + "format" : "int64" + }, + "aux_digit" : { + "type" : "integer", + "format" : "int64" + }, + "segregation_code" : { + "type" : "integer", + "format" : "int64" + }, + "mod4" : { + "type" : "boolean" + }, + "broadcast" : { + "type" : "boolean" + }, + "primitive_version" : { + "type" : "integer", + "format" : "int32" + }, + "spontaneous_payment" : { + "type" : "boolean" + } + } + }, + "Timeouts" : { + "required" : [ "timeout_a", "timeout_b", "timeout_c" ], + "type" : "object", + "properties" : { + "timeout_a" : { + "type" : "integer", + "format" : "int64" + }, + "timeout_b" : { + "type" : "integer", + "format" : "int64" + }, + "timeout_c" : { + "type" : "integer", + "format" : "int64" + } + } + }, + "CacheVersion" : { + "required" : [ "version" ], + "type" : "object", + "properties" : { + "version" : { + "type" : "string" + } + } + } + }, + "securitySchemes" : { + "ApiKey" : { + "type" : "apiKey", + "description" : "The API key to access this function app.", + "name" : "Ocp-Apim-Subscription-Key", + "in" : "header" + } + } + } +} \ No newline at end of file diff --git a/openapi-client/checkout/payment-requests-api.yaml b/openapi-client/checkout/openapi.yaml similarity index 100% rename from openapi-client/checkout/payment-requests-api.yaml rename to openapi-client/checkout/openapi.yaml diff --git a/openapi-client/gpd/openapi_internal.json b/openapi-client/gpd/openapi.json similarity index 100% rename from openapi-client/gpd/openapi_internal.json rename to openapi-client/gpd/openapi.json diff --git a/pom.xml b/pom.xml index 21ea7f5f..10677c93 100644 --- a/pom.xml +++ b/pom.xml @@ -15,9 +15,7 @@ wisp-converter 0.0.4 pagoPA WISP Converter - A service that permits to handle nodoInviaRPT and nodoInviaCarrelloRPT request from WISP, interfacing - them with GPD system - + A service that permits to handle nodoInviaRPT and nodoInviaCarrelloRPT request from WISP, converting them in NMU payments. 17 @@ -62,15 +60,16 @@ org.springframework.boot spring-boot-starter-actuator - - - - org.springframework.boot spring-boot-starter-web-services + + org.springframework.boot + spring-boot-starter-thymeleaf + + @@ -101,16 +100,6 @@ ${cosmos-data-version} - - - - - - - - - - org.springdoc @@ -135,6 +124,12 @@ ${jsr305.version} + + org.openapitools + jackson-databind-nullable + 0.2.6 + + junit junit @@ -145,12 +140,6 @@ logback-ecs-encoder ${java-ecs-logging.version} - - - org.openapitools - jackson-databind-nullable - 0.2.6 - @@ -222,6 +211,7 @@ + org.apache.cxf @@ -269,7 +259,7 @@ - + org.jacoco jacoco-maven-plugin @@ -293,6 +283,8 @@ + + org.sonarsource.scanner.maven sonar-maven-plugin @@ -306,34 +298,51 @@ + + org.openapitools openapi-generator-maven-plugin - 7.4.0 - + + apiconfig-cache + + generate + + + ${project.basedir}/openapi-client/apiconfig-cache/openapi.json + java + Dto + false + false + resttemplate + + it.gov.pagopa.wispconverter.client.cache + it.gov.pagopa.wispconverter.client.cache.model + it.gov.pagopa.wispconverter.client.cache.api + it.gov.pagopa.wispconverter.client.cache.invoker + java8 + + + gpd generate - ${project.basedir}/openapi-client/gpd/openapi_internal.json - + ${project.basedir}/openapi-client/gpd/openapi.json java Dto - false false - resttemplate - - it.gov.pagopa.gpdclient - it.gov.pagopa.gpdclient.model - it.gov.pagopa.gpdclient.api - it.gov.pagopa.gpdclient.client + it.gov.pagopa.wispconverter.client.gpd + it.gov.pagopa.wispconverter.client.gpd.model + it.gov.pagopa.wispconverter.client.gpd.api + it.gov.pagopa.wispconverter.client.gpd.invoker java8 @@ -345,20 +354,16 @@ ${project.basedir}/openapi-client/iuv-generator/openapi.json - java Dto - false false - resttemplate - - it.gov.pagopa.iuvgeneratorclient - it.gov.pagopa.iuvgeneratorclient.model - it.gov.pagopa.iuvgeneratorclient.api - it.gov.pagopa.iuvgeneratorclient.client + it.gov.pagopa.wispconverter.client.iuvgenerator + it.gov.pagopa.wispconverter.client.iuvgenerator.model + it.gov.pagopa.wispconverter.client.iuvgenerator.api + it.gov.pagopa.wispconverter.client.iuvgenerator.invoker java8 @@ -369,21 +374,17 @@ generate - ${project.basedir}/openapi-client/checkout/payment-requests-api.yaml - + ${project.basedir}/openapi-client/checkout/openapi.yaml java Dto - false false - resttemplate - - it.gov.pagopa.checkoutclient - it.gov.pagopa.checkoutclient.model - it.gov.pagopa.checkoutclient.api - it.gov.pagopa.checkoutclient.client + it.gov.pagopa.wispconverter.client.checkout + it.gov.pagopa.wispconverter.client.checkout.model + it.gov.pagopa.wispconverter.client.checkout.api + it.gov.pagopa.wispconverter.client.checkout.invoker java8 @@ -395,20 +396,17 @@ ${project.basedir}/openapi-client/decoupler-caching/openapi.json - java Dto - false false - resttemplate - - it.gov.pagopa.decouplercachingclient - it.gov.pagopa.decouplercachingclient.model - it.gov.pagopa.decouplercachingclient.api - it.gov.pagopa.decouplercachingclient.client + it.gov.pagopa.wispconverter.client.decouplercaching + it.gov.pagopa.wispconverter.client.decouplercaching.model + it.gov.pagopa.wispconverter.client.decouplercaching.api + it.gov.pagopa.wispconverter.client.decouplercaching.invoker + java8 diff --git a/scripts/update-specs.sh b/scripts/update-specs.sh index 7fa71e23..1b15d8ad 100644 --- a/scripts/update-specs.sh +++ b/scripts/update-specs.sh @@ -23,6 +23,6 @@ echo "\n------------------------------------------------------------------------ echo "Downloaded all files!" echo "\n------------------------------------------------------------------------" echo "Compiling class from new WSDL and XSD files..." -mvn clean package -Dmaven.test.skip=true +mvn clean compile echo "\n\nEnded compilation!" echo "\n------------------------------------------------------------------------" \ No newline at end of file diff --git a/src/main/java/it/gov/pagopa/wispconverter/config/CosmosDBConfig.java b/src/main/java/it/gov/pagopa/wispconverter/config/CosmosDBConfig.java index 92a4d1a0..d91dda39 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/config/CosmosDBConfig.java +++ b/src/main/java/it/gov/pagopa/wispconverter/config/CosmosDBConfig.java @@ -22,7 +22,7 @@ @EnableCosmosRepositories("it.gov.pagopa.wispconverter.repository") @EnableConfigurationProperties @EnableCosmosAuditing -@ConditionalOnExpression("'${properties.environment}'!='test'") +@ConditionalOnExpression("'${info.properties.environment}'!='test'") @Slf4j public class CosmosDBConfig extends AbstractCosmosConfiguration { diff --git a/src/main/java/it/gov/pagopa/wispconverter/config/RedisConfig.java b/src/main/java/it/gov/pagopa/wispconverter/config/RedisConfig.java index f82cb60c..a4edf240 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/config/RedisConfig.java +++ b/src/main/java/it/gov/pagopa/wispconverter/config/RedisConfig.java @@ -32,8 +32,7 @@ public LettuceConnectionFactory registerRedisConnectionFactory() { return new LettuceConnectionFactory(redisConfiguration, lettuceConfig); } - @Bean - @Qualifier("redisSimpleTemplate") + @Bean(name="redisSimpleTemplate") public RedisTemplate registerRedisSimpleTemplate(final LettuceConnectionFactory connectionFactory, ObjectMapper objectMapper) { RedisTemplate template = new RedisTemplate<>(); template.setKeySerializer(new StringRedisSerializer()); diff --git a/src/main/java/it/gov/pagopa/wispconverter/config/ScheduledJobsConfig.java b/src/main/java/it/gov/pagopa/wispconverter/config/ScheduledJobsConfig.java new file mode 100644 index 00000000..2b786d28 --- /dev/null +++ b/src/main/java/it/gov/pagopa/wispconverter/config/ScheduledJobsConfig.java @@ -0,0 +1,28 @@ +package it.gov.pagopa.wispconverter.config; + +import it.gov.pagopa.wispconverter.service.ConfigCacheService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; + +import java.time.ZonedDateTime; + +@Configuration +@Slf4j +@EnableScheduling +public class ScheduledJobsConfig { + + private final ConfigCacheService configCacheService; + public ScheduledJobsConfig(ConfigCacheService configCacheService){ + this.configCacheService = configCacheService; + } + + @Scheduled(cron = "${wisp-converter-cache.refresh.cron:-}") + public void refreshCache() { + log.info("[Scheduled] Starting configuration refresh {}", ZonedDateTime.now()); + configCacheService.loadCache(); + } + +} diff --git a/src/main/java/it/gov/pagopa/wispconverter/config/client/APIConfigCacheClientConfig.java b/src/main/java/it/gov/pagopa/wispconverter/config/client/APIConfigCacheClientConfig.java new file mode 100644 index 00000000..869abfc2 --- /dev/null +++ b/src/main/java/it/gov/pagopa/wispconverter/config/client/APIConfigCacheClientConfig.java @@ -0,0 +1,113 @@ +package it.gov.pagopa.wispconverter.config.client; + +import it.gov.pagopa.wispconverter.client.cache.invoker.ApiClient; +import it.gov.pagopa.wispconverter.service.ReService; +import it.gov.pagopa.wispconverter.util.client.MDCInterceptor; +import it.gov.pagopa.wispconverter.util.client.ReInterceptor; +import it.gov.pagopa.wispconverter.util.client.apiconfigcache.ApiConfigCacheClientLoggingInterceptor; +import it.gov.pagopa.wispconverter.util.client.apiconfigcache.ApiConfigCacheClientResponseErrorHandler; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.BufferingClientHttpRequestFactory; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.http.client.ClientHttpRequestInterceptor; +import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.DefaultUriBuilderFactory; + +import java.util.List; + +@Configuration +@Slf4j +@RequiredArgsConstructor +public class APIConfigCacheClientConfig { + private final ReService reService; + + @Value("${client.cache.read-timeout}") + private Integer readTimeout; + + @Value("${client.cache.connect-timeout}") + private Integer connectTimeout; + + @Value("${client.cache.base-path}") + private String basePath; + + @Value("${client.cache.api-key}") + private String apiKey; + + @Value("${log.client.cache.request.include-headers}") + private boolean clientRequestIncludeHeaders; + @Value("${log.client.cache.request.include-payload}") + private boolean clientRequestIncludePayload; + @Value("${log.client.cache.request.max-payload-length}") + private int clientRequestMaxLength; + @Value("${log.client.cache.response.include-headers}") + private boolean clientResponseIncludeHeaders; + @Value("${log.client.cache.response.include-payload}") + private boolean clientResponseIncludePayload; + @Value("${log.client.cache.response.max-payload-length}") + private int clientResponseMaxLength; + + @Value("${log.client.cache.mask.header.name}") + private String maskHeaderName; + + @Value("${log.client.cache.request.pretty}") + private boolean clientRequestPretty; + + @Value("${log.client.cache.response.pretty}") + private boolean clientResponsePretty; + + + @Bean + public ApiClient configCacheClient() { + ApiConfigCacheClientLoggingInterceptor clientLogging = new ApiConfigCacheClientLoggingInterceptor(); + clientLogging.setRequestIncludeHeaders(clientRequestIncludeHeaders); + clientLogging.setRequestIncludePayload(clientRequestIncludePayload); + clientLogging.setRequestMaxPayloadLength(clientRequestMaxLength); + clientLogging.setRequestHeaderPredicate(p -> !p.equals(maskHeaderName)); + clientLogging.setRequestPretty(clientRequestPretty); + + clientLogging.setResponseIncludeHeaders(clientResponseIncludeHeaders); + clientLogging.setResponseIncludePayload(clientResponseIncludePayload); + clientLogging.setResponseMaxPayloadLength(clientResponseMaxLength); + clientLogging.setResponsePretty(clientResponsePretty); + + RestTemplate restTemplate = restTemplate(); + + List currentInterceptors = restTemplate.getInterceptors(); + currentInterceptors.add(new MDCInterceptor()); + currentInterceptors.add(new ReInterceptor(reService)); + currentInterceptors.add(clientLogging); + restTemplate.setInterceptors(currentInterceptors); + + restTemplate.setErrorHandler(new ApiConfigCacheClientResponseErrorHandler()); + + ApiClient client = new ApiClient(restTemplate); + client.setBasePath(basePath); +// client.setApiKey(apiKey); + + return client; + } + + private RestTemplate restTemplate() { + RestTemplate restTemplate = new RestTemplate(); + // This allows us to read the response more than once - Necessary for debugging. + restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(getSimpleClientHttpRequestFactory(restTemplate.getRequestFactory()))); + + // disable default URL encoding + DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory(); + uriBuilderFactory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.VALUES_ONLY); + restTemplate.setUriTemplateHandler(uriBuilderFactory); + return restTemplate; + } + + private SimpleClientHttpRequestFactory getSimpleClientHttpRequestFactory(ClientHttpRequestFactory requestFactory) { + SimpleClientHttpRequestFactory simpleClientHttpRequestFactory = (SimpleClientHttpRequestFactory) requestFactory; + simpleClientHttpRequestFactory.setConnectTimeout(this.connectTimeout); + simpleClientHttpRequestFactory.setReadTimeout(this.readTimeout); + return simpleClientHttpRequestFactory; + } +} \ No newline at end of file diff --git a/src/main/java/it/gov/pagopa/wispconverter/config/client/CheckoutClientConfig.java b/src/main/java/it/gov/pagopa/wispconverter/config/client/CheckoutClientConfig.java index 30eb74b7..5b3b60c9 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/config/client/CheckoutClientConfig.java +++ b/src/main/java/it/gov/pagopa/wispconverter/config/client/CheckoutClientConfig.java @@ -1,5 +1,6 @@ package it.gov.pagopa.wispconverter.config.client; +import it.gov.pagopa.wispconverter.client.checkout.invoker.ApiClient; import it.gov.pagopa.wispconverter.service.ReService; import it.gov.pagopa.wispconverter.util.client.MDCInterceptor; import it.gov.pagopa.wispconverter.util.client.ReInterceptor; @@ -61,7 +62,7 @@ public class CheckoutClientConfig { private boolean clientResponsePretty; @Bean - public it.gov.pagopa.checkoutclient.client.ApiClient checkoutClient() { + public ApiClient checkoutClient() { CheckoutClientLoggingInterceptor clientLogging = new CheckoutClientLoggingInterceptor(); clientLogging.setRequestIncludeHeaders(clientRequestIncludeHeaders); clientLogging.setRequestIncludePayload(clientRequestIncludePayload); @@ -84,7 +85,7 @@ public it.gov.pagopa.checkoutclient.client.ApiClient checkoutClient() { restTemplate.setErrorHandler(new CheckoutClientResponseErrorHandler()); - it.gov.pagopa.checkoutclient.client.ApiClient client = new it.gov.pagopa.checkoutclient.client.ApiClient(restTemplate); + ApiClient client = new ApiClient(restTemplate); client.setBasePath(basePath); // client.setApiKey(apiKey); @@ -109,4 +110,4 @@ private SimpleClientHttpRequestFactory getSimpleClientHttpRequestFactory(ClientH simpleClientHttpRequestFactory.setReadTimeout(this.readTimeout); return simpleClientHttpRequestFactory; } -} +} \ No newline at end of file diff --git a/src/main/java/it/gov/pagopa/wispconverter/config/client/DecouplerCachingClientConfig.java b/src/main/java/it/gov/pagopa/wispconverter/config/client/DecouplerCachingClientConfig.java index 290b9ec5..c1dfcb12 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/config/client/DecouplerCachingClientConfig.java +++ b/src/main/java/it/gov/pagopa/wispconverter/config/client/DecouplerCachingClientConfig.java @@ -1,5 +1,6 @@ package it.gov.pagopa.wispconverter.config.client; +import it.gov.pagopa.wispconverter.client.decouplercaching.invoker.ApiClient; import it.gov.pagopa.wispconverter.service.ReService; import it.gov.pagopa.wispconverter.util.client.MDCInterceptor; import it.gov.pagopa.wispconverter.util.client.ReInterceptor; @@ -60,7 +61,7 @@ public class DecouplerCachingClientConfig { private boolean clientResponsePretty; @Bean - public it.gov.pagopa.decouplercachingclient.client.ApiClient decouplerCachingClient() { + public ApiClient decouplerCachingClient() { DecouplerCachingClientLoggingInterceptor clientLogging = new DecouplerCachingClientLoggingInterceptor(); clientLogging.setRequestIncludeHeaders(clientRequestIncludeHeaders); clientLogging.setRequestIncludePayload(clientRequestIncludePayload); @@ -83,7 +84,7 @@ public it.gov.pagopa.decouplercachingclient.client.ApiClient decouplerCachingCli restTemplate.setErrorHandler(new DecouplerCachingClientResponseErrorHandler()); - it.gov.pagopa.decouplercachingclient.client.ApiClient client = new it.gov.pagopa.decouplercachingclient.client.ApiClient(restTemplate); + ApiClient client = new ApiClient(restTemplate); client.setBasePath(basePath); client.setApiKey(apiKey); @@ -109,4 +110,4 @@ private SimpleClientHttpRequestFactory getSimpleClientHttpRequestFactory(ClientH simpleClientHttpRequestFactory.setReadTimeout(this.readTimeout); return simpleClientHttpRequestFactory; } -} +} \ No newline at end of file diff --git a/src/main/java/it/gov/pagopa/wispconverter/config/client/GpdClientConfig.java b/src/main/java/it/gov/pagopa/wispconverter/config/client/GpdClientConfig.java index ccbf3def..c2af6545 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/config/client/GpdClientConfig.java +++ b/src/main/java/it/gov/pagopa/wispconverter/config/client/GpdClientConfig.java @@ -1,5 +1,6 @@ package it.gov.pagopa.wispconverter.config.client; +import it.gov.pagopa.wispconverter.client.gpd.invoker.ApiClient; import it.gov.pagopa.wispconverter.service.ReService; import it.gov.pagopa.wispconverter.util.client.MDCInterceptor; import it.gov.pagopa.wispconverter.util.client.ReInterceptor; @@ -60,7 +61,7 @@ public class GpdClientConfig { private boolean clientResponsePretty; @Bean - public it.gov.pagopa.gpdclient.client.ApiClient gpdClient() { + public ApiClient gpdClient() { GpdClientLoggingInterceptor clientLogging = new GpdClientLoggingInterceptor(); clientLogging.setRequestIncludeHeaders(clientRequestIncludeHeaders); clientLogging.setRequestIncludePayload(clientRequestIncludePayload); @@ -83,7 +84,7 @@ public it.gov.pagopa.gpdclient.client.ApiClient gpdClient() { restTemplate.setErrorHandler(new GpdClientResponseErrorHandler()); - it.gov.pagopa.gpdclient.client.ApiClient client = new it.gov.pagopa.gpdclient.client.ApiClient(restTemplate); + ApiClient client = new ApiClient(restTemplate); client.setBasePath(basePath); client.setApiKey(apiKey); @@ -109,4 +110,4 @@ private SimpleClientHttpRequestFactory getSimpleClientHttpRequestFactory(ClientH simpleClientHttpRequestFactory.setReadTimeout(this.readTimeout); return simpleClientHttpRequestFactory; } -} +} \ No newline at end of file diff --git a/src/main/java/it/gov/pagopa/wispconverter/config/client/IuvGeneratorClientConfig.java b/src/main/java/it/gov/pagopa/wispconverter/config/client/IuvGeneratorClientConfig.java index 73c48581..07fab5d2 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/config/client/IuvGeneratorClientConfig.java +++ b/src/main/java/it/gov/pagopa/wispconverter/config/client/IuvGeneratorClientConfig.java @@ -1,10 +1,11 @@ package it.gov.pagopa.wispconverter.config.client; +import it.gov.pagopa.wispconverter.client.iuvgenerator.invoker.ApiClient; import it.gov.pagopa.wispconverter.service.ReService; import it.gov.pagopa.wispconverter.util.client.MDCInterceptor; +import it.gov.pagopa.wispconverter.util.client.ReInterceptor; import it.gov.pagopa.wispconverter.util.client.iuvgenerator.IuvGeneratorClientLoggingInterceptor; import it.gov.pagopa.wispconverter.util.client.iuvgenerator.IuvGeneratorClientResponseErrorHandler; -import it.gov.pagopa.wispconverter.util.client.ReInterceptor; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -61,7 +62,7 @@ public class IuvGeneratorClientConfig { @Bean - public it.gov.pagopa.iuvgeneratorclient.client.ApiClient iuvGeneratorClient() { + public ApiClient iuvGeneratorClient() { IuvGeneratorClientLoggingInterceptor clientLogging = new IuvGeneratorClientLoggingInterceptor(); clientLogging.setRequestIncludeHeaders(clientRequestIncludeHeaders); clientLogging.setRequestIncludePayload(clientRequestIncludePayload); @@ -84,7 +85,7 @@ public it.gov.pagopa.iuvgeneratorclient.client.ApiClient iuvGeneratorClient() { restTemplate.setErrorHandler(new IuvGeneratorClientResponseErrorHandler()); - it.gov.pagopa.iuvgeneratorclient.client.ApiClient client = new it.gov.pagopa.iuvgeneratorclient.client.ApiClient(restTemplate); + ApiClient client = new ApiClient(restTemplate); client.setBasePath(basePath); // client.setApiKey(apiKey); @@ -109,4 +110,4 @@ private SimpleClientHttpRequestFactory getSimpleClientHttpRequestFactory(ClientH simpleClientHttpRequestFactory.setReadTimeout(this.readTimeout); return simpleClientHttpRequestFactory; } -} +} \ No newline at end of file diff --git a/src/main/java/it/gov/pagopa/wispconverter/controller/RedirectController.java b/src/main/java/it/gov/pagopa/wispconverter/controller/RedirectController.java index c496638d..9e4d9f98 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/controller/RedirectController.java +++ b/src/main/java/it/gov/pagopa/wispconverter/controller/RedirectController.java @@ -12,7 +12,6 @@ import it.gov.pagopa.wispconverter.exception.AppErrorCodeMessageEnum; import it.gov.pagopa.wispconverter.exception.AppException; import it.gov.pagopa.wispconverter.service.ConverterService; -import it.gov.pagopa.wispconverter.service.model.ConversionResultDTO; import it.gov.pagopa.wispconverter.util.Constants; import it.gov.pagopa.wispconverter.util.ErrorUtil; import jakarta.servlet.http.HttpServletResponse; @@ -57,21 +56,21 @@ public String redirect(@Parameter(description = "", example = "identificativoInt @NotBlank(message = "{redirect.session-id.not-blank}") @RequestParam("sessionId") String sessionId, Model model, HttpServletResponse response) { - try{ + try { String redirectURI = converterService.convert(sessionId); return "redirect:" + redirectURI; - } catch (AppException appException){ + } catch (AppException appException) { ErrorResponse errorResponse = errorUtil.forAppException(appException); ProblemDetail problemDetail = errorResponse.updateAndGetBody(this.messageSource, LocaleContextHolder.getLocale()); errorUtil.finalizeError(problemDetail, errorResponse.getStatusCode().value()); response.setStatus(errorResponse.getStatusCode().value()); - model.addAttribute("sessionId",sessionId); + model.addAttribute("sessionId", sessionId); enrichModelWithError(model, problemDetail, errorResponse.getStatusCode().value()); return "error"; } catch (Exception ex) { String operationId = MDC.get(Constants.MDC_OPERATION_ID); - log.error(String.format("GenericException: operation-id=[%s]", operationId!=null?operationId:"n/a"), ex); + log.error(String.format("GenericException: operation-id=[%s]", operationId != null ? operationId : "n/a"), ex); AppException appException = new AppException(ex, AppErrorCodeMessageEnum.ERROR, ex.getMessage()); ErrorResponse errorResponse = errorUtil.forAppException(appException); @@ -79,7 +78,7 @@ public String redirect(@Parameter(description = "", example = "identificativoInt errorUtil.finalizeError(problemDetail, errorResponse.getStatusCode().value()); response.setStatus(errorResponse.getStatusCode().value()); - model.addAttribute("sessionId",sessionId); + model.addAttribute("sessionId", sessionId); enrichModelWithError(model, problemDetail, errorResponse.getStatusCode().value()); return "error"; } @@ -92,33 +91,33 @@ public String redirect(@Parameter(description = "", example = "identificativoInt }) @GetMapping(value = "/redirect", consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity redirectInfo(@Parameter(description = "", example = "identificativoIntermediarioPA_sessionId") - @NotBlank(message = "{redirect.session-id.not-blank}") - @RequestParam("sessionId") String sessionId) { + @NotBlank(message = "{redirect.session-id.not-blank}") + @RequestParam("sessionId") String sessionId) { String redirectURI = converterService.convert(sessionId); return ResponseEntity.ok(RedirectResponse.builder().redirectUrl(redirectURI).build()); } - private void enrichModelWithError(Model model, ProblemDetail problemDetail, int statusCode){ - model.addAttribute("type",problemDetail.getType()); - model.addAttribute("title",problemDetail.getTitle()); - model.addAttribute("status",statusCode); - model.addAttribute("detail",problemDetail.getDetail()); + private void enrichModelWithError(Model model, ProblemDetail problemDetail, int statusCode) { + model.addAttribute("type", problemDetail.getType()); + model.addAttribute("title", problemDetail.getTitle()); + model.addAttribute("status", statusCode); + model.addAttribute("detail", problemDetail.getDetail()); Map properties = problemDetail.getProperties(); - if(properties!=null){ + if (properties != null) { Instant timestamp = (Instant) properties.get(ErrorUtil.EXTRA_FIELD_ERROR_TIMESTAMP); - if(timestamp!=null){ - model.addAttribute("timestamp",timestamp.toString()); + if (timestamp != null) { + model.addAttribute("timestamp", timestamp.toString()); } String errrCode = (String) properties.get(ErrorUtil.EXTRA_FIELD_ERROR_CODE); - if(errrCode!=null){ - model.addAttribute("errorCode",errrCode); + if (errrCode != null) { + model.addAttribute("errorCode", errrCode); } String operationId = (String) properties.get(ErrorUtil.EXTRA_FIELD_OPERATION_ID); - if(operationId!=null){ - model.addAttribute("operationId",operationId); + if (operationId != null) { + model.addAttribute("operationId", operationId); } } } diff --git a/src/main/java/it/gov/pagopa/wispconverter/controller/advice/GlobalExceptionHandler.java b/src/main/java/it/gov/pagopa/wispconverter/controller/advice/GlobalExceptionHandler.java index 049e1a65..d4332561 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/controller/advice/GlobalExceptionHandler.java +++ b/src/main/java/it/gov/pagopa/wispconverter/controller/advice/GlobalExceptionHandler.java @@ -2,32 +2,23 @@ import it.gov.pagopa.wispconverter.exception.AppErrorCodeMessageEnum; import it.gov.pagopa.wispconverter.exception.AppException; -import it.gov.pagopa.wispconverter.util.CommonUtility; import it.gov.pagopa.wispconverter.util.Constants; import it.gov.pagopa.wispconverter.util.ErrorUtil; import it.gov.pagopa.wispconverter.util.MDCUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.slf4j.MDC; -import org.springframework.beans.factory.annotation.Value; import org.springframework.context.MessageSource; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatusCode; import org.springframework.http.ProblemDetail; import org.springframework.http.ResponseEntity; -import org.springframework.lang.Nullable; -import org.springframework.util.Assert; import org.springframework.web.ErrorResponse; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.context.request.WebRequest; import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; -import org.springframework.web.util.DefaultUriBuilderFactory; - -import java.net.URI; -import java.time.Instant; -import java.util.Map; /** * All Exceptions are handled by this class diff --git a/src/main/java/it/gov/pagopa/wispconverter/exception/AppErrorCodeMessageEnum.java b/src/main/java/it/gov/pagopa/wispconverter/exception/AppErrorCodeMessageEnum.java index 344194e5..a6122ff4 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/exception/AppErrorCodeMessageEnum.java +++ b/src/main/java/it/gov/pagopa/wispconverter/exception/AppErrorCodeMessageEnum.java @@ -6,15 +6,33 @@ @Getter public enum AppErrorCodeMessageEnum { - ERROR ( 500, "System error", "{0}", HttpStatus.INTERNAL_SERVER_ERROR), - PARSE_ERROR (1000, "Parse error", "Error while parsing: {0}", HttpStatus.BAD_REQUEST), - PRIMITIVE_NOT_VALID (1001, "Primitive not valid", "Primitive [{0}] not valid", HttpStatus.NOT_ACCEPTABLE), - RPT_NOT_FOUND (1002, "RPT not found", "RPT with sessionId [{0}] not found", HttpStatus.NOT_FOUND), - CLIENT_IUV_GENERATOR (1003, "IUVGeneratorClient error", "IUVGeneratorClient - {0}", HttpStatus.EXPECTATION_FAILED), - CLIENT_GPD (1004, "GPDClient error", "GPDClient - {0}", HttpStatus.EXPECTATION_FAILED), - CLIENT_DECOUPLER_CACHING(1005, "DecouplerCachingClient error", "DecouplerCachingClient - {0}", HttpStatus.EXPECTATION_FAILED), - CLIENT_CHECKOUT (1006, "CheckoutClient error", "CheckoutClient - {0}", HttpStatus.EXPECTATION_FAILED), - UNZIP (1007, "Unzip error", "{0}", HttpStatus.INTERNAL_SERVER_ERROR), + ERROR(500, "System error", "{0}", HttpStatus.INTERNAL_SERVER_ERROR), + // --- Internal logic errors --- + GENERIC_ERROR(1000, "Generic flow error", "Error while executing conversion flow. {0}", HttpStatus.UNPROCESSABLE_ENTITY), + PARSING_GENERIC_ERROR(1001, "Generic parsing error", "Error while parsing payload. {0}", HttpStatus.BAD_REQUEST), + PARSING_INVALID_HEADER(1002, "SOAP Header parsing error", "Error while parsing payload. The SOAP header in payload is invalid: {0}", HttpStatus.BAD_REQUEST), + PARSING_INVALID_BODY(1003, "SOAP Body parsing error", "Error while parsing payload. The SOAP body in payload is invalid: {0}", HttpStatus.BAD_REQUEST), + PARSING_INVALID_XML_NODES(1004, "XML parsing error", "Error while parsing payload. The list of nodes extracted from document must be greater than zero, but currently it is zero.", HttpStatus.BAD_REQUEST), + PARSING_INVALID_ZIPPED_PAYLOAD(1005, "ZIP extraction error", "Error while parsing payload. Cannot unzip payload correctly.", HttpStatus.BAD_REQUEST), + PARSING_PRIMITIVE_NOT_VALID(1006, "Primitive not valid", "Error while checking primitive. Primitive [{0}] not valid.", HttpStatus.NOT_ACCEPTABLE), + VALIDATION_INVALID_MULTIBENEFICIARY_CART(1100, "RPTs not valid", "Error while generating debt position for GPD service. The cart is defined as multi-beneficiary but there are a number of RPTs lower than 2.", HttpStatus.BAD_REQUEST), + VALIDATION_INVALID_IBANS(1101, "IBANs not valid", "Error while generating debt position for GPD service. The IBAN field must be set if digital stamp is not defined for the transfer.", HttpStatus.BAD_REQUEST), + VALIDATION_INVALID_DEBTOR(1102, "Debtor subject not valid", "Error while generating debt position for GPD service. The debtor subject information is different between the various RPT of the cart.", HttpStatus.BAD_REQUEST), + VALIDATION_INVALID_CREDITOR_INSTITUTION(1103, "Creditor institution not valid", "Error while generating debt position for GPD service. The creditor institution information is different between the various RPT of the cart.", HttpStatus.BAD_REQUEST), + CONFIGURATION_INVALID_STATION(1200, "Station not valid", "Error while generating cart for Checkout service. No valid station found with code [{0}].", HttpStatus.NOT_FOUND), + CONFIGURATION_INVALID_STATION_REDIRECT_URL(1201, "Station not valid", "Error while generating cart for Checkout service. The station with code [{0}] contains wrong redirect URL and it is not possible to generate valid URI.", HttpStatus.NOT_FOUND), + // --- DB and storage interaction errors --- + PERSISTENCE_RPT_NOT_FOUND(2000, "RPT not found", "Error while retrieving RPT. RPT with sessionId [{0}] not found.", HttpStatus.NOT_FOUND), + PERSISTENCE_REQUESTID_CACHING_ERROR(2001, "RequestID caching error", "Error while caching RequestID. {0}", HttpStatus.UNPROCESSABLE_ENTITY), + // --- Client errors --- + CLIENT_APICONFIGCACHE(3000, "APIConfig cache client error", "Error while communicating with APIConfig cache service. {0}", HttpStatus.EXPECTATION_FAILED), + CLIENT_GPD(3001, "GPD client error", "Error while communicating with GPD service. {0}", HttpStatus.EXPECTATION_FAILED), + CLIENT_IUVGENERATOR(3002, "IUV Generator client error", "Error while communicating with IUV Generator service. {0}", HttpStatus.EXPECTATION_FAILED), + CLIENT_DECOUPLER_CACHING(3003, "Decoupler caching client error", "Error while communicating with decoupler caching API. {0}", HttpStatus.EXPECTATION_FAILED), + CLIENT_CHECKOUT(3004, "Checkout error", "Error while communicating with Checkout service. {0}", HttpStatus.EXPECTATION_FAILED), + CLIENT_CHECKOUT_NO_REDIRECT_LOCATION(3005, "Checkout redirect error", "Error while communicating with Checkout service. No valid 'Location' header was found,", HttpStatus.EXPECTATION_FAILED), + CLIENT_CHECKOUT_INVALID_REDIRECT_LOCATION(3006, "Checkout redirect error", "Error while communicating with Checkout service. An empty 'Location' header was found.", HttpStatus.EXPECTATION_FAILED), + ; private final Integer code; diff --git a/src/main/java/it/gov/pagopa/wispconverter/service/CacheService.java b/src/main/java/it/gov/pagopa/wispconverter/service/CacheService.java index e6e929cc..d428faef 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/service/CacheService.java +++ b/src/main/java/it/gov/pagopa/wispconverter/service/CacheService.java @@ -1,15 +1,21 @@ package it.gov.pagopa.wispconverter.service; +import io.lettuce.core.RedisException; +import it.gov.pagopa.wispconverter.client.decouplercaching.api.DefaultApi; +import it.gov.pagopa.wispconverter.client.decouplercaching.model.DecouplerCachingKeysDto; +import it.gov.pagopa.wispconverter.exception.AppErrorCodeMessageEnum; +import it.gov.pagopa.wispconverter.exception.AppException; import it.gov.pagopa.wispconverter.repository.CacheRepository; -import it.gov.pagopa.wispconverter.service.model.RPTContentDTO; +import it.gov.pagopa.wispconverter.service.model.CommonRPTFieldsDTO; +import it.gov.pagopa.wispconverter.service.model.PaymentNoticeContentDTO; import it.gov.pagopa.wispconverter.util.Constants; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.slf4j.MDC; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import org.springframework.web.client.RestClientException; -import java.util.Collections; import java.util.List; @Service @@ -21,31 +27,36 @@ public class CacheService { private static final String CACHING_KEY_TEMPLATE = "wisp_" + COMPOSITE_TWOVALUES_KEY_TEMPLATE; - private final it.gov.pagopa.decouplercachingclient.client.ApiClient decouplerCachingClient; + private final it.gov.pagopa.wispconverter.client.decouplercaching.invoker.ApiClient decouplerCachingClient; private final CacheRepository cacheRepository; @Value("${wisp-converter.cached-requestid-mapping.ttl.minutes}") private Long requestIDMappingTTL; + public void storeRequestMappingInCache(CommonRPTFieldsDTO commonRPTFieldsDTO, String sessionId) { + try { + String idIntermediarioPA = commonRPTFieldsDTO.getCreditorInstitutionBrokerId(); + List noticeNumbers = commonRPTFieldsDTO.getPaymentNotices().stream() + .map(PaymentNoticeContentDTO::getNoticeNumber) + .toList(); - public void storeRequestMappingInCache(List rptContentDTOs, String sessionId) { - rptContentDTOs.forEach(e -> { - String idIntermediarioPA = e.getIdIntermediarioPA(); - String noticeNumber = e.getNoticeNumber(); - String requestIDForDecoupler = String.format(COMPOSITE_TWOVALUES_KEY_TEMPLATE, idIntermediarioPA, noticeNumber); // TODO can be optimized in a single request??? - - it.gov.pagopa.decouplercachingclient.model.DecouplerCachingKeysDto decouplerCachingKeysDto = new it.gov.pagopa.decouplercachingclient.model.DecouplerCachingKeysDto(); - decouplerCachingKeysDto.setKeys(Collections.singletonList(requestIDForDecoupler)); - - String xRequestId = MDC.get(Constants.MDC_REQUEST_ID); - - it.gov.pagopa.decouplercachingclient.api.DefaultApi apiInstance = new it.gov.pagopa.decouplercachingclient.api.DefaultApi(decouplerCachingClient); - apiInstance.saveMapping(decouplerCachingKeysDto, xRequestId); + // communicating with APIM policy for caching data for decoupler + DecouplerCachingKeysDto decouplerCachingKeys = new DecouplerCachingKeysDto(); + noticeNumbers.forEach(noticeNumber -> decouplerCachingKeys.addKeysItem(String.format(COMPOSITE_TWOVALUES_KEY_TEMPLATE, idIntermediarioPA, noticeNumber))); + DefaultApi apiInstance = new DefaultApi(decouplerCachingClient); + apiInstance.saveMapping(decouplerCachingKeys, MDC.get(Constants.MDC_REQUEST_ID)); // save in Redis cache the mapping of the request identifier needed for RT generation in next steps - String requestIDForRTHandling = String.format(CACHING_KEY_TEMPLATE, idIntermediarioPA, noticeNumber); - this.cacheRepository.insert(requestIDForRTHandling, sessionId, this.requestIDMappingTTL); - }); + for (String noticeNumber : noticeNumbers) { + String requestIDForRTHandling = String.format(CACHING_KEY_TEMPLATE, idIntermediarioPA, noticeNumber); + this.cacheRepository.insert(requestIDForRTHandling, sessionId, this.requestIDMappingTTL); + } + + } catch (RestClientException e) { + throw new AppException(e, AppErrorCodeMessageEnum.CLIENT_DECOUPLER_CACHING, e.getMessage()); + } catch (RedisException e) { + throw new AppException(e, AppErrorCodeMessageEnum.PERSISTENCE_REQUESTID_CACHING_ERROR, e.getMessage()); + } } } diff --git a/src/main/java/it/gov/pagopa/wispconverter/service/CheckoutService.java b/src/main/java/it/gov/pagopa/wispconverter/service/CheckoutService.java index cd955eec..8c95ef85 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/service/CheckoutService.java +++ b/src/main/java/it/gov/pagopa/wispconverter/service/CheckoutService.java @@ -1,24 +1,87 @@ package it.gov.pagopa.wispconverter.service; +import it.gov.pagopa.wispconverter.client.cache.model.ConfigDataV1Dto; +import it.gov.pagopa.wispconverter.client.cache.model.RedirectDto; +import it.gov.pagopa.wispconverter.client.cache.model.StationDto; +import it.gov.pagopa.wispconverter.client.checkout.api.PaymentRequestsApi; +import it.gov.pagopa.wispconverter.client.checkout.invoker.ApiClient; +import it.gov.pagopa.wispconverter.client.checkout.model.CartRequestDto; +import it.gov.pagopa.wispconverter.client.checkout.model.CartRequestReturnUrlsDto; +import it.gov.pagopa.wispconverter.exception.AppErrorCodeMessageEnum; +import it.gov.pagopa.wispconverter.exception.AppException; +import it.gov.pagopa.wispconverter.service.mapper.CartMapper; +import it.gov.pagopa.wispconverter.service.model.CommonRPTFieldsDTO; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatusCode; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; +import org.springframework.web.client.RestClientException; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Collection; +import java.util.Map; @Service @Slf4j @RequiredArgsConstructor public class CheckoutService { - private final it.gov.pagopa.checkoutclient.client.ApiClient checkoutClient; + private final ApiClient checkoutClient; + + private final ConfigCacheService configCacheService; + + private final CartMapper mapper; + + public String executeCall(CommonRPTFieldsDTO commonRPTFieldsDTO) { - public String executeCall() { + String location; - // execute mapping for Checkout carts invocation + try { + // execute mapping for Checkout carts invocation + CartRequestDto cart = mapper.toCart(commonRPTFieldsDTO); + String stationRedirectURL = ""; // FIXME on next API version will be added the stationID so -> getRedirectURL(cart.getStationId()); + CartRequestReturnUrlsDto returnUrls = new CartRequestReturnUrlsDto(); + returnUrls.setReturnOkUrl(new URI(stationRedirectURL + "/success.html")); + returnUrls.setReturnCancelUrl(new URI(stationRedirectURL + "/cancel.html")); + returnUrls.setReturnErrorUrl(new URI(stationRedirectURL + "/error.html")); + cart.setReturnUrls(returnUrls); - // call Checkout carts API, receive Checkout response and returns redirection URI + PaymentRequestsApi apiInstance = new PaymentRequestsApi(checkoutClient); + ResponseEntity response = apiInstance.postCartsWithHttpInfo(cart); + + HttpStatusCode status = response.getStatusCode(); + if (status.value() != 302) { + throw new AppException(AppErrorCodeMessageEnum.CLIENT_CHECKOUT, "The response retrieved from Checkout is not '302 Found'."); + } + Collection locationHeader = response.getHeaders().get("location"); + if (locationHeader == null) { + throw new AppException(AppErrorCodeMessageEnum.CLIENT_CHECKOUT_NO_REDIRECT_LOCATION); + } + location = locationHeader.stream().findFirst().orElseThrow(() -> new AppException(AppErrorCodeMessageEnum.CLIENT_CHECKOUT_INVALID_REDIRECT_LOCATION)); + + } catch (RestClientException e) { + throw new AppException(AppErrorCodeMessageEnum.CLIENT_CHECKOUT, e.getMessage()); + } catch (URISyntaxException e) { + throw new AppException(AppErrorCodeMessageEnum.CONFIGURATION_INVALID_STATION_REDIRECT_URL); + } + + return location; + } - // generate uri + private String getRedirectURL(String stationId) { + ConfigDataV1Dto cache = configCacheService.getCache(); + Map stations = cache.getStations(); + StationDto station = stations.get(stationId); + if (station == null) { + throw new AppException(AppErrorCodeMessageEnum.CONFIGURATION_INVALID_STATION, stationId); + } + RedirectDto redirect = station.getRedirect(); + String protocol = redirect.getProtocol() == null ? "http" : redirect.getProtocol().getValue().toLowerCase(); + String url = redirect.getIp() + "/" + redirect.getPath(); + url = url.replace("//", "/"); - return null; + return protocol + "://" + url; } } diff --git a/src/main/java/it/gov/pagopa/wispconverter/service/ConfigCacheService.java b/src/main/java/it/gov/pagopa/wispconverter/service/ConfigCacheService.java new file mode 100644 index 00000000..402acbe5 --- /dev/null +++ b/src/main/java/it/gov/pagopa/wispconverter/service/ConfigCacheService.java @@ -0,0 +1,36 @@ +package it.gov.pagopa.wispconverter.service; + +import it.gov.pagopa.wispconverter.client.cache.api.CacheApi; +import it.gov.pagopa.wispconverter.client.cache.invoker.ApiClient; +import it.gov.pagopa.wispconverter.client.cache.model.ConfigDataV1Dto; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +@Service +@Slf4j +@RequiredArgsConstructor +public class ConfigCacheService { + + private final ApiClient configCacheClient; + + private ConfigDataV1Dto configData; + + + public ConfigDataV1Dto getCache() { + if (configData == null) { + loadCache(); + } + return configData; + } + + public void loadCache() { + log.info("loadCache from cache api"); + try { + CacheApi apiInstance = new CacheApi(configCacheClient); + configData = apiInstance.cache(); + } catch (Exception e) { + log.error("Cannot get cache", e); + } + } +} diff --git a/src/main/java/it/gov/pagopa/wispconverter/service/ConverterService.java b/src/main/java/it/gov/pagopa/wispconverter/service/ConverterService.java index 58e6b5af..5704df28 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/service/ConverterService.java +++ b/src/main/java/it/gov/pagopa/wispconverter/service/ConverterService.java @@ -1,28 +1,14 @@ package it.gov.pagopa.wispconverter.service; -import gov.telematici.pagamenti.ws.NodoInviaCarrelloRPT; -import gov.telematici.pagamenti.ws.NodoInviaRPT; -import gov.telematici.pagamenti.ws.ppthead.IntestazioneCarrelloPPT; -import gov.telematici.pagamenti.ws.ppthead.IntestazionePPT; -import it.gov.digitpa.schemas._2011.pagamenti.CtRichiestaPagamentoTelematico; import it.gov.pagopa.wispconverter.exception.AppErrorCodeMessageEnum; import it.gov.pagopa.wispconverter.exception.AppException; import it.gov.pagopa.wispconverter.repository.RPTRequestRepository; import it.gov.pagopa.wispconverter.repository.model.RPTRequestEntity; -import it.gov.pagopa.wispconverter.service.model.ConversionResultDTO; -import it.gov.pagopa.wispconverter.service.model.RPTContentDTO; -import it.gov.pagopa.wispconverter.util.FileReader; -import it.gov.pagopa.wispconverter.util.JaxbElementUtil; -import it.gov.pagopa.wispconverter.util.ZipUtil; +import it.gov.pagopa.wispconverter.service.model.CommonRPTFieldsDTO; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; -import org.w3c.dom.Element; -import org.xmlsoap.schemas.soap.envelope.Envelope; -import java.io.IOException; -import java.util.Collections; -import java.util.List; import java.util.Optional; @Service @@ -30,7 +16,7 @@ @RequiredArgsConstructor public class ConverterService { - private final NAVGeneratorService navGeneratorService; + private final RPTExtractorService rptExtractorService; private final DebtPositionService debtPositionService; @@ -40,85 +26,28 @@ public class ConverterService { private final RPTRequestRepository rptRequestRepository; - private final JaxbElementUtil jaxbElementUtil; - public String convert(String sessionId) { - // get request entity from CosmosDB + // get RPT request entity from database RPTRequestEntity rptRequestEntity = getRPTRequestEntity(sessionId); - // unmarshalling header and body from request entity - List rptContentDTOs = getRPTContentDTO(rptRequestEntity.getPrimitive(), rptRequestEntity.getPayload()); + // unmarshalling and mapping RPT content from request entity + CommonRPTFieldsDTO commonRPTFieldsDTO = this.rptExtractorService.extractRPTContentDTOs(rptRequestEntity.getPrimitive(), rptRequestEntity.getPayload()); - // extracting creditor institution code from header and call GPD bulk creation API - this.debtPositionService.executeBulkCreation(rptContentDTOs); + // calling GPD creation API in order to generate the debt position associated to RPTs + this.debtPositionService.createDebtPositions(commonRPTFieldsDTO); // call APIM policy for save key for decoupler and save in Redis cache the mapping of the request identifier needed for RT generation in next steps - this.cacheService.storeRequestMappingInCache(rptContentDTOs, sessionId); + this.cacheService.storeRequestMappingInCache(commonRPTFieldsDTO, sessionId); // execute communication with Checkout service and set the redirection URI as response - String redirectURI = this.checkoutService.executeCall(); - return redirectURI; + return this.checkoutService.executeCall(commonRPTFieldsDTO); } - - private RPTRequestEntity getRPTRequestEntity(String sessionId) { Optional optRPTReqEntity = this.rptRequestRepository.findById(sessionId); - return optRPTReqEntity.orElseThrow(() -> new AppException(AppErrorCodeMessageEnum.RPT_NOT_FOUND, sessionId)); + return optRPTReqEntity.orElseThrow(() -> new AppException(AppErrorCodeMessageEnum.PERSISTENCE_RPT_NOT_FOUND, sessionId)); // TODO RE } - - private List getRPTContentDTO(String primitive, String payload) { - - byte[] payloadUnzipped = new byte[0]; - try { - payloadUnzipped = ZipUtil.unzip(ZipUtil.base64Decode(payload)); - } catch (IOException e) { - throw new AppException(e, AppErrorCodeMessageEnum.UNZIP, e.getMessage()); - } - Element envelopeElement = jaxbElementUtil.convertToEnvelopeElement(payloadUnzipped); - Envelope envelope = jaxbElementUtil.convertToBean(envelopeElement, Envelope.class); - - switch (primitive) { - case "nodoInviaRPT" -> { - IntestazionePPT soapHeader = jaxbElementUtil.getSoapHeader(envelope, IntestazionePPT.class); - NodoInviaRPT soapBody = jaxbElementUtil.getSoapBody(envelope, NodoInviaRPT.class); - String idDominio = soapHeader.getIdentificativoDominio(); - return Collections.singletonList(RPTContentDTO.builder() - .idDominio(idDominio) - .noticeNumber(this.navGeneratorService.getNAVCodeFromIUVGenerator(idDominio)) - .rpt(getRPT(soapBody.getRpt())) - .build()); - } - case "nodoInviaCarrelloRPT" -> { - IntestazioneCarrelloPPT soapHeader = jaxbElementUtil.getSoapHeader(envelope, IntestazioneCarrelloPPT.class); - NodoInviaCarrelloRPT soapBody = jaxbElementUtil.getSoapBody(envelope, NodoInviaCarrelloRPT.class); - - return soapBody.getListaRPT().getElementoListaRPT().stream().map(a -> { - boolean isMultibeneficiario = soapBody.isMultiBeneficiario(); - CtRichiestaPagamentoTelematico rpt = getRPT(a.getRpt()); - String idDominio = isMultibeneficiario ? - soapHeader.getIdentificativoCarrello().substring(0, 11) : - rpt.getDominio().getIdentificativoDominio(); - - return RPTContentDTO.builder() - .idDominio(idDominio) - .noticeNumber(this.navGeneratorService.getNAVCodeFromIUVGenerator(idDominio)) - .multibeneficiario(isMultibeneficiario) - .rpt(rpt) - .build(); - }).toList(); - } - default -> throw new AppException(AppErrorCodeMessageEnum.PRIMITIVE_NOT_VALID, primitive); - } - } - - private CtRichiestaPagamentoTelematico getRPT(byte[] rptBytes) { - Element rptElement = jaxbElementUtil.convertToRPTElement(rptBytes); - return jaxbElementUtil.convertToBean(rptElement, CtRichiestaPagamentoTelematico.class); - } - - } diff --git a/src/main/java/it/gov/pagopa/wispconverter/service/DebtPositionService.java b/src/main/java/it/gov/pagopa/wispconverter/service/DebtPositionService.java index 29fdf316..ccf261d3 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/service/DebtPositionService.java +++ b/src/main/java/it/gov/pagopa/wispconverter/service/DebtPositionService.java @@ -1,46 +1,247 @@ package it.gov.pagopa.wispconverter.service; +import it.gov.pagopa.wispconverter.client.gpd.api.DebtPositionsApiApi; +import it.gov.pagopa.wispconverter.client.gpd.model.*; +import it.gov.pagopa.wispconverter.client.iuvgenerator.api.IuvGeneratorApiApi; +import it.gov.pagopa.wispconverter.client.iuvgenerator.model.IuvGenerationModelDto; +import it.gov.pagopa.wispconverter.client.iuvgenerator.model.IuvGenerationModelResponseDto; +import it.gov.pagopa.wispconverter.exception.AppErrorCodeMessageEnum; +import it.gov.pagopa.wispconverter.exception.AppException; import it.gov.pagopa.wispconverter.service.mapper.DebtPositionMapper; -import it.gov.pagopa.wispconverter.service.model.RPTContentDTO; +import it.gov.pagopa.wispconverter.service.model.*; +import it.gov.pagopa.wispconverter.service.model.paymentrequest.PaymentRequestDTO; import it.gov.pagopa.wispconverter.util.Constants; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.slf4j.MDC; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import org.springframework.web.client.RestClientException; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.LinkedList; import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; +import java.util.UUID; +import java.util.regex.Matcher; +import java.util.regex.Pattern; @Service @Slf4j @RequiredArgsConstructor public class DebtPositionService { - private final it.gov.pagopa.gpdclient.client.ApiClient gpdClient; + private final it.gov.pagopa.wispconverter.client.gpd.invoker.ApiClient gpdClient; + + private final it.gov.pagopa.wispconverter.client.iuvgenerator.invoker.ApiClient iuvGeneratorClient; private final DebtPositionMapper mapper; - public void executeBulkCreation(List rptContentDTOs) { + private final Pattern taxonomyPattern = Pattern.compile("([^/]++/[^/]++)/?"); + + @Value("${wisp-converter.poste-italiane.abi-code}") + private String posteItalianeABICode; + + @Value("${wisp-converter.aux-digit}") + private Long auxDigit; - Map> paymentPositionsByDomain = rptContentDTOs.stream().collect(Collectors.groupingBy(RPTContentDTO::getIdDominio)); + @Value("${wisp-converter.segregation-code}") + private Long segregationCode; - paymentPositionsByDomain.forEach((idDominio, value) -> { - List paymentPositionModelDtoList = value.stream().map(mapper::toPaymentPosition).toList(); + public void createDebtPositions(CommonRPTFieldsDTO rptContentDTOs) { - // generating request - it.gov.pagopa.gpdclient.model.MultiplePaymentPositionModelDto multiplePaymentPositionModelDto = new it.gov.pagopa.gpdclient.model.MultiplePaymentPositionModelDto(); - multiplePaymentPositionModelDto.setPaymentPositions(paymentPositionModelDtoList); + try { + // converting RPTs in single payment position + MultiplePaymentPositionModelDto multiplePaymentPositions = extractPaymentPositions(rptContentDTOs); - String xRequestId = MDC.get(Constants.MDC_REQUEST_ID); - Boolean toPublish = true; + // communicating with GPD-core service in order to execute the operation + DebtPositionsApiApi apiInstance = new DebtPositionsApiApi(gpdClient); + apiInstance.createMultiplePositions1(rptContentDTOs.getCreditorInstitutionId(), multiplePaymentPositions, MDC.get(Constants.MDC_REQUEST_ID), true); - it.gov.pagopa.gpdclient.api.DebtPositionsApiApi apiInstance = new it.gov.pagopa.gpdclient.api.DebtPositionsApiApi(gpdClient); - apiInstance.createMultiplePositions1(idDominio, multiplePaymentPositionModelDto, xRequestId, toPublish); //FIXME gestire errori di connessione //FIXME cosa succede se si spacca al secondo giro? - }); + } catch (RestClientException e) { + throw new AppException(AppErrorCodeMessageEnum.CLIENT_GPD, e.getMessage()); + } + } + + private MultiplePaymentPositionModelDto extractPaymentPositions(CommonRPTFieldsDTO commonRPTFieldsDTO) { + + List paymentPositions; + if (Boolean.TRUE.equals(commonRPTFieldsDTO.getIsMultibeneficiary())) { + paymentPositions = extractPaymentPositionsForMultibeneficiary(commonRPTFieldsDTO); + } else { + paymentPositions = extractPaymentPositionsForNonMultibeneficiary(commonRPTFieldsDTO); + } + + MultiplePaymentPositionModelDto multiplePaymentPosition = new MultiplePaymentPositionModelDto(); + multiplePaymentPosition.setPaymentPositions(paymentPositions); + return multiplePaymentPosition; + } + + private List extractPaymentPositionsForMultibeneficiary(CommonRPTFieldsDTO commonRPTFieldsDTO) { + + if (commonRPTFieldsDTO.getRpts().size() < 2) { + throw new AppException(AppErrorCodeMessageEnum.VALIDATION_INVALID_MULTIBENEFICIARY_CART); + } + RPTContentDTO firstRPTContentDTO = commonRPTFieldsDTO.getRpts().get(0); + + // mapping of transfers + List transfers = new ArrayList<>(); + for (RPTContentDTO rptContentDTO : commonRPTFieldsDTO.getRpts()) { + + PaymentRequestDTO paymentRequestDTO = rptContentDTO.getRpt(); + + int transferIdCounter = 1; + for (TransferDTO transferDTO : paymentRequestDTO.getTransferData().getTransfer()) { + + transfers.add(extractPaymentOptionTransfer(transferDTO, paymentRequestDTO.getDomain().getDomainId(), transferIdCounter)); + transferIdCounter++; + } + } + + // generating notice number and add to common RPT fields + String noticeNumber = getNAVCodeFromIUVGenerator(commonRPTFieldsDTO.getCreditorInstitutionId()); + + // mapping of payment option + Long amount = commonRPTFieldsDTO.getRpts().stream() + .map(rptContentDTO -> rptContentDTO.getRpt().getTransferData().getTotalAmount()) + .reduce(BigDecimal.valueOf(0L), BigDecimal::add) + .longValue() * 100; + PaymentOptionModelDto paymentOption = mapper.toPaymentOption(firstRPTContentDTO); + paymentOption.setNav(noticeNumber); + paymentOption.setAmount(amount); + paymentOption.setTransfer(transfers); + + // mapping of payment position + PaymentPositionModelDto paymentPosition = mapper.toPaymentPosition(commonRPTFieldsDTO); + paymentPosition.setIupd(calculateIUPD(commonRPTFieldsDTO.getCreditorInstitutionId())); + paymentPosition.setPaymentOption(List.of(paymentOption)); + + // update payment notices to be used for communication with Checkout + commonRPTFieldsDTO.getPaymentNotices().add(PaymentNoticeContentDTO.builder() + .noticeNumber(noticeNumber) + .fiscalCode(firstRPTContentDTO.getRpt().getDomain().getDomainId()) + .amount(amount) + .build()); + + return List.of(paymentPosition); } + private List extractPaymentPositionsForNonMultibeneficiary(CommonRPTFieldsDTO commonRPTFieldsDTO) { + List paymentPositions = new LinkedList<>(); + + List paymentNotices = commonRPTFieldsDTO.getPaymentNotices(); + + for (RPTContentDTO rptContentDTO : commonRPTFieldsDTO.getRpts()) { + + PaymentRequestDTO paymentRequestDTO = rptContentDTO.getRpt(); + + // mapping of transfers + int transferIdCounter = 1; + List transfers = new ArrayList<>(); + for (TransferDTO transferDTO : paymentRequestDTO.getTransferData().getTransfer()) { + + transfers.add(extractPaymentOptionTransfer(transferDTO, null, transferIdCounter)); + transferIdCounter++; + } + // generating notice number and add to common RPT fields + String noticeNumber = getNAVCodeFromIUVGenerator(commonRPTFieldsDTO.getCreditorInstitutionId()); + + // mapping of payment option + Long amount = paymentRequestDTO.getTransferData().getTotalAmount().longValue() * 100; + PaymentOptionModelDto paymentOption = mapper.toPaymentOption(rptContentDTO); + paymentOption.setAmount(amount); + paymentOption.setNav(noticeNumber); + paymentOption.setTransfer(transfers); + + // mapping of payment position + PaymentPositionModelDto paymentPosition = mapper.toPaymentPosition(commonRPTFieldsDTO); + paymentPosition.setIupd(calculateIUPD(commonRPTFieldsDTO.getCreditorInstitutionId())); + paymentPosition.setPaymentOption(List.of(paymentOption)); + paymentPositions.add(paymentPosition); + + // update payment notices to be used for communication with Checkout + paymentNotices.add(PaymentNoticeContentDTO.builder() + .noticeNumber(noticeNumber) + .fiscalCode(paymentRequestDTO.getDomain().getDomainId()) + .amount(amount) + .build()); + } + return paymentPositions; + } + + private String getNAVCodeFromIUVGenerator(String creditorInstitutionCode) { + + String navCode; + try { + IuvGenerationModelDto request = new IuvGenerationModelDto(); + request.setAuxDigit(this.auxDigit); + request.setSegregationCode(this.segregationCode); + + // communicating with IUV Generator service in order to retrieve response + IuvGeneratorApiApi apiInstance = new IuvGeneratorApiApi(iuvGeneratorClient); + IuvGenerationModelResponseDto response = apiInstance.generateIUV(creditorInstitutionCode, request); + + navCode = response.getIuv(); + } catch (RestClientException e) { + throw new AppException(AppErrorCodeMessageEnum.CLIENT_IUVGENERATOR, e.getMessage()); + } + return navCode; + } + + private TransferModelDto extractPaymentOptionTransfer(TransferDTO transferDTO, String organizationFiscalCode, int transferIdCounter) { + + // definition of standard transfer metadata + TransferMetadataModelDto transferMetadata = new TransferMetadataModelDto(); + transferMetadata.setKey("DatiSpecificiRiscossione"); + transferMetadata.setValue(transferDTO.getCategory()); + + // common definition for the transfer + TransferModelDto transfer = new TransferModelDto(); + transfer.setIdTransfer(TransferModelDto.IdTransferEnum.fromValue(String.valueOf(transferIdCounter))); + transfer.setAmount(transferDTO.getAmount().longValue() * 100); + transfer.setRemittanceInformation(transferDTO.getRemittanceInformation()); + transfer.setCategory(getTaxonomy(transferDTO)); + transfer.setTransferMetadata(List.of(transferMetadata)); + + /* + If digital stamp exists, it is a special transfer that does not require IBANs. + If digital stamp doesn't exists, it is a common transfer that needs IBAN and needs the explicit setting of the organization fiscal code. + */ + DigitalStampDTO digitalStampDTO = transferDTO.getDigitalStamp(); + if (digitalStampDTO != null) { + + transfer.setStamp(mapper.toStamp(digitalStampDTO)); + } else { + + String iban = transferDTO.getCreditIban(); + if (iban == null) { + throw new AppException(AppErrorCodeMessageEnum.VALIDATION_INVALID_IBANS); + } + transfer.setIban(iban); + transfer.setPostalIban(isPostalIBAN(iban) ? iban : null); + transfer.setOrganizationFiscalCode(organizationFiscalCode); + } + + return transfer; + } + + private String getTaxonomy(TransferDTO transferDTO) { + String taxonomy = transferDTO.getCategory(); + Matcher matcher = taxonomyPattern.matcher(taxonomy); + if (matcher.find()) { + taxonomy = matcher.group(1); + } + return taxonomy; + } + + private boolean isPostalIBAN(String iban) { + return iban != null && iban.substring(5, 10).equals(posteItalianeABICode); + } + + private String calculateIUPD(String creditorInstitutionBroker) { + return "wisp_" + creditorInstitutionBroker + "_" + UUID.randomUUID(); + } } diff --git a/src/main/java/it/gov/pagopa/wispconverter/service/NAVGeneratorService.java b/src/main/java/it/gov/pagopa/wispconverter/service/NAVGeneratorService.java deleted file mode 100644 index 97a5da9a..00000000 --- a/src/main/java/it/gov/pagopa/wispconverter/service/NAVGeneratorService.java +++ /dev/null @@ -1,31 +0,0 @@ -package it.gov.pagopa.wispconverter.service; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; - -@Service -@Slf4j -@RequiredArgsConstructor -public class NAVGeneratorService { - - private final it.gov.pagopa.iuvgeneratorclient.client.ApiClient iuvGeneratorClient; - - @Value("${wisp-converter.aux-digit}") - private String auxDigit; - - @Value("${wisp-converter.segregation-code}") - private String segregationCode; - - public String getNAVCodeFromIUVGenerator(String creditorInstitutionCode) { - it.gov.pagopa.iuvgeneratorclient.api.IuvGeneratorApiApi apiInstance = new it.gov.pagopa.iuvgeneratorclient.api.IuvGeneratorApiApi(iuvGeneratorClient); - it.gov.pagopa.iuvgeneratorclient.model.IuvGenerationModelDto iuvGenerationModelDto = new it.gov.pagopa.iuvgeneratorclient.model.IuvGenerationModelDto(); - iuvGenerationModelDto.setAuxDigit(Long.valueOf(this.auxDigit)); - iuvGenerationModelDto.setSegregationCode(Long.valueOf(this.segregationCode)); - - it.gov.pagopa.iuvgeneratorclient.model.IuvGenerationModelResponseDto result = apiInstance.generateIUV(creditorInstitutionCode, iuvGenerationModelDto); - return result.getIuv(); - //FIXME gestire errori di connessione - } -} diff --git a/src/main/java/it/gov/pagopa/wispconverter/service/RPTExtractorService.java b/src/main/java/it/gov/pagopa/wispconverter/service/RPTExtractorService.java new file mode 100644 index 00000000..331c4ff0 --- /dev/null +++ b/src/main/java/it/gov/pagopa/wispconverter/service/RPTExtractorService.java @@ -0,0 +1,164 @@ +package it.gov.pagopa.wispconverter.service; + +import gov.telematici.pagamenti.ws.NodoInviaCarrelloRPT; +import gov.telematici.pagamenti.ws.NodoInviaRPT; +import gov.telematici.pagamenti.ws.TipoElementoListaRPT; +import gov.telematici.pagamenti.ws.ppthead.IntestazioneCarrelloPPT; +import gov.telematici.pagamenti.ws.ppthead.IntestazionePPT; +import it.gov.digitpa.schemas._2011.pagamenti.CtRichiestaPagamentoTelematico; +import it.gov.pagopa.wispconverter.exception.AppErrorCodeMessageEnum; +import it.gov.pagopa.wispconverter.exception.AppException; +import it.gov.pagopa.wispconverter.service.mapper.RPTMapper; +import it.gov.pagopa.wispconverter.service.model.CommonRPTFieldsDTO; +import it.gov.pagopa.wispconverter.service.model.RPTContentDTO; +import it.gov.pagopa.wispconverter.service.model.paymentrequest.PaymentRequestDTO; +import it.gov.pagopa.wispconverter.util.JaxbElementUtil; +import it.gov.pagopa.wispconverter.util.ZipUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.w3c.dom.Element; +import org.xmlsoap.schemas.soap.envelope.Envelope; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +@Service +@Slf4j +@RequiredArgsConstructor +public class RPTExtractorService { + + private final JaxbElementUtil jaxbElementUtil; + + private final RPTMapper mapper; + + public CommonRPTFieldsDTO extractRPTContentDTOs(String primitive, String payload) { + + Envelope envelope; + try { + byte[] payloadUnzipped = ZipUtil.unzip(ZipUtil.base64Decode(payload)); + Element envelopeElement = this.jaxbElementUtil.convertToEnvelopeElement(payloadUnzipped); + envelope = this.jaxbElementUtil.convertToBean(envelopeElement, Envelope.class); + } catch (IOException e) { + throw new AppException(AppErrorCodeMessageEnum.PARSING_INVALID_ZIPPED_PAYLOAD); + } + + CommonRPTFieldsDTO commonRPTFieldsDTO; + switch (primitive) { + case "nodoInviaRPT" -> commonRPTFieldsDTO = extractRPTContentDTOsFromNodoInviaRPT(envelope); + case "nodoInviaCarrelloRPT" -> commonRPTFieldsDTO = extractRPTContentDTOsFromNodoInviaCarrelloRPT(envelope); + default -> throw new AppException(AppErrorCodeMessageEnum.PARSING_PRIMITIVE_NOT_VALID); + } + return commonRPTFieldsDTO; + } + + private CommonRPTFieldsDTO extractRPTContentDTOsFromNodoInviaRPT(Envelope envelope) { + IntestazionePPT soapHeader = this.jaxbElementUtil.getSoapHeader(envelope, IntestazionePPT.class); + NodoInviaRPT soapBody = this.jaxbElementUtil.getSoapBody(envelope, NodoInviaRPT.class); + + String creditorInstitutionId = soapHeader.getIdentificativoDominio(); + PaymentRequestDTO rpt = extractRPT(soapBody.getRpt()); + boolean containsDigitalStamp = rpt.getTransferData().getTransfer().stream().anyMatch(transfer -> transfer.getDigitalStamp() != null); + + return CommonRPTFieldsDTO.builder() + .creditorInstitutionId(creditorInstitutionId) + .creditorInstitutionBrokerId(soapHeader.getIdentificativoIntermediarioPA()) + .stationId(soapHeader.getIdentificativoStazioneIntermediarioPA()) + .payerType(rpt.getPayer().getSubjectUniqueIdentifier().getType()) + .payerFiscalCode(rpt.getPayer().getSubjectUniqueIdentifier().getCode()) + .payerFullName(rpt.getPayer().getName()) + .isMultibeneficiary(false) + .containsDigitalStamp(containsDigitalStamp) + .paymentNotices(new ArrayList<>()) + .rpts(Collections.singletonList(RPTContentDTO.builder() + .iupd(soapHeader.getIdentificativoIntermediarioPA() + soapHeader.getIdentificativoUnivocoVersamento()) + .iuv(rpt.getTransferData().getIuv()) + .rpt(rpt) + .containsDigitalStamp(containsDigitalStamp) + .build())) + .build(); + } + + private CommonRPTFieldsDTO extractRPTContentDTOsFromNodoInviaCarrelloRPT(Envelope envelope) { + IntestazioneCarrelloPPT soapHeader = this.jaxbElementUtil.getSoapHeader(envelope, IntestazioneCarrelloPPT.class); + NodoInviaCarrelloRPT soapBody = this.jaxbElementUtil.getSoapBody(envelope, NodoInviaCarrelloRPT.class); + + // initializing common fields + boolean isMultibeneficiary = soapBody.isMultiBeneficiario() !=null && soapBody.isMultiBeneficiario(); + String creditorInstitutionId = null; + String payerType = null; + String payerFiscalCode = null; + String fullName = null; + String streetName = null; + String streetNumber = null; + String postalCode = null; + String city = null; + String province = null; + String nation = null; + String email = null; + + List rptContentDTOs = new LinkedList<>(); + for (TipoElementoListaRPT elementoListaRPT : soapBody.getListaRPT().getElementoListaRPT()) { + + // generating RPT + PaymentRequestDTO rpt = extractRPT(elementoListaRPT.getRpt()); + // validating common fields + creditorInstitutionId = isMultibeneficiary ? + soapHeader.getIdentificativoCarrello().substring(0, 11) : + checkUniqueness(creditorInstitutionId, rpt.getDomain().getDomainId(), AppErrorCodeMessageEnum.VALIDATION_INVALID_CREDITOR_INSTITUTION); + payerType = checkUniqueness(payerType, rpt.getPayer().getSubjectUniqueIdentifier().getType(), AppErrorCodeMessageEnum.VALIDATION_INVALID_DEBTOR); + payerFiscalCode = checkUniqueness(payerFiscalCode, rpt.getPayer().getSubjectUniqueIdentifier().getCode(), AppErrorCodeMessageEnum.VALIDATION_INVALID_DEBTOR); + fullName = checkUniqueness(fullName, rpt.getPayer().getName(), AppErrorCodeMessageEnum.VALIDATION_INVALID_DEBTOR); + streetName = checkUniqueness(streetName, rpt.getPayer().getAddress(), AppErrorCodeMessageEnum.VALIDATION_INVALID_DEBTOR); + streetNumber = checkUniqueness(streetNumber, rpt.getPayer().getStreetNumber(), AppErrorCodeMessageEnum.VALIDATION_INVALID_DEBTOR); + postalCode = checkUniqueness(postalCode, rpt.getPayer().getPostalCode(), AppErrorCodeMessageEnum.VALIDATION_INVALID_DEBTOR); + city = checkUniqueness(city, rpt.getPayer().getCity(), AppErrorCodeMessageEnum.VALIDATION_INVALID_DEBTOR); + province = checkUniqueness(province, rpt.getPayer().getProvince(), AppErrorCodeMessageEnum.VALIDATION_INVALID_DEBTOR); + nation = checkUniqueness(nation, rpt.getPayer().getNation(), AppErrorCodeMessageEnum.VALIDATION_INVALID_DEBTOR); + email = checkUniqueness(email, rpt.getPayer().getEmail(), AppErrorCodeMessageEnum.VALIDATION_INVALID_DEBTOR); + // generating RPT content DTO + rptContentDTOs.add(RPTContentDTO.builder() + .iupd(soapHeader.getIdentificativoIntermediarioPA() + soapHeader.getIdentificativoCarrello()) + .iuv(rpt.getTransferData().getIuv()) + .containsDigitalStamp(rpt.getTransferData().getTransfer().stream().anyMatch(transfer -> transfer.getDigitalStamp() != null)) + .rpt(rpt) + .build()); + } + + return CommonRPTFieldsDTO.builder() + .cartId(soapHeader.getIdentificativoCarrello()) + .creditorInstitutionId(creditorInstitutionId) + .creditorInstitutionBrokerId(soapHeader.getIdentificativoIntermediarioPA()) + .stationId(soapHeader.getIdentificativoStazioneIntermediarioPA()) + .payerType(payerType) + .payerFiscalCode(payerFiscalCode) + .payerFullName(fullName) + .payerAddressStreetName(streetName) + .payerAddressStreetNumber(streetNumber) + .payerAddressPostalCode(postalCode) + .payerAddressCity(city) + .payerAddressProvince(province) + .payerAddressNation(nation) + .payerEmail(email) + .isMultibeneficiary(isMultibeneficiary) + .containsDigitalStamp(rptContentDTOs.stream().anyMatch(RPTContentDTO::getContainsDigitalStamp)) + .paymentNotices(new ArrayList<>()) + .rpts(rptContentDTOs) + .build(); + } + + private T checkUniqueness(T existingValue, T newValue, AppErrorCodeMessageEnum error) { + if (existingValue != null && !existingValue.equals(newValue)) { + throw new AppException(error); + } + return newValue; + } + + private PaymentRequestDTO extractRPT(byte[] rptBytes) { + Element rptElement = this.jaxbElementUtil.convertToRPTElement(rptBytes); + return mapper.toPaymentRequestDTO(this.jaxbElementUtil.convertToBean(rptElement, CtRichiestaPagamentoTelematico.class)); + } +} diff --git a/src/main/java/it/gov/pagopa/wispconverter/service/ReService.java b/src/main/java/it/gov/pagopa/wispconverter/service/ReService.java index ca2578c3..0aec5391 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/service/ReService.java +++ b/src/main/java/it/gov/pagopa/wispconverter/service/ReService.java @@ -1,6 +1,5 @@ package it.gov.pagopa.wispconverter.service; -import it.gov.pagopa.wispconverter.service.model.ConversionResultDTO; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -10,6 +9,6 @@ @RequiredArgsConstructor public class ReService { public void addRe(String dir) { - log.info("\n#################\n# RE INTERFACE "+dir+"\n#################"); + log.info("\n#################\n# RE INTERFACE " + dir + "\n#################"); } } diff --git a/src/main/java/it/gov/pagopa/wispconverter/service/mapper/CartMapper.java b/src/main/java/it/gov/pagopa/wispconverter/service/mapper/CartMapper.java new file mode 100644 index 00000000..c6a93c28 --- /dev/null +++ b/src/main/java/it/gov/pagopa/wispconverter/service/mapper/CartMapper.java @@ -0,0 +1,26 @@ +package it.gov.pagopa.wispconverter.service.mapper; + +import it.gov.pagopa.wispconverter.client.checkout.model.CartRequestDto; +import it.gov.pagopa.wispconverter.client.checkout.model.PaymentNoticeDto; +import it.gov.pagopa.wispconverter.service.model.CommonRPTFieldsDTO; +import it.gov.pagopa.wispconverter.service.model.PaymentNoticeContentDTO; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ReportingPolicy; + +@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface CartMapper { + + @Mapping(source = "cartId", target = "idCart") + @Mapping(source = "payerEmail", target = "emailNotice") + @Mapping(target = "allCCP", constant = "false") + //@Mapping(source = "stationId", target = "stationId") TODO to be added on new API version + CartRequestDto toCart(CommonRPTFieldsDTO commonRPTFieldsDTO); + + @Mapping(source = "noticeNumber", target = "noticeNumber") + @Mapping(source = "fiscalCode", target = "fiscalCode") + @Mapping(source = "amount", target = "amount") + @Mapping(target = "companyName", constant = "null") + @Mapping(target = "description", constant = "null") + PaymentNoticeDto toPaymentNotice(PaymentNoticeContentDTO paymentNoticeContentDTO); +} diff --git a/src/main/java/it/gov/pagopa/wispconverter/service/mapper/DebtPositionMapper.java b/src/main/java/it/gov/pagopa/wispconverter/service/mapper/DebtPositionMapper.java index 41c77eb6..13c8919b 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/service/mapper/DebtPositionMapper.java +++ b/src/main/java/it/gov/pagopa/wispconverter/service/mapper/DebtPositionMapper.java @@ -1,11 +1,45 @@ package it.gov.pagopa.wispconverter.service.mapper; +import it.gov.pagopa.wispconverter.client.gpd.model.PaymentOptionModelDto; +import it.gov.pagopa.wispconverter.client.gpd.model.PaymentPositionModelDto; +import it.gov.pagopa.wispconverter.client.gpd.model.StampDto; +import it.gov.pagopa.wispconverter.service.model.CommonRPTFieldsDTO; +import it.gov.pagopa.wispconverter.service.model.DigitalStampDTO; import it.gov.pagopa.wispconverter.service.model.RPTContentDTO; import org.mapstruct.Mapper; +import org.mapstruct.Mapping; import org.mapstruct.ReportingPolicy; @Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE) public interface DebtPositionMapper { - it.gov.pagopa.gpdclient.model.PaymentPositionModelDto toPaymentPosition(RPTContentDTO rptContentDTO); + PaymentPositionModelDto toPaymentPosition(RPTContentDTO rptContentDTO); + + @Mapping(source = "payerType", target = "type") + @Mapping(source = "payerFiscalCode", target = "fiscalCode") + @Mapping(source = "payerFullName", target = "fullName") + @Mapping(source = "payerAddressStreetName", target = "streetName") + @Mapping(source = "payerAddressStreetNumber", target = "civicNumber") + @Mapping(source = "payerAddressPostalCode", target = "postalCode") + @Mapping(source = "payerAddressCity", target = "city") + @Mapping(source = "payerAddressProvince", target = "province") + @Mapping(source = "payerAddressNation", target = "country") + @Mapping(source = "payerEmail", target = "email") + @Mapping(source = "payerFullName", target = "companyName") + @Mapping(target = "validityDate", expression = "java(null)") + @Mapping(target = "switchToExpired", constant = "true") + PaymentPositionModelDto toPaymentPosition(CommonRPTFieldsDTO commonRPTFieldsDTO); + + @Mapping(source = "iuv", target = "iuv") + @Mapping(target = "description", constant = "-") + @Mapping(target = "isPartialPayment", constant = "false") + @Mapping(target = "retentionDate", expression = "java(null)") + @Mapping(target = "fee", constant = "0L") + @Mapping(target = "dueDate", expression = "java(java.time.OffsetDateTime.now().plusDays(1))") + PaymentOptionModelDto toPaymentOption(RPTContentDTO rptContentDTO); + + @Mapping(target = "hashDocument", expression = "java(new String(digitalStampDTO.getDocumentHash()))") + @Mapping(source = "type", target = "stampType") + @Mapping(source = "province", target = "provincialResidence") + StampDto toStamp(DigitalStampDTO digitalStampDTO); } diff --git a/src/main/java/it/gov/pagopa/wispconverter/service/mapper/RPTMapper.java b/src/main/java/it/gov/pagopa/wispconverter/service/mapper/RPTMapper.java new file mode 100644 index 00000000..b9e16b4a --- /dev/null +++ b/src/main/java/it/gov/pagopa/wispconverter/service/mapper/RPTMapper.java @@ -0,0 +1,99 @@ +package it.gov.pagopa.wispconverter.service.mapper; + +import it.gov.digitpa.schemas._2011.pagamenti.*; +import it.gov.pagopa.wispconverter.service.model.*; +import it.gov.pagopa.wispconverter.service.model.paymentrequest.PaymentRequestDTO; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ReportingPolicy; + +@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface RPTMapper { + + @Mapping(source = "versioneOggetto", target = "version") + @Mapping(source = "dominio", target = "domain") + @Mapping(source = "identificativoMessaggioRichiesta", target = "messageRequestId") + @Mapping(source = "dataOraMessaggioRichiesta", target = "messageRequestDatetime") + @Mapping(source = "autenticazioneSoggetto", target = "subjectAuthentication") + @Mapping(source = "soggettoVersante", target = "debtor") + @Mapping(source = "soggettoPagatore", target = "payer") + @Mapping(source = "enteBeneficiario", target = "payeeInstitution") + @Mapping(source = "datiVersamento", target = "transferData") + PaymentRequestDTO toPaymentRequestDTO(CtRichiestaPagamentoTelematico rpt); + + @Mapping(source = "identificativoDominio", target = "domainId") + @Mapping(source = "identificativoStazioneRichiedente", target = "stationId") + PaymentRequestDomainDTO toPaymentRequestDomainDTO(CtDominio dominio); + + @Mapping(source = "identificativoUnivocoPagatore", target = "subjectUniqueIdentifier") + @Mapping(source = "anagraficaPagatore", target = "name") + @Mapping(source = "indirizzoPagatore", target = "address") + @Mapping(source = "civicoPagatore", target = "streetNumber") + @Mapping(source = "capPagatore", target = "postalCode") + @Mapping(source = "localitaPagatore", target = "city") + @Mapping(source = "provinciaPagatore", target = "province") + @Mapping(source = "nazionePagatore", target = "nation") + @Mapping(source = "EMailPagatore", target = "email") + PaymentSubjectDTO toPayerPaymentSubjectDTO(CtSoggettoPagatore soggettoPagatore); + + @Mapping(source = "identificativoUnivocoVersante", target = "subjectUniqueIdentifier") + @Mapping(source = "anagraficaVersante", target = "name") + @Mapping(source = "indirizzoVersante", target = "address") + @Mapping(source = "civicoVersante", target = "streetNumber") + @Mapping(source = "capVersante", target = "postalCode") + @Mapping(source = "localitaVersante", target = "city") + @Mapping(source = "provinciaVersante", target = "province") + @Mapping(source = "nazioneVersante", target = "nation") + @Mapping(source = "EMailVersante", target = "email") + PaymentSubjectDTO toDebtorPaymentSubjectDTO(CtSoggettoVersante soggettoVersante); + + @Mapping(source = "identificativoUnivocoBeneficiario", target = "subjectUniqueIdentifier") + @Mapping(source = "denominazioneBeneficiario", target = "name") + @Mapping(source = "codiceUnitOperBeneficiario", target = "operUnitCode") + @Mapping(source = "denomUnitOperBeneficiario", target = "operUnitDenom") + @Mapping(source = "indirizzoBeneficiario", target = "address") + @Mapping(source = "civicoBeneficiario", target = "streetNumber") + @Mapping(source = "capBeneficiario", target = "postalCode") + @Mapping(source = "localitaBeneficiario", target = "city") + @Mapping(source = "provinciaBeneficiario", target = "province") + @Mapping(source = "nazioneBeneficiario", target = "nation") + PaymentSubjectDTO toPaymentPayeeInstitutionDTO(CtEnteBeneficiario enteBeneficiario); + + @Mapping(source = "tipoIdentificativoUnivoco", target = "type") + @Mapping(source = "codiceIdentificativoUnivoco", target = "code") + SubjectUniqueIdentifierDTO toIdentificativoUnivocoPersonaFGDTO(CtIdentificativoUnivocoPersonaFG identificativoUnivoco); + + @Mapping(source = "tipoIdentificativoUnivoco", target = "type") + @Mapping(source = "codiceIdentificativoUnivoco", target = "code") + SubjectUniqueIdentifierDTO toIdentificativoUnivocoPersonaGDTO(CtIdentificativoUnivocoPersonaG identificativoUnivoco); + + + @Mapping(source = "dataEsecuzionePagamento", target = "paymentDate") + @Mapping(source = "importoTotaleDaVersare", target = "totalAmount") + @Mapping(source = "tipoVersamento", target = "type") + @Mapping(source = "identificativoUnivocoVersamento", target = "iuv") + @Mapping(source = "codiceContestoPagamento", target = "ccp") + @Mapping(source = "ibanAddebito", target = "debitIban") + @Mapping(source = "bicAddebito", target = "debitBic") + @Mapping(source = "firmaRicevuta", target = "rtSignature") + @Mapping(source = "datiSingoloVersamento", target = "transfer") + TransferDataDTO toTransferDataDTO(CtDatiVersamentoRPT datiVersamentoRPT); + + + @Mapping(source = "importoSingoloVersamento", target = "amount") + @Mapping(source = "commissioneCaricoPA", target = "fee") + @Mapping(source = "ibanAccredito", target = "creditIban") + @Mapping(source = "bicAccredito", target = "creditBic") + @Mapping(source = "ibanAppoggio", target = "supportIban") + @Mapping(source = "bicAppoggio", target = "supportBic") + @Mapping(source = "credenzialiPagatore", target = "payerCredentials") + @Mapping(source = "causaleVersamento", target = "remittanceInformation") + @Mapping(source = "datiSpecificiRiscossione", target = "category") + @Mapping(source = "datiMarcaBolloDigitale", target = "digitalStamp") + TransferDTO toTransferDTO(CtDatiSingoloVersamentoRPT datiSingoloVersamentoRPT); + + @Mapping(source = "tipoBollo", target = "type") + @Mapping(source = "hashDocumento", target = "documentHash") + @Mapping(source = "provinciaResidenza", target = "province") + DigitalStampDTO toDigitalStampDTO(CtDatiMarcaBolloDigitale datiMarcaBolloDigitale); +} diff --git a/src/main/java/it/gov/pagopa/wispconverter/service/model/CommonRPTFieldsDTO.java b/src/main/java/it/gov/pagopa/wispconverter/service/model/CommonRPTFieldsDTO.java new file mode 100644 index 00000000..c0ed598b --- /dev/null +++ b/src/main/java/it/gov/pagopa/wispconverter/service/model/CommonRPTFieldsDTO.java @@ -0,0 +1,34 @@ +package it.gov.pagopa.wispconverter.service.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.*; + +import java.util.List; + +@Data +@Builder(toBuilder = true) +@NoArgsConstructor +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@ToString +@JsonIgnoreProperties(ignoreUnknown = true) +public class CommonRPTFieldsDTO { + + private String cartId; + private String creditorInstitutionId; + private String creditorInstitutionBrokerId; + private String stationId; + private String payerType; + private String payerFiscalCode; + private String payerFullName; + private String payerAddressStreetName; + private String payerAddressStreetNumber; + private String payerAddressPostalCode; + private String payerAddressCity; + private String payerAddressProvince; + private String payerAddressNation; + private String payerEmail; + private Boolean isMultibeneficiary; + private Boolean containsDigitalStamp; + private List paymentNotices; + private List rpts; +} diff --git a/src/main/java/it/gov/pagopa/wispconverter/service/model/DigitalStampDTO.java b/src/main/java/it/gov/pagopa/wispconverter/service/model/DigitalStampDTO.java new file mode 100644 index 00000000..196c8bfd --- /dev/null +++ b/src/main/java/it/gov/pagopa/wispconverter/service/model/DigitalStampDTO.java @@ -0,0 +1,16 @@ +package it.gov.pagopa.wispconverter.service.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@ToString +@JsonIgnoreProperties(ignoreUnknown = true) +public class DigitalStampDTO { + + protected String type; + protected byte[] documentHash; + protected String province; +} diff --git a/src/main/java/it/gov/pagopa/wispconverter/service/model/ConversionResultDTO.java b/src/main/java/it/gov/pagopa/wispconverter/service/model/PaymentNoticeContentDTO.java similarity index 69% rename from src/main/java/it/gov/pagopa/wispconverter/service/model/ConversionResultDTO.java rename to src/main/java/it/gov/pagopa/wispconverter/service/model/PaymentNoticeContentDTO.java index 2326a816..d86ed7e0 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/service/model/ConversionResultDTO.java +++ b/src/main/java/it/gov/pagopa/wispconverter/service/model/PaymentNoticeContentDTO.java @@ -9,8 +9,9 @@ @AllArgsConstructor(access = AccessLevel.PRIVATE) @ToString @JsonIgnoreProperties(ignoreUnknown = true) -public class ConversionResultDTO { - private boolean success; - private String errorPage; - private String uri; +public class PaymentNoticeContentDTO { + + private String noticeNumber; + private String fiscalCode; + private Long amount; } diff --git a/src/main/java/it/gov/pagopa/wispconverter/service/model/RPTRequestDTO.java b/src/main/java/it/gov/pagopa/wispconverter/service/model/PaymentRequestDomainDTO.java similarity index 71% rename from src/main/java/it/gov/pagopa/wispconverter/service/model/RPTRequestDTO.java rename to src/main/java/it/gov/pagopa/wispconverter/service/model/PaymentRequestDomainDTO.java index ffb5410f..802dc98f 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/service/model/RPTRequestDTO.java +++ b/src/main/java/it/gov/pagopa/wispconverter/service/model/PaymentRequestDomainDTO.java @@ -4,12 +4,12 @@ import lombok.*; @Data -@Builder(toBuilder = true) @NoArgsConstructor @AllArgsConstructor(access = AccessLevel.PRIVATE) @ToString @JsonIgnoreProperties(ignoreUnknown = true) -public class RPTRequestDTO { - private H header; - private B body; +public class PaymentRequestDomainDTO { + + protected String domainId; + protected String stationId; } diff --git a/src/main/java/it/gov/pagopa/wispconverter/service/model/PaymentSubjectDTO.java b/src/main/java/it/gov/pagopa/wispconverter/service/model/PaymentSubjectDTO.java new file mode 100644 index 00000000..5b569a0d --- /dev/null +++ b/src/main/java/it/gov/pagopa/wispconverter/service/model/PaymentSubjectDTO.java @@ -0,0 +1,34 @@ +package it.gov.pagopa.wispconverter.service.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@ToString +@JsonIgnoreProperties(ignoreUnknown = true) +public class PaymentSubjectDTO { + + protected SubjectUniqueIdentifierDTO subjectUniqueIdentifier; + + protected String name; + + protected String operUnitCode; + + protected String operUnitDenom; + + protected String address; + + protected String streetNumber; + + protected String postalCode; + + protected String city; + + protected String province; + + protected String nation; + + protected String email; +} diff --git a/src/main/java/it/gov/pagopa/wispconverter/service/model/RPTContentDTO.java b/src/main/java/it/gov/pagopa/wispconverter/service/model/RPTContentDTO.java index 9909bf15..ec522164 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/service/model/RPTContentDTO.java +++ b/src/main/java/it/gov/pagopa/wispconverter/service/model/RPTContentDTO.java @@ -1,7 +1,7 @@ package it.gov.pagopa.wispconverter.service.model; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import it.gov.digitpa.schemas._2011.pagamenti.CtRichiestaPagamentoTelematico; +import it.gov.pagopa.wispconverter.service.model.paymentrequest.PaymentRequestDTO; import lombok.*; @Data @@ -11,10 +11,9 @@ @ToString @JsonIgnoreProperties(ignoreUnknown = true) public class RPTContentDTO { - private String idDominio; - private String idIntermediarioPA; - private Boolean multibeneficiario; - private String noticeNumber; - // TODO disaccoppia CtRichiestaPagamentoTelematico, estraendo TUTTI i campi - private CtRichiestaPagamentoTelematico rpt; + + private String iupd; + private String iuv; + private Boolean containsDigitalStamp; + private PaymentRequestDTO rpt; } diff --git a/src/main/java/it/gov/pagopa/wispconverter/service/model/SubjectUniqueIdentifierDTO.java b/src/main/java/it/gov/pagopa/wispconverter/service/model/SubjectUniqueIdentifierDTO.java new file mode 100644 index 00000000..be2309a5 --- /dev/null +++ b/src/main/java/it/gov/pagopa/wispconverter/service/model/SubjectUniqueIdentifierDTO.java @@ -0,0 +1,16 @@ +package it.gov.pagopa.wispconverter.service.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@ToString +@JsonIgnoreProperties(ignoreUnknown = true) +public class SubjectUniqueIdentifierDTO { + + protected String type; + + protected String code; +} diff --git a/src/main/java/it/gov/pagopa/wispconverter/service/model/TransferDTO.java b/src/main/java/it/gov/pagopa/wispconverter/service/model/TransferDTO.java new file mode 100644 index 00000000..92d68653 --- /dev/null +++ b/src/main/java/it/gov/pagopa/wispconverter/service/model/TransferDTO.java @@ -0,0 +1,24 @@ +package it.gov.pagopa.wispconverter.service.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.*; + +import java.math.BigDecimal; + +@Data +@NoArgsConstructor +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@ToString +@JsonIgnoreProperties(ignoreUnknown = true) +public class TransferDTO { + protected BigDecimal amount; + protected BigDecimal fee; + protected String creditIban; + protected String creditBic; + protected String supportIban; + protected String supportBic; + protected String payerCredentials; + protected String remittanceInformation; + protected String category; + protected DigitalStampDTO digitalStamp; +} diff --git a/src/main/java/it/gov/pagopa/wispconverter/service/model/TransferDataDTO.java b/src/main/java/it/gov/pagopa/wispconverter/service/model/TransferDataDTO.java new file mode 100644 index 00000000..b21a6cfe --- /dev/null +++ b/src/main/java/it/gov/pagopa/wispconverter/service/model/TransferDataDTO.java @@ -0,0 +1,26 @@ +package it.gov.pagopa.wispconverter.service.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.*; + +import javax.xml.datatype.XMLGregorianCalendar; +import java.math.BigDecimal; +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@ToString +@JsonIgnoreProperties(ignoreUnknown = true) +public class TransferDataDTO { + + protected XMLGregorianCalendar paymentDate; + protected BigDecimal totalAmount; + protected String type; + protected String iuv; + protected String ccp; + protected String debitIban; + protected String debitBic; + protected String rtSignature; + protected List transfer; +} diff --git a/src/main/java/it/gov/pagopa/wispconverter/service/model/paymentrequest/PaymentRequestDTO.java b/src/main/java/it/gov/pagopa/wispconverter/service/model/paymentrequest/PaymentRequestDTO.java new file mode 100644 index 00000000..b33d4eb1 --- /dev/null +++ b/src/main/java/it/gov/pagopa/wispconverter/service/model/paymentrequest/PaymentRequestDTO.java @@ -0,0 +1,28 @@ +package it.gov.pagopa.wispconverter.service.model.paymentrequest; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import it.gov.pagopa.wispconverter.service.model.PaymentRequestDomainDTO; +import it.gov.pagopa.wispconverter.service.model.PaymentSubjectDTO; +import it.gov.pagopa.wispconverter.service.model.TransferDataDTO; +import lombok.*; + +import javax.xml.datatype.XMLGregorianCalendar; + + +@Data +@NoArgsConstructor +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@ToString +@JsonIgnoreProperties(ignoreUnknown = true) +public class PaymentRequestDTO { + + private String version; + private PaymentRequestDomainDTO domain; + private String messageRequestId; + private XMLGregorianCalendar messageRequestDatetime; + private SubjectAuthentication subjectAuthentication; + private PaymentSubjectDTO debtor; // versante + private PaymentSubjectDTO payer; // pagatore + private PaymentSubjectDTO payeeInstitution; + private TransferDataDTO transferData; +} diff --git a/src/main/java/it/gov/pagopa/wispconverter/service/model/paymentrequest/SubjectAuthentication.java b/src/main/java/it/gov/pagopa/wispconverter/service/model/paymentrequest/SubjectAuthentication.java new file mode 100644 index 00000000..4b841195 --- /dev/null +++ b/src/main/java/it/gov/pagopa/wispconverter/service/model/paymentrequest/SubjectAuthentication.java @@ -0,0 +1,31 @@ +package it.gov.pagopa.wispconverter.service.model.paymentrequest; + +import jakarta.xml.bind.annotation.XmlEnumValue; + +public enum SubjectAuthentication { + + CNS("CNS"), + USR("USR"), + OTH("OTH"), + @XmlEnumValue("N/A") + N_A("N/A"); + private final String value; + + SubjectAuthentication(String v) { + value = v; + } + + public static SubjectAuthentication fromValue(String v) { + for (SubjectAuthentication c : SubjectAuthentication.values()) { + if (c.value.equals(v)) { + return c; + } + } + throw new IllegalArgumentException(v); + } + + public String value() { + return value; + } + +} \ No newline at end of file diff --git a/src/main/java/it/gov/pagopa/wispconverter/util/CommonUtility.java b/src/main/java/it/gov/pagopa/wispconverter/util/CommonUtility.java index 9b6d3ced..ea6d5478 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/util/CommonUtility.java +++ b/src/main/java/it/gov/pagopa/wispconverter/util/CommonUtility.java @@ -1,12 +1,11 @@ package it.gov.pagopa.wispconverter.util; +import it.gov.pagopa.wispconverter.exception.AppErrorCodeMessageEnum; import lombok.AccessLevel; import lombok.NoArgsConstructor; -import org.springframework.util.MultiValueMap; -import java.util.List; import java.util.Optional; -import java.util.stream.Collectors; + @NoArgsConstructor(access = AccessLevel.PRIVATE) public class CommonUtility { @@ -45,4 +44,8 @@ public static String getExecutionTime(String startTime) { return "-"; } + + public static String getAppCode(AppErrorCodeMessageEnum error) { + return String.format("%s-%s", Constants.SERVICE_CODE_APP, error.getCode()); + } } diff --git a/src/main/java/it/gov/pagopa/wispconverter/util/ErrorUtil.java b/src/main/java/it/gov/pagopa/wispconverter/util/ErrorUtil.java index df307faf..3a703f91 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/util/ErrorUtil.java +++ b/src/main/java/it/gov/pagopa/wispconverter/util/ErrorUtil.java @@ -6,7 +6,6 @@ import org.slf4j.MDC; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.MessageSource; -import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.http.ProblemDetail; import org.springframework.lang.Nullable; import org.springframework.stereotype.Component; @@ -21,18 +20,16 @@ @RequiredArgsConstructor public class ErrorUtil { - @Value("${error.code.uri}") - private String errorCodeUri; - private static final String ERROR_CODE_TITLE = "error-code.%s.title"; - private static final String ERROR_CODE_DETAIL = "error-code.%s.detail"; - public static final String EXTRA_FIELD_OPERATION_ID = "operation-id"; public static final String EXTRA_FIELD_ERROR_TIMESTAMP = "timestamp"; public static final String EXTRA_FIELD_ERROR_CODE = "error-code"; - + private static final String ERROR_CODE_TITLE = "error-code.%s.title"; + private static final String ERROR_CODE_DETAIL = "error-code.%s.detail"; private final MessageSource messageSource; + @Value("${exception.error-code.uri}") + private String errorCodeUri; - public ErrorResponse forAppException(AppException appEx){ + public ErrorResponse forAppException(AppException appEx) { return ErrorResponse.builder(appEx, forAppErrorCodeMessageEnum(appEx.getError(), appEx.getMessage())) .titleMessageCode(String.format(ERROR_CODE_TITLE, appEx.getError().name())) .detailMessageCode(String.format(ERROR_CODE_DETAIL, appEx.getError().name())) @@ -53,15 +50,15 @@ public ProblemDetail forAppErrorCodeMessageEnum(AppErrorCodeMessageEnum error, @ return problemDetail; } - private void setExtraProperties(ProblemDetail problemDetail){ + private void setExtraProperties(ProblemDetail problemDetail) { problemDetail.setProperty(EXTRA_FIELD_ERROR_TIMESTAMP, Instant.now()); String operationId = MDC.get(Constants.MDC_OPERATION_ID); - if(operationId!=null){ + if (operationId != null) { problemDetail.setProperty(EXTRA_FIELD_OPERATION_ID, operationId); } } - public void finalizeError(ProblemDetail problemDetail, int statusCode){ + public void finalizeError(ProblemDetail problemDetail, int statusCode) { setExtraProperties(problemDetail); MDCUtil.setMDCError(problemDetail); MDCUtil.setMDCCloseFailedOperation(statusCode); @@ -74,7 +71,7 @@ public URI getTypeFromErrorCode(String errorCode) { .build(); } - public String getAppCode(AppErrorCodeMessageEnum error){ + public String getAppCode(AppErrorCodeMessageEnum error) { return String.format("%s-%s", Constants.SERVICE_CODE_APP, error.getCode()); } } diff --git a/src/main/java/it/gov/pagopa/wispconverter/util/FileReader.java b/src/main/java/it/gov/pagopa/wispconverter/util/FileReader.java deleted file mode 100644 index c8c9cca1..00000000 --- a/src/main/java/it/gov/pagopa/wispconverter/util/FileReader.java +++ /dev/null @@ -1,31 +0,0 @@ -package it.gov.pagopa.wispconverter.util; - -import org.springframework.core.io.Resource; -import org.springframework.core.io.ResourceLoader; -import org.springframework.stereotype.Service; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Scanner; - -@Service -public class FileReader { - - private final ResourceLoader resourceLoader; - - public FileReader(ResourceLoader resourceLoader) { - this.resourceLoader = resourceLoader; - } - - public String readFileFromResources(String fileName) throws IOException { - Resource resource = resourceLoader.getResource("classpath:" + fileName); - InputStream inputStream = resource.getInputStream(); - try (Scanner scanner = new Scanner(inputStream)) { - StringBuilder content = new StringBuilder(); - while (scanner.hasNextLine()) { - content.append(scanner.nextLine()).append("\n"); - } - return content.toString(); - } - } -} diff --git a/src/main/java/it/gov/pagopa/wispconverter/util/JaxbElementUtil.java b/src/main/java/it/gov/pagopa/wispconverter/util/JaxbElementUtil.java index aa0111ae..954fbb70 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/util/JaxbElementUtil.java +++ b/src/main/java/it/gov/pagopa/wispconverter/util/JaxbElementUtil.java @@ -39,17 +39,17 @@ public class JaxbElementUtil { private final DocumentBuilderFactory documentBuilderFactory; - private Element convertToElement(InputSource is, String nameSpaceUri, String localName) { + private Element convertToElement(InputSource inputSource, String nameSpaceUri, String localName) { try { - DocumentBuilder db = documentBuilderFactory.newDocumentBuilder(); - Document doc = db.parse(is); - NodeList nodeList = doc.getElementsByTagNameNS(nameSpaceUri, localName); + DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); + Document document = documentBuilder.parse(inputSource); + NodeList nodeList = document.getElementsByTagNameNS(nameSpaceUri, localName); if (nodeList.getLength() == 0) { - throw new AppException(AppErrorCodeMessageEnum.PARSE_ERROR, "NodeList must be > 0"); + throw new AppException(AppErrorCodeMessageEnum.PARSING_INVALID_XML_NODES); } return (Element) nodeList.item(0); } catch (ParserConfigurationException | IOException | SAXException e) { - throw new AppException(e, AppErrorCodeMessageEnum.PARSE_ERROR, e.getMessage()); + throw new AppException(e, AppErrorCodeMessageEnum.PARSING_GENERIC_ERROR, e.getMessage()); } } @@ -60,7 +60,7 @@ public T convertToBean(Element element, Class targetType) { JAXBElement jaxbElement = unmarshaller.unmarshal(element, targetType); return jaxbElement.getValue(); } catch (JAXBException e) { - throw new AppException(e, AppErrorCodeMessageEnum.PARSE_ERROR, e.getMessage()); + throw new AppException(e, AppErrorCodeMessageEnum.PARSING_GENERIC_ERROR, e.getMessage()); } } @@ -77,12 +77,12 @@ public Element convertToRPTElement(byte[] source) { public T getSoapHeader(Envelope envelope, Class targetType) { Header header = envelope.getHeader(); if (header == null) { - throw new AppException(AppErrorCodeMessageEnum.PARSE_ERROR, "header is null"); + throw new AppException(AppErrorCodeMessageEnum.PARSING_INVALID_HEADER, "header is null"); } List list = header.getAny(); if (list == null || list.isEmpty()) { - throw new AppException(AppErrorCodeMessageEnum.PARSE_ERROR, "headerValue is null or is empty"); + throw new AppException(AppErrorCodeMessageEnum.PARSING_INVALID_HEADER, "headerValue is null or is empty"); } Element element = (Element) list.get(0); return convertToBean(element, targetType); @@ -91,16 +91,14 @@ public T getSoapHeader(Envelope envelope, Class targetType) { public T getSoapBody(Envelope envelope, Class targetType) { Body body = envelope.getBody(); if (body == null) { - throw new AppException(AppErrorCodeMessageEnum.PARSE_ERROR, "body is null"); + throw new AppException(AppErrorCodeMessageEnum.PARSING_INVALID_BODY, "body is null"); } List list = body.getAny(); if (list == null || list.isEmpty()) { - throw new AppException(AppErrorCodeMessageEnum.PARSE_ERROR, "bodyValue is null or is empty"); + throw new AppException(AppErrorCodeMessageEnum.PARSING_INVALID_BODY, "bodyValue is null or is empty"); } Element element = (Element) list.get(0); return convertToBean(element, targetType); } - - } diff --git a/src/main/java/it/gov/pagopa/wispconverter/util/ZipUtil.java b/src/main/java/it/gov/pagopa/wispconverter/util/ZipUtil.java index 29f2b467..fab182cf 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/util/ZipUtil.java +++ b/src/main/java/it/gov/pagopa/wispconverter/util/ZipUtil.java @@ -8,6 +8,8 @@ public class ZipUtil { + private ZipUtil(){} + public static byte[] unzip(byte[] compressed) throws IOException { ByteArrayInputStream bais = new ByteArrayInputStream(compressed); GZIPInputStream gzipInputStream = new GZIPInputStream(bais); diff --git a/src/main/java/it/gov/pagopa/wispconverter/util/client/apiconfigcache/ApiConfigCacheClientLoggingInterceptor.java b/src/main/java/it/gov/pagopa/wispconverter/util/client/apiconfigcache/ApiConfigCacheClientLoggingInterceptor.java new file mode 100644 index 00000000..56bcc3bc --- /dev/null +++ b/src/main/java/it/gov/pagopa/wispconverter/util/client/apiconfigcache/ApiConfigCacheClientLoggingInterceptor.java @@ -0,0 +1,29 @@ +package it.gov.pagopa.wispconverter.util.client.apiconfigcache; + +import it.gov.pagopa.wispconverter.util.client.AbstractAppClientLoggingInterceptor; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpRequest; +import org.springframework.http.client.ClientHttpResponse; + +@Slf4j +@RequiredArgsConstructor +public class ApiConfigCacheClientLoggingInterceptor extends AbstractAppClientLoggingInterceptor { + + + @Override + protected void request(String clientOperationId, String operationId, HttpRequest request, byte[] reqBody) { + if (log.isDebugEnabled()) { + log.debug(createRequestMessage(clientOperationId, operationId, request, reqBody)); + } + } + + @SneakyThrows + @Override + protected void response(String clientOperationId, String operationId, String clientExecutionTime, HttpRequest request, ClientHttpResponse response) { + if (log.isDebugEnabled()) { + log.debug(createResponseMessage(clientOperationId, operationId, clientExecutionTime, request, response)); + } + } +} diff --git a/src/main/java/it/gov/pagopa/wispconverter/util/client/apiconfigcache/ApiConfigCacheClientResponseErrorHandler.java b/src/main/java/it/gov/pagopa/wispconverter/util/client/apiconfigcache/ApiConfigCacheClientResponseErrorHandler.java new file mode 100644 index 00000000..9eff7ba6 --- /dev/null +++ b/src/main/java/it/gov/pagopa/wispconverter/util/client/apiconfigcache/ApiConfigCacheClientResponseErrorHandler.java @@ -0,0 +1,47 @@ +package it.gov.pagopa.wispconverter.util.client.apiconfigcache; + +import it.gov.pagopa.wispconverter.exception.AppErrorCodeMessageEnum; +import it.gov.pagopa.wispconverter.exception.AppException; +import it.gov.pagopa.wispconverter.util.client.AbstractResponseErrorHandler; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatusCode; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.stereotype.Component; +import org.springframework.web.client.*; + +import java.io.IOException; +import java.nio.charset.Charset; + +@Component +@Slf4j +public class ApiConfigCacheClientResponseErrorHandler extends AbstractResponseErrorHandler implements ResponseErrorHandler { + @Override + public boolean hasError(ClientHttpResponse response) throws IOException { + return response.getStatusCode().is5xxServerError() || + response.getStatusCode().is4xxClientError(); + } + + @Override + public void handleError(ClientHttpResponse response) throws IOException { + HttpStatusCode statusCode = response.getStatusCode(); + String statusText = response.getStatusText(); + HttpHeaders headers = response.getHeaders(); + byte[] body = getResponseBody(response); + Charset charset = getCharset(response); + String message = getErrorMessage(statusCode.value(), statusText, body, charset); + + RestClientResponseException ex; + if (statusCode.is4xxClientError()) { + ex = HttpClientErrorException.create(message, statusCode, statusText, headers, body, charset); + } else if (statusCode.is5xxServerError()) { + ex = HttpServerErrorException.create(message, statusCode, statusText, headers, body, charset); + } else { + ex = new UnknownHttpStatusCodeException(message, statusCode.value(), statusText, headers, body, charset); + } + + throw new AppException(ex, AppErrorCodeMessageEnum.CLIENT_APICONFIGCACHE, message); + } + + +} diff --git a/src/main/java/it/gov/pagopa/wispconverter/util/client/iuvgenerator/IuvGeneratorClientLoggingInterceptor.java b/src/main/java/it/gov/pagopa/wispconverter/util/client/iuvgenerator/IuvGeneratorClientLoggingInterceptor.java index b93c264b..2f429ce8 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/util/client/iuvgenerator/IuvGeneratorClientLoggingInterceptor.java +++ b/src/main/java/it/gov/pagopa/wispconverter/util/client/iuvgenerator/IuvGeneratorClientLoggingInterceptor.java @@ -12,18 +12,18 @@ public class IuvGeneratorClientLoggingInterceptor extends AbstractAppClientLoggingInterceptor { - @Override - protected void request(String clientOperationId, String operationId, HttpRequest request, byte[] reqBody) { - if(log.isDebugEnabled()){ - log.debug(createRequestMessage(clientOperationId, operationId, request, reqBody)); + @Override + protected void request(String clientOperationId, String operationId, HttpRequest request, byte[] reqBody) { + if (log.isDebugEnabled()) { + log.debug(createRequestMessage(clientOperationId, operationId, request, reqBody)); + } } - } - @SneakyThrows - @Override - protected void response(String clientOperationId, String operationId, String clientExecutionTime, HttpRequest request, ClientHttpResponse response) { - if(log.isDebugEnabled()){ - log.debug(createResponseMessage(clientOperationId, operationId, clientExecutionTime, request, response)); + @SneakyThrows + @Override + protected void response(String clientOperationId, String operationId, String clientExecutionTime, HttpRequest request, ClientHttpResponse response) { + if (log.isDebugEnabled()) { + log.debug(createResponseMessage(clientOperationId, operationId, clientExecutionTime, request, response)); + } } - } } diff --git a/src/main/java/it/gov/pagopa/wispconverter/util/client/iuvgenerator/IuvGeneratorClientResponseErrorHandler.java b/src/main/java/it/gov/pagopa/wispconverter/util/client/iuvgenerator/IuvGeneratorClientResponseErrorHandler.java index 7d2e6a17..f09f0832 100644 --- a/src/main/java/it/gov/pagopa/wispconverter/util/client/iuvgenerator/IuvGeneratorClientResponseErrorHandler.java +++ b/src/main/java/it/gov/pagopa/wispconverter/util/client/iuvgenerator/IuvGeneratorClientResponseErrorHandler.java @@ -16,34 +16,32 @@ @Component @Slf4j public class IuvGeneratorClientResponseErrorHandler extends AbstractResponseErrorHandler implements ResponseErrorHandler { - @Override - public boolean hasError(ClientHttpResponse response) throws IOException { - return response.getStatusCode().is5xxServerError() || - response.getStatusCode().is4xxClientError(); - } - - @Override - public void handleError(ClientHttpResponse response) throws IOException { - HttpStatusCode statusCode = response.getStatusCode(); - String statusText = response.getStatusText(); - HttpHeaders headers = response.getHeaders(); - byte[] body = getResponseBody(response); - Charset charset = getCharset(response); - String message = getErrorMessage(statusCode.value(), statusText, body, charset); - - RestClientResponseException ex; - if (statusCode.is4xxClientError()) { - ex = HttpClientErrorException.create(message, statusCode, statusText, headers, body, charset); - } - else if (statusCode.is5xxServerError()) { - ex = HttpServerErrorException.create(message, statusCode, statusText, headers, body, charset); - } - else { - ex = new UnknownHttpStatusCodeException(message, statusCode.value(), statusText, headers, body, charset); + @Override + public boolean hasError(ClientHttpResponse response) throws IOException { + return response.getStatusCode().is5xxServerError() || + response.getStatusCode().is4xxClientError(); } - throw new AppException(ex, AppErrorCodeMessageEnum.CLIENT_IUV_GENERATOR, message) ; - } + @Override + public void handleError(ClientHttpResponse response) throws IOException { + HttpStatusCode statusCode = response.getStatusCode(); + String statusText = response.getStatusText(); + HttpHeaders headers = response.getHeaders(); + byte[] body = getResponseBody(response); + Charset charset = getCharset(response); + String message = getErrorMessage(statusCode.value(), statusText, body, charset); + + RestClientResponseException ex; + if (statusCode.is4xxClientError()) { + ex = HttpClientErrorException.create(message, statusCode, statusText, headers, body, charset); + } else if (statusCode.is5xxServerError()) { + ex = HttpServerErrorException.create(message, statusCode, statusText, headers, body, charset); + } else { + ex = new UnknownHttpStatusCodeException(message, statusCode.value(), statusText, headers, body, charset); + } + + throw new AppException(ex, AppErrorCodeMessageEnum.CLIENT_IUVGENERATOR, message); + } } diff --git a/src/main/java/it/gov/pagopa/wispconverter/util/openapi/OpenAPIDescriptionCustomizer.java b/src/main/java/it/gov/pagopa/wispconverter/util/openapi/OpenAPIDescriptionCustomizer.java new file mode 100644 index 00000000..ff14d06e --- /dev/null +++ b/src/main/java/it/gov/pagopa/wispconverter/util/openapi/OpenAPIDescriptionCustomizer.java @@ -0,0 +1,40 @@ +package it.gov.pagopa.wispconverter.util.openapi; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Info; +import it.gov.pagopa.wispconverter.exception.AppErrorCodeMessageEnum; +import it.gov.pagopa.wispconverter.util.CommonUtility; +import org.springdoc.core.customizers.OpenApiCustomizer; +import org.springframework.stereotype.Component; + +@Component +public class OpenAPIDescriptionCustomizer implements OpenApiCustomizer { + + + private static final String SEPARATOR = " | "; + + private static String buildErrorData() { + StringBuilder builder = new StringBuilder("\n\n**STANDARD ERRORS:**\n"); + builder.append("NAME").append(SEPARATOR).append("CODE").append(SEPARATOR).append("DESCRIPTION").append("\n"); + builder.append("-").append(SEPARATOR).append("-").append(SEPARATOR).append("-").append("\n"); + + for (AppErrorCodeMessageEnum errorCode : AppErrorCodeMessageEnum.values()) { + + String detail = errorCode.getDetail(); + detail = detail.replaceAll("(\\[\\{\\d+\\}\\])", " [*...content...*]"); + detail = detail.replaceAll("(\\{\\d+\\})", " *...error description...*"); + + builder.append("**").append(CommonUtility.getAppCode(errorCode)).append("**").append(SEPARATOR) + .append("*").append(errorCode.name()).append("*").append(SEPARATOR) + .append(detail).append("\n"); + } + + return builder.toString(); + } + + @Override + public void customise(OpenAPI openApi) { + Info info = openApi.getInfo(); + info.setDescription(info.getDescription() + buildErrorData()); + } +} \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 8f4c6be8..53d9b7e7 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,8 +1,8 @@ # Info -info.application.name=@project.name@ -info.application.artifactId=@project.artifactId@ +info.application.name=WISP Converter +info.application.artifactId=WISP Converter info.application.version=@project.version@ -info.application.description=@project.description@ +info.application.description=A service that permits to handle nodoInviaRPT and nodoInviaCarrelloRPT request from WISP, converting them in NMU payments. info.properties.environment=${ENV:azure} @@ -18,6 +18,7 @@ management.health.readinessState.enabled=true # Openapi springdoc.writer-with-order-by-keys=true springdoc.writer-with-default-pretty-printer=true +springdoc.model-and-view-allowed=true # Server @@ -73,30 +74,38 @@ client.decoupler-caching.base-path=${CLIENT_DECOUPLERCACHING_HOST:none} client.decoupler-caching.api-key=${CLIENT_DECOUPLERCACHING_SUBKEY:none} +# Cache client configuration +client.cache.read-timeout=${CLIENT_CACHE_READ_TIMEOUT:5000} +client.cache.connect-timeout=${CLIENT_CACHE_READ_TIMEOUT:5000} +client.cache.base-path=${CLIENT_CACHE_HOST:none} +client.cache.api-key=${CLIENT_CACHE_SUBKEY:none} + + # Application domain configuration +exception.error-code.uri=${ERROR_CODE_URI:https://pagopa.gov/error-code} wisp-converter.aux-digit=3 wisp-converter.segregation-code=48 wisp-converter.cached-requestid-mapping.ttl.minutes=${CACHED_REQUESTID_MAPPING_TTL_MINUTES:1440} - -# Error -error.code.uri=${ERROR_CODE_URI:https://pagopa.gov/error-code} +wisp-converter.poste-italiane.abi-code=07601 +wisp-converter-cache.refresh.cron=${CACHE_REFRESH_CRON:-} ## Exclude url from filter filter.exclude-url-patterns=/swagger-ui/**,/v3/api-docs/** + ## Server Logging filter log.server.request.include-headers=true log.server.request.include-client-info=true log.server.request.include-payload=true log.server.request.max-payload-length=10000 log.server.request.pretty=true - log.server.response.include-headers=true log.server.response.include-payload=true log.server.response.max-payload-length=10000 log.server.response.pretty=true + ## Client logging Checkout log.client.checkout.request.include-headers=true log.client.checkout.request.include-payload=true @@ -108,6 +117,7 @@ log.client.checkout.mask.header.name=${CLIENT_CHECKOUT_API_KEY_NAME:Ocp-Apim-Sub log.client.checkout.request.pretty=true log.client.checkout.response.pretty=true + ## Client logging Decoupler Caching log.client.decoupler-caching.request.include-headers=true log.client.decoupler-caching.request.include-payload=true @@ -119,6 +129,7 @@ log.client.decoupler-caching.mask.header.name=${CLIENT_DECOUPLERCACHING_API_KEY_ log.client.decoupler-caching.request.pretty=true log.client.decoupler-caching.response.pretty=true + ## Client logging Iuv Generator log.client.iuvgenerator.request.include-headers=true log.client.iuvgenerator.request.include-payload=true @@ -130,6 +141,7 @@ log.client.iuvgenerator.mask.header.name=${CLIENT_IUVGENERATOR_API_KEY_NAME:Ocp- log.client.iuvgenerator.request.pretty=true log.client.iuvgenerator.response.pretty=true + ## Client logging Gpd log.client.gpd.request.include-headers=true log.client.gpd.request.include-payload=true @@ -142,3 +154,13 @@ log.client.gpd.request.pretty=true log.client.gpd.response.pretty=true +## Client logging APIConfig cache +log.client.cache.request.include-headers=true +log.client.cache.request.include-payload=true +log.client.cache.request.max-payload-length=10000 +log.client.cache.response.include-headers=true +log.client.cache.response.include-payload=true +log.client.cache.response.max-payload-length=10000 +log.client.cache.mask.header.name=${CLIENT_CONFIG_API_KEY_NAME:Ocp-Apim-Subscription-Key} +log.client.cache.request.pretty=true +log.client.cache.response.pretty=true diff --git a/src/main/resources/i18n/messages_en.properties b/src/main/resources/i18n/messages_en.properties index 7e170fe4..16d08679 100644 --- a/src/main/resources/i18n/messages_en.properties +++ b/src/main/resources/i18n/messages_en.properties @@ -1,25 +1,56 @@ error-code.ERROR.title=System error error-code.ERROR.detail=An unexpected error has occurred. Please contact support -error-code.PARSE_ERROR.title=Parse error -error-code.PARSE_ERROR.detail=Error while parsing: {0} +error-code.GENERIC_ERROR.title=Generic flow error +error-code.GENERIC_ERROR.detail=Error while executing conversion flow. {0} -error-code.PRIMITIVE_NOT_VALID.title=Primitive not valid -error-code.PRIMITIVE_NOT_VALID.detail=Primitive [{0}] not valid +error-code.PARSING_GENERIC_ERROR.title=Generic parsing error +error-code.PARSING_GENERIC_ERROR.detail=Error while parsing payload. {0} -error-code.RPT_NOT_FOUND.title=RPT not found -error-code.RPT_NOT_FOUND.detail=RPT with sessionId [{0}] not found +error-code.PARSING_INVALID_HEADER.title=SOAP Header parsing error +error-code.PARSING_INVALID_HEADER.detail=Error while parsing payload. The SOAP header in payload is invalid: {0} + +error-code.PARSING_INVALID_BODY.title=SOAP Body parsing error +error-code.PARSING_INVALID_BODY.detail=Error while parsing payload. The SOAP body in payload is invalid: {0} + +error-code.PARSING_INVALID_XML_NODES.title=XML parsing error +error-code.PARSING_INVALID_XML_NODES.detail=Error while parsing payload. The list of nodes extracted from XML document must contains at leas one element. + +error-code.PARSING_INVALID_ZIPPED_PAYLOAD.title=ZIP extraction error +error-code.PARSING_INVALID_ZIPPED_PAYLOAD.detail=Error while parsing payload. Cannot unzip payload correctly. + +error-code.PARSING_PRIMITIVE_NOT_VALID.title=Primitive not valid +error-code.PARSING_PRIMITIVE_NOT_VALID.detail=Error while checking primitive. Primitive [{0}] not valid. + +error-code.VALIDATION_INVALID_IBANS.title=IBANs not valid +error-code.VALIDATION_INVALID_IBANS.detail=Error while generating debt position for GPD service. The IBAN field must be set if digital stamp is not defined for the transfer. + +error-code.CONFIGURATION_INVALID_STATION.title=Station not valid +error-code.CONFIGURATION_INVALID_STATION.detail=Error while generating cart for Checkout service. No valid station found with code [{0}]. + +error-code.PERSISTENCE_RPT_NOT_FOUND.title=RPT not found +error-code.PERSISTENCE_RPT_NOT_FOUND.detail=Error while retrieving RPT. RPT with sessionId [{0}] not found. + +error-code.PERSISTENCE_REQUESTID_CACHING_ERROR.title=RequestID caching error +error-code.PERSISTENCE_REQUESTID_CACHING_ERROR.detail=Error while caching RequestID. {0} + +error-code.CLIENT_GPD.title=GPD client error +error-code.CLIENT_GPD.detail=Error while communicating with GPD service. Status [{0}] - {1} error-code.CLIENT_IUV_GENERATOR.title=IUVGeneratorClient error error-code.CLIENT_IUV_GENERATOR.detail=IUVGeneratorClient - {0} -error-code.CLIENT_GPD.title=GPDClient error -error-code.CLIENT_GPD.detail=GPDClient - {0} +error-code.CLIENT_IUVGENERATOR_INVALID_RESPONSE.title=IUV Generator client error +error-code.CLIENT_IUVGENERATOR_INVALID_RESPONSE.detail=Error while communicating with IUV Generator service. Status [{0}] - {1} -error-code.CLIENT_DECOUPLER_CACHING.title=DecouplerCachingClient error -error-code.CLIENT_DECOUPLER_CACHING.detail=DecouplerCachingClient - {0} +error-code.CLIENT_DECOUPLER_CACHING.title=Decoupler caching client error +error-code.CLIENT_DECOUPLER_CACHING.detail=Error while communicating with decoupler caching API. Status [{0}] - {1} -error-code.CLIENT_CHECKOUT.title=CheckoutClient error -error-code.CLIENT_CHECKOUT.detail=CheckoutClient - {0} +error-code.CLIENT_CHECKOUT.title=Checkout error +error-code.CLIENT_CHECKOUT.detail=Error while communicating with Checkout service. status [{0}] - {1} +error-code.CLIENT_CHECKOUT_NO_REDIRECT_LOCATION.title=Checkout redirect error +error-code.CLIENT_CHECKOUT_NO_REDIRECT_LOCATION.detail=Error while communicating with Checkout service. No valid 'Location' header was found. +error-code.CLIENT_CHECKOUT_INVALID_REDIRECT_LOCATION.title=Checkout redirect error +error-code.CLIENT_CHECKOUT_INVALID_REDIRECT_LOCATION.detail=Error while communicating with Checkout service. An empty 'Location' header was found. diff --git a/src/main/resources/i18n/messages_it.properties b/src/main/resources/i18n/messages_it.properties index 93de9613..6de8459e 100644 --- a/src/main/resources/i18n/messages_it.properties +++ b/src/main/resources/i18n/messages_it.properties @@ -1,8 +1,38 @@ error-code.ERROR.title=Errore del sistema error-code.ERROR.detail=Si è verificato un errore imprevisto. Si prega di contattare l'assistenza -error-code.PARSE_ERROR.title=Errore di lettura -error-code.PARSE_ERROR.detail=Errore durante la lettura: {0} +error-code.GENERIC_ERROR.title=Errore generico flusso +error-code.GENERIC_ERROR.detail=Si è verificato un errore durante il flusso di conversione. {0} + +error-code.PARSING_GENERIC_ERROR.title=Errore generico di parsing +error-code.PARSING_GENERIC_ERROR.detail=Si è verificato un errore durante il parsing del payload. {0} + +error-code.PARSING_INVALID_HEADER.title=Errore di parsing per SOAP Header +error-code.PARSING_INVALID_HEADER.detail=Si è verificato un errore durante il parsing del payload. L'header SOAP nel payload non è corretto: {0} + +error-code.PARSING_INVALID_BODY.title=Errore di parsing per SOAP Body +error-code.PARSING_INVALID_BODY.detail=Si è verificato un errore durante il parsing del payload. Il body SOAP nel payload non è corretto: {0} + +error-code.PARSING_INVALID_XML_NODES.title=Errore di parsing per XML +error-code.PARSING_INVALID_XML_NODES.detail=Si è verificato un errore durante il parsing del payload. La lista dei nodi estratti dal documento XML deve contenere almeno un elemento. + +error-code.PARSING_INVALID_ZIPPED_PAYLOAD.title=Errore di estrazione ZIP +error-code.PARSING_INVALID_ZIPPED_PAYLOAD.detail=Si è verificato un errore durante il parsing del payload. Impossibile effettuare l'unzip del payload. + +error-code.PARSING_PRIMITIVE_NOT_VALID.title=Primitiva non valida +error-code.PARSING_PRIMITIVE_NOT_VALID.detail=Si è verificato un errore durante il check della primitiva. La primitive [{0}] non è valida. + +error-code.VALIDATION_INVALID_IBANS.title=IBAN non validi +error-code.VALIDATION_INVALID_IBANS.detail=Si è verificato un errore durante la generaizone della posizione debitoria per il servizio GPD. Il campo IBAN deve essere impostato se non è stata associata una marca da bollo al versamento. + +error-code.CONFIGURATION_INVALID_STATION.title=Stazione non trovata +error-code.CONFIGURATION_INVALID_STATION.detail=Si è verificato un errore durante la generazione del carrello per il servizio Checkout. La stazione con codice [{0}] non esiste. + +error-code.PERSISTENCE_RPT_NOT_FOUND.title=RPT non trovata +error-code.PERSISTENCE_RPT_NOT_FOUND.detail=Si è verificato un errore durante la ricerca dell'RPT. L'RPT con sessionId [{0}] not esiste. + +error-code.PERSISTENCE_REQUESTID_CACHING_ERROR.title=Errore di caching RequestID +error-code.PERSISTENCE_REQUESTID_CACHING_ERROR.detail=Si è verificato un errore durante il caching della RequestID. {0} error-code.PRIMITIVE_NOT_VALID.title=Primitiva non valida error-code.PRIMITIVE_NOT_VALID.detail=Primitiva [{0}] non valida @@ -20,4 +50,10 @@ error-code.CLIENT_DECOUPLER_CACHING.title=Errore DecouplerCachingClient error-code.CLIENT_DECOUPLER_CACHING.detail=DecouplerCachingClient - {0} error-code.CLIENT_CHECKOUT.title=Errore CheckoutClient -error-code.CLIENT_CHECKOUT.detail=CheckoutClient - {0} \ No newline at end of file +error-code.CLIENT_CHECKOUT.detail=CheckoutClient - {0} + +error-code.CLIENT_CHECKOUT_NO_REDIRECT_LOCATION.title=Errore di redirect Checkout +error-code.CLIENT_CHECKOUT_NO_REDIRECT_LOCATION.detail=Si è verificato un errore durante la comunicazione con il servizio Checkout. Nessun header 'Location' trovato. + +error-code.CLIENT_CHECKOUT_INVALID_REDIRECT_LOCATION.title=Errore di redirect Checkout +error-code.CLIENT_CHECKOUT_INVALID_REDIRECT_LOCATION.detail=Si è verificato un errore durante la comunicazione con il servizio Checkout. E' stato trovato un header 'Location' vuoto. diff --git a/src/main/resources/static/error.html b/src/main/resources/static/error.html deleted file mode 100644 index 1ab76a59..00000000 --- a/src/main/resources/static/error.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - Errore Generico - - - -
-

Errore Generico

-

Si è verificato un errore generico.

-

Si prega di riprovare più tardi.

-
- - diff --git a/src/test/java/it/gov/pagopa/wispconverter/ApplicationTest.java b/src/test/java/it/gov/pagopa/wispconverter/ApplicationTest.java deleted file mode 100644 index a32a222b..00000000 --- a/src/test/java/it/gov/pagopa/wispconverter/ApplicationTest.java +++ /dev/null @@ -1,16 +0,0 @@ -package it.gov.pagopa.wispconverter; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class ApplicationTest { - - @Test - void contextLoads() { - // check only if the context is loaded - assertTrue(true); - } -} diff --git a/src/test/java/it/gov/pagopa/wispconverter/CarrelloTest.java b/src/test/java/it/gov/pagopa/wispconverter/CarrelloTest.java new file mode 100644 index 00000000..578f3c44 --- /dev/null +++ b/src/test/java/it/gov/pagopa/wispconverter/CarrelloTest.java @@ -0,0 +1,174 @@ +package it.gov.pagopa.wispconverter; + +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest(classes = Application.class) +@AutoConfigureMockMvc +class CarrelloTest { + + /* + @Autowired + ObjectMapper objectMapper; + @Autowired + private MockMvc mvc; + @Autowired private ConfigCacheService configCacheService; + +// @MockBean private CosmosStationRepository cosmosStationRepository; +// @MockBean private CosmosEventsRepository cosmosEventsRepository; +// @MockBean private DatabaseStationsRepository databaseStationsRepository; +// @MockBean private EntityManagerFactory entityManagerFactory; +// @MockBean private EntityManager entityManager; +// @MockBean private DataSource dataSource; +// @MockBean private CosmosClient cosmosClient; + @MockBean private RPTRequestRepository rptRequestRepository; + @MockBean private IUVGeneratorClient iuveneratorClient; + @MockBean private GPDClient gpdClient; + @MockBean private CheckoutClient checkoutClient; + @MockBean private DecouplerCachingClient decouplerCachingClient; + @Qualifier("redisSimpleTemplate") + @MockBean private RedisTemplate redisSimpleTemplate; + + private String getCarrelloPayload(int numofrpt,String station,String amount,boolean multibeneficiario){ + String rpt = TestUtils.loadFileContent("/requests/rpt.xml"); + String rptreplace = rpt.replaceAll("\\{amount\\}", amount); + StringBuilder listaRpt = new StringBuilder(""); + for(int i=0;i"+ + ""+ + ""+ + ""+ + ""+ + "{rpt}" + + "").replace("{rpt}",Base64.getEncoder().encodeToString(rptreplace.getBytes(StandardCharsets.UTF_8))) + ); + } + + String carrello = TestUtils.loadFileContent("/requests/nodoInviaCarrelloRPT.xml"); + return carrello + .replace("{station}",station) + .replace("{multi}",multibeneficiario?"true":"") + .replace("{elementiRpt}", listaRpt.toString()); + } + + private byte[] zip(byte[] uncompressed) throws IOException { + ByteArrayOutputStream bais = new ByteArrayOutputStream(); + GZIPOutputStream gzipOutputStream = new GZIPOutputStream(bais); + gzipOutputStream.write(uncompressed); + gzipOutputStream.close(); + bais.close(); + return bais.toByteArray(); + } + + @Test + void success() throws Exception { + ConfigDataV1Dto configDataV1 = new ConfigDataV1Dto(); + configDataV1.setStations(new HashMap<>()); + StationDto station = new StationDto(); + station.setStationCode("mystation"); + station.setRedirect(new RedirectDto()); + station.getRedirect().setIp("127.0.0.1"); + station.getRedirect().setPath("/redirect"); + station.getRedirect().setPort(8888l); + station.getRedirect().setProtocol(Redirect.ProtocolEnum.HTTPS); + station.getRedirect().setQueryString("param=1"); + configDataV1.getStations().put(station.getStationCode(), station); + org.springframework.test.util.ReflectionTestUtils.setField(configCacheService, "configData",configDataV1); + + HashMap> headers = new HashMap<>(); + headers.put("location", Arrays.asList("locationheader")); + Response executeCreationResponse = + Response.builder() + .status(302) + .headers(headers) + .request(Request.create(Request.HttpMethod.GET, "", new HashMap<>(),"".getBytes(StandardCharsets.UTF_8),null)) + .build(); + + when(checkoutClient.executeCreation(any())).thenReturn(executeCreationResponse); + + when(iuveneratorClient.generate(any(),any())).thenReturn( + IUVGeneratorResponse.builder().iuv("00000000").build() + ); + when(rptRequestRepository.findById(any())).thenReturn( + Optional.of( + RPTRequestEntity.builder().primitive("nodoInviaCarrelloRPT") + .payload( + new String(Base64.getEncoder().encode(zip( + getCarrelloPayload(1,station.getStationCode(), + "100.00",false + ).getBytes(StandardCharsets.UTF_8))),StandardCharsets.UTF_8) + ).build() + ) + ); + when(redisSimpleTemplate.opsForValue()).thenReturn(mock(ValueOperations.class)); + + + + mvc.perform(MockMvcRequestBuilders.get("/redirect?sessionId=aaaaaaaaaaaa").accept(MediaType.APPLICATION_JSON)) + .andExpect(MockMvcResultMatchers.status().is3xxRedirection()) + .andDo( + (result) -> { + assertNotNull(result); + assertNotNull(result.getResponse()); + }); + + verify(checkoutClient,times(1)).executeCreation(any()); + } + + @Test + void success_multibeneficiario() throws Exception { + ConfigDataV1Dto configDataV1 = new ConfigDataV1Dto(); + configDataV1.setStations(new HashMap<>()); + StationDto station = new StationDto(); + station.setStationCode("mystation"); + station.setRedirect(new RedirectDto()); + station.getRedirect().setIp("127.0.0.1"); + station.getRedirect().setPath("/redirect"); + station.getRedirect().setPort(8888l); + station.getRedirect().setProtocol(Redirect.ProtocolEnum.HTTPS); + station.getRedirect().setQueryString("param=1"); + configDataV1.getStations().put(station.getStationCode(), station); + org.springframework.test.util.ReflectionTestUtils.setField(configCacheService, "configData",configDataV1); + + HashMap> headers = new HashMap<>(); + headers.put("location", Arrays.asList("locationheader")); + Response executeCreationResponse = + Response.builder() + .status(302) + .headers(headers) + .request(Request.create(Request.HttpMethod.GET, "", new HashMap<>(),"".getBytes(StandardCharsets.UTF_8),null)) + .build(); + + when(checkoutClient.executeCreation(any())).thenReturn(executeCreationResponse); + + when(iuveneratorClient.generate(any(),any())).thenReturn( + IUVGeneratorResponse.builder().iuv("00000000").build() + ); + when(rptRequestRepository.findById(any())).thenReturn( + Optional.of( + RPTRequestEntity.builder().primitive("nodoInviaCarrelloRPT") + .payload( + new String(Base64.getEncoder().encode(zip( + getCarrelloPayload(2,station.getStationCode(), + "100.00",true + ).getBytes(StandardCharsets.UTF_8))),StandardCharsets.UTF_8) + ).build() + ) + ); + when(redisSimpleTemplate.opsForValue()).thenReturn(mock(ValueOperations.class)); + + + + mvc.perform(MockMvcRequestBuilders.get("/redirect?sessionId=aaaaaaaaaaaa").accept(MediaType.APPLICATION_JSON)) + .andExpect(MockMvcResultMatchers.status().is3xxRedirection()) + .andDo( + (result) -> { + assertNotNull(result); + assertNotNull(result.getResponse()); + }); + + verify(checkoutClient,times(1)).executeCreation(any()); + } + */ +} diff --git a/src/test/java/it/gov/pagopa/wispconverter/HomeTest.java b/src/test/java/it/gov/pagopa/wispconverter/HomeTest.java new file mode 100644 index 00000000..a0161578 --- /dev/null +++ b/src/test/java/it/gov/pagopa/wispconverter/HomeTest.java @@ -0,0 +1,74 @@ +package it.gov.pagopa.wispconverter; + +import com.fasterxml.jackson.databind.ObjectMapper; +import it.gov.pagopa.wispconverter.controller.model.AppInfoResponse; +import it.gov.pagopa.wispconverter.repository.CacheRepository; +import it.gov.pagopa.wispconverter.repository.RPTRequestRepository; +import it.gov.pagopa.wispconverter.service.ConfigCacheService; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +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.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; + +import static org.junit.jupiter.api.Assertions.*; + +@SpringBootTest(classes = Application.class) +@AutoConfigureMockMvc +class HomeTest { + + @Autowired + ObjectMapper objectMapper; + @Value("${info.application.name}") + private String name; + @Value("${info.application.version}") + private String version; + @Value("${info.properties.environment}") + private String environment; + @Autowired + private MockMvc mvc; + @Autowired + private ConfigCacheService configCacheService; + + // @MockBean private CosmosStationRepository cosmosStationRepository; +// @MockBean private CosmosEventsRepository cosmosEventsRepository; +// @MockBean private DatabaseStationsRepository databaseStationsRepository; +// @MockBean private EntityManagerFactory entityManagerFactory; +// @MockBean private EntityManager entityManager; +// @MockBean private DataSource dataSource; +// @MockBean private CosmosClient cosmosClient; + @MockBean + private RPTRequestRepository rptRequestRepository; + @MockBean + private CacheRepository cacheRepository; + + @Test + void slash() throws Exception { + mvc.perform(MockMvcRequestBuilders.get("/").accept(MediaType.APPLICATION_JSON)) + .andExpect(MockMvcResultMatchers.status().is3xxRedirection()); + + } + + @Test + void info() throws Exception { + mvc.perform(MockMvcRequestBuilders.get("/info").accept(MediaType.APPLICATION_JSON)) + .andExpect(MockMvcResultMatchers.status().is2xxSuccessful()).andDo( + (result) -> { + assertNotNull(result); + assertNotNull(result.getResponse()); + final String content = result.getResponse().getContentAsString(); + assertFalse(content.isBlank()); + assertFalse(content.contains("${"), "Generated swagger contains placeholders"); + AppInfoResponse info = objectMapper.readValue(result.getResponse().getContentAsString(), AppInfoResponse.class); + assertEquals(info.getName(), name); + assertEquals(info.getEnvironment(), environment); + assertEquals(info.getVersion(), version); + }); + + } +} diff --git a/src/test/java/it/gov/pagopa/wispconverter/OpenApiGenerationTest.java b/src/test/java/it/gov/pagopa/wispconverter/OpenApiGenerationTest.java index 9a1e2334..5c45fb87 100644 --- a/src/test/java/it/gov/pagopa/wispconverter/OpenApiGenerationTest.java +++ b/src/test/java/it/gov/pagopa/wispconverter/OpenApiGenerationTest.java @@ -1,32 +1,44 @@ package it.gov.pagopa.wispconverter; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; - +import com.azure.cosmos.CosmosAsyncClient; import com.fasterxml.jackson.databind.ObjectMapper; - -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; - +import it.gov.pagopa.wispconverter.controller.RedirectController; +import it.gov.pagopa.wispconverter.repository.RPTRequestRepository; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; +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.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; + @SpringBootTest(classes = Application.class) @AutoConfigureMockMvc class OpenApiGenerationTest { @Autowired - ObjectMapper objectMapper; + private MockMvc mvc; @Autowired - private MockMvc mvc; + private ObjectMapper objectMapper; + + @MockBean + private RedirectController redirectController; + + @MockBean + private RPTRequestRepository rptRequestRepository; + + @MockBean + private CosmosAsyncClient cosmosAsyncClient; @Test void swaggerSpringPlugin() throws Exception { diff --git a/src/test/java/it/gov/pagopa/wispconverter/RptTest.java b/src/test/java/it/gov/pagopa/wispconverter/RptTest.java new file mode 100644 index 00000000..00c2c5cf --- /dev/null +++ b/src/test/java/it/gov/pagopa/wispconverter/RptTest.java @@ -0,0 +1,108 @@ +package it.gov.pagopa.wispconverter; + +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest(classes = Application.class) +@AutoConfigureMockMvc +class RptTest { + + /* + @Autowired + ObjectMapper objectMapper; + @Autowired + private MockMvc mvc; + @Autowired private ConfigCacheService configCacheService; + +// @MockBean private CosmosStationRepository cosmosStationRepository; +// @MockBean private CosmosEventsRepository cosmosEventsRepository; +// @MockBean private DatabaseStationsRepository databaseStationsRepository; +// @MockBean private EntityManagerFactory entityManagerFactory; +// @MockBean private EntityManager entityManager; +// @MockBean private DataSource dataSource; +// @MockBean private CosmosClient cosmosClient; + @MockBean private RPTRequestRepository rptRequestRepository; + @MockBean private IUVGeneratorClient iuveneratorClient; + @MockBean private GPDClient gpdClient; + @MockBean private CheckoutClient checkoutClient; + @MockBean private DecouplerCachingClient decouplerCachingClient; + @Qualifier("redisSimpleTemplate") + @MockBean private RedisTemplate redisSimpleTemplate; + @MockBean private org.openapitools.client.api.CacheApi cacheClient; + + private String getRptPayload(String station,String amount){ + String rpt = TestUtils.loadFileContent("/requests/rpt.xml"); + String rptreplace = rpt.replaceAll("\\{amount\\}", amount); + String nodoInviaRPT = TestUtils.loadFileContent("/requests/nodoInviaRPT.xml"); + return nodoInviaRPT + .replace("{station}",station) + .replace("{rpt}", Base64.getEncoder().encodeToString(rptreplace.getBytes(StandardCharsets.UTF_8))); + } + + private byte[] zip(byte[] uncompressed) throws IOException { + ByteArrayOutputStream bais = new ByteArrayOutputStream(); + GZIPOutputStream gzipOutputStream = new GZIPOutputStream(bais); + gzipOutputStream.write(uncompressed); + gzipOutputStream.close(); + bais.close(); + return bais.toByteArray(); + } + + @Test + void success() throws Exception { + ConfigDataV1 configDataV1 = new ConfigDataV1(); + configDataV1.setStations(new HashMap<>()); + Station station = new Station(); + station.setStationCode("mystation"); + station.setRedirect(new Redirect()); + station.getRedirect().setIp("127.0.0.1"); + station.getRedirect().setPath("/redirect"); + station.getRedirect().setPort(8888l); + station.getRedirect().setProtocol(Redirect.ProtocolEnum.HTTPS); + station.getRedirect().setQueryString("param=1"); + configDataV1.getStations().put(station.getStationCode(), station); + when(cacheClient.cache()).thenReturn(configDataV1); + + org.springframework.test.util.ReflectionTestUtils.setField(configCacheService, "cacheClient",cacheClient); + + HashMap> headers = new HashMap<>(); + headers.put("location", Arrays.asList("locationheader")); + Response executeCreationResponse = + Response.builder() + .status(302) + .headers(headers) + .request(Request.create(Request.HttpMethod.GET, "", new HashMap<>(),"".getBytes(StandardCharsets.UTF_8),null)) + .build(); + + when(checkoutClient.executeCreation(any())).thenReturn(executeCreationResponse); + + when(iuveneratorClient.generate(any(),any())).thenReturn( + IUVGeneratorResponse.builder().iuv("00000000").build() + ); + when(rptRequestRepository.findById(any())).thenReturn( + Optional.of( + RPTRequestEntity.builder().primitive("nodoInviaRPT") + .payload( + new String(Base64.getEncoder().encode(zip( + getRptPayload(station.getStationCode(), + "100.00" + ).getBytes(StandardCharsets.UTF_8))),StandardCharsets.UTF_8) + ).build() + ) + ); + when(redisSimpleTemplate.opsForValue()).thenReturn(mock(ValueOperations.class)); + + + + mvc.perform(MockMvcRequestBuilders.get("/redirect?sessionId=aaaaaaaaaaaa").accept(MediaType.APPLICATION_JSON)) + .andExpect(MockMvcResultMatchers.status().is3xxRedirection()) + .andDo( + (result) -> { + assertNotNull(result); + assertNotNull(result.getResponse()); + }); + + verify(checkoutClient,times(1)).executeCreation(any()); + } + */ +} diff --git a/src/test/java/it/gov/pagopa/wispconverter/utils/TestUtils.java b/src/test/java/it/gov/pagopa/wispconverter/utils/TestUtils.java new file mode 100644 index 00000000..7f94e478 --- /dev/null +++ b/src/test/java/it/gov/pagopa/wispconverter/utils/TestUtils.java @@ -0,0 +1,26 @@ +package it.gov.pagopa.wispconverter.utils; + + +public class TestUtils { + + /* + public static String loadFileContent(String fileName) { + String content = null; + try { + // Get the InputStream of the resource + InputStream inputStream = TestUtils.class.getResourceAsStream(fileName); + if (inputStream != null) { + // Use Apache Commons IO to read the content from the InputStream + content = IOUtils.toString(inputStream, StandardCharsets.UTF_8); + } else { + System.err.println("File not found: " + fileName); + } + } catch (IOException e) { + e.printStackTrace(); + } + return content; + } + */ + + +} diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties index 8f4c6be8..e27d5aa5 100644 --- a/src/test/resources/application.properties +++ b/src/test/resources/application.properties @@ -1,10 +1,9 @@ # Info -info.application.name=@project.name@ -info.application.artifactId=@project.artifactId@ +info.application.name=WISP Converter +info.application.artifactId=WISP Converter info.application.version=@project.version@ -info.application.description=@project.description@ -info.properties.environment=${ENV:azure} - +info.application.description=A service that permits to handle nodoInviaRPT and nodoInviaCarrelloRPT request from WISP, converting them in NMU payments. +info.properties.environment=test # Actuator management.endpoints.web.exposure.include=health,info @@ -18,6 +17,7 @@ management.health.readinessState.enabled=true # Openapi springdoc.writer-with-order-by-keys=true springdoc.writer-with-default-pretty-printer=true +springdoc.model-and-view-allowed=true # Server @@ -36,13 +36,13 @@ cors.configuration=${CORS_CONFIGURATION:{"origins": ["*"], "methods": ["*"]}} # Databases configuration -azure.cosmos.uri=${COSMOS_URI:https://pagopa-d-weu-nodo-wispconv-cosmos-account.documents.azure.com:443/} -azure.cosmos.key=${COSMOS_KEY} +azure.cosmos.uri=nn +azure.cosmos.key=nn azure.cosmos.populate-query-metrics=false azure.cosmos.database=wispconverter -spring.redis.host=${REDIS_HOST:pagopa-d-redis.redis.cache.windows.net} -spring.redis.port=${REDIS_PORT:6380} -spring.redis.password=${REDIS_PASSWORD} +spring.redis.host=nn +spring.redis.port=6380 +spring.redis.password=nn # GPD client configuration @@ -73,30 +73,38 @@ client.decoupler-caching.base-path=${CLIENT_DECOUPLERCACHING_HOST:none} client.decoupler-caching.api-key=${CLIENT_DECOUPLERCACHING_SUBKEY:none} +# Cache client configuration +client.cache.read-timeout=${CLIENT_CACHE_READ_TIMEOUT:5000} +client.cache.connect-timeout=${CLIENT_CACHE_READ_TIMEOUT:5000} +client.cache.base-path=${CLIENT_CACHE_HOST:none} +client.cache.api-key=${CLIENT_CACHE_SUBKEY:none} + + # Application domain configuration +exception.error-code.uri=${ERROR_CODE_URI:https://pagopa.gov/error-code} wisp-converter.aux-digit=3 wisp-converter.segregation-code=48 wisp-converter.cached-requestid-mapping.ttl.minutes=${CACHED_REQUESTID_MAPPING_TTL_MINUTES:1440} - -# Error -error.code.uri=${ERROR_CODE_URI:https://pagopa.gov/error-code} +wisp-converter.poste-italiane.abi-code=07601 +wisp-converter-cache.refresh.cron=${CACHE_REFRESH_CRON:-} ## Exclude url from filter filter.exclude-url-patterns=/swagger-ui/**,/v3/api-docs/** + ## Server Logging filter log.server.request.include-headers=true log.server.request.include-client-info=true log.server.request.include-payload=true log.server.request.max-payload-length=10000 log.server.request.pretty=true - log.server.response.include-headers=true log.server.response.include-payload=true log.server.response.max-payload-length=10000 log.server.response.pretty=true + ## Client logging Checkout log.client.checkout.request.include-headers=true log.client.checkout.request.include-payload=true @@ -108,6 +116,7 @@ log.client.checkout.mask.header.name=${CLIENT_CHECKOUT_API_KEY_NAME:Ocp-Apim-Sub log.client.checkout.request.pretty=true log.client.checkout.response.pretty=true + ## Client logging Decoupler Caching log.client.decoupler-caching.request.include-headers=true log.client.decoupler-caching.request.include-payload=true @@ -119,6 +128,7 @@ log.client.decoupler-caching.mask.header.name=${CLIENT_DECOUPLERCACHING_API_KEY_ log.client.decoupler-caching.request.pretty=true log.client.decoupler-caching.response.pretty=true + ## Client logging Iuv Generator log.client.iuvgenerator.request.include-headers=true log.client.iuvgenerator.request.include-payload=true @@ -130,6 +140,7 @@ log.client.iuvgenerator.mask.header.name=${CLIENT_IUVGENERATOR_API_KEY_NAME:Ocp- log.client.iuvgenerator.request.pretty=true log.client.iuvgenerator.response.pretty=true + ## Client logging Gpd log.client.gpd.request.include-headers=true log.client.gpd.request.include-payload=true @@ -142,3 +153,13 @@ log.client.gpd.request.pretty=true log.client.gpd.response.pretty=true +## Client logging APIConfig cache +log.client.cache.request.include-headers=true +log.client.cache.request.include-payload=true +log.client.cache.request.max-payload-length=10000 +log.client.cache.response.include-headers=true +log.client.cache.response.include-payload=true +log.client.cache.response.max-payload-length=10000 +log.client.cache.mask.header.name=${CLIENT_CONFIG_API_KEY_NAME:Ocp-Apim-Subscription-Key} +log.client.cache.request.pretty=true +log.client.cache.response.pretty=true diff --git a/src/test/resources/logback-spring.xml b/src/test/resources/logback-spring.xml new file mode 100644 index 00000000..fc9d47df --- /dev/null +++ b/src/test/resources/logback-spring.xml @@ -0,0 +1,18 @@ + + + + + + + + + + %clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(%5p) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m %clr(%mdc){magenta}%n%wEx + + + + + + + + diff --git a/src/test/resources/requests/nodoInviaCarrelloRPT.xml b/src/test/resources/requests/nodoInviaCarrelloRPT.xml new file mode 100644 index 00000000..fa176b66 --- /dev/null +++ b/src/test/resources/requests/nodoInviaCarrelloRPT.xml @@ -0,0 +1,21 @@ + + + + {brokerPa} + {station} + {idCarrello} + + + + + {stationPwd} + {psp} + {brokerPsp} + {channel} + + {elementiRpt} + + {multi} + + + \ No newline at end of file diff --git a/src/test/resources/requests/nodoInviaRPT.xml b/src/test/resources/requests/nodoInviaRPT.xml new file mode 100644 index 00000000..3eda1c5c --- /dev/null +++ b/src/test/resources/requests/nodoInviaRPT.xml @@ -0,0 +1,21 @@ + + + + {brokerPa} + {station} + {pa} + {iuv} + {ccp} + + + + + {stationPwd} + {psp} + {brokerPsp} + {channel} + 1 + {rpt} + + + \ No newline at end of file diff --git a/src/test/resources/requests/rpt.xml b/src/test/resources/requests/rpt.xml new file mode 100644 index 00000000..85faac31 --- /dev/null +++ b/src/test/resources/requests/rpt.xml @@ -0,0 +1,37 @@ + + 0 + + {pa} + + 3179612d79fa3366 + {dataOraMessaggioRichiesta}T17:13:30 + USR + + + F + TTTTTT11T11T123T + + Utente Test + + + + G + 00053070918 + + Comune di Nuoro + + + {dataEsecuzionePagamento} + {amount} + {tipoVersamento} + {iuv} + {ccp} + 0 + + {amount} + IT96R0123454321000000012345 + Pagamento di prova + 9/tipodovuto_7 + + + \ No newline at end of file diff --git a/src/test/resources/requests/rptBollo.xml b/src/test/resources/requests/rptBollo.xml new file mode 100644 index 00000000..d91f9861 --- /dev/null +++ b/src/test/resources/requests/rptBollo.xml @@ -0,0 +1,47 @@ + + 0 + + {pa} + + 3179612d79fa3366 + 2017-09-06T17:13:30 + USR + + + F + TTTTTT11T11T123T + + Utente Test + + + + G + 00053070918 + + Comune di Nuoro + + + 2017-09-06 + {amount} + {tipoVersamento} + {iuv} + {ccp} + 0 + + {amountVersamento1} + IT96R0123454321000000012345 + Pagamento di prova + 9/tipodovuto_6 + + + {amountBollo} + Pagamento di prova + 9/tipodovuto_7 + + 01 + YWVvbGlhbQ== + MI + + + + \ No newline at end of file diff --git a/src/test/resources/requests/rptPayPal.xml b/src/test/resources/requests/rptPayPal.xml new file mode 100644 index 00000000..e8aa754d --- /dev/null +++ b/src/test/resources/requests/rptPayPal.xml @@ -0,0 +1,37 @@ + + 0 + + {pa} + + 3179612d79fa3366 + 2017-09-06T17:13:30 + USR + + + F + TTTTTT11T11T123T + + Utente Test + + + + G + 00053070918 + + Comune di Nuoro + + + 2017-09-06 + {amount} + PPAL + {iuv} + {ccp} + 0 + + {amount} + IT96R0123454321000000012345 + Pagamento di prova + 9/tipodovuto_7 + + + \ No newline at end of file