diff --git a/.github/workflows/release_onboarding_sdk.yml b/.github/workflows/release_onboarding_sdk.yml
index 79b8ec495..81befd061 100644
--- a/.github/workflows/release_onboarding_sdk.yml
+++ b/.github/workflows/release_onboarding_sdk.yml
@@ -1,24 +1,15 @@
name: Release onboarding sdk
on:
- pull_request:
- branches:
- - develop
- types: [ closed ]
- paths:
- - 'onboarding-sdk/**/src/**'
-
workflow_dispatch:
permissions:
- packages: write
contents: write
jobs:
setup:
name: Release
runs-on: ubuntu-latest
- if: github.event.pull_request.merged == true
steps:
- uses: actions/checkout@v3
with:
@@ -31,28 +22,6 @@ jobs:
java-version: '17'
cache: maven
- - name: Set patch
- run: |
- mvn build-helper:parse-version versions:set -pl onboarding-sdk -DnewVersion='${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.nextIncrementalVersion}'
- mvn versions:commit -f onboarding-sdk/pom.xml
- shell: bash
-
- - name: Get Version
- run: |
- echo "VERSION=$(mvn -pl onboarding-sdk help:evaluate -Dexpression=project.version -q -DforceStdout)" >> $GITHUB_ENV
- echo "🆙 Bump Version to ${VERSION}"
- shell: bash
-
- - name: Push New Version
- shell: bash
- run: |
- echo "${VERSION}"
- git ls-files ./onboarding-sdk | grep 'pom.xml' | xargs git add
- git config --global user.email "selfcare-github-bot@pagopa.it"
- git config --global user.name "selfcare-github-bot"
- git commit -m "Bump onboarding SDK to version ${VERSION}" || exit 0
- git push origin ${{ github.ref_name}}
-
- name: Build with Maven
run: mvn -B package -f onboarding-sdk/pom.xml
shell: bash
diff --git a/onboarding-sdk/onboarding-sdk-azure-storage/pom.xml b/onboarding-sdk/onboarding-sdk-azure-storage/pom.xml
index 31b8dc22b..ad9715b8b 100644
--- a/onboarding-sdk/onboarding-sdk-azure-storage/pom.xml
+++ b/onboarding-sdk/onboarding-sdk-azure-storage/pom.xml
@@ -6,14 +6,12 @@
it.pagopa.selfcare
onboarding-sdk
- 0.0.4
+ 0.1.0
onboarding-sdk-azure-storage
- 11
- 11
UTF-8
12.24.0
diff --git a/onboarding-sdk/onboarding-sdk-common/pom.xml b/onboarding-sdk/onboarding-sdk-common/pom.xml
index 55836d50d..90dffcbe0 100644
--- a/onboarding-sdk/onboarding-sdk-common/pom.xml
+++ b/onboarding-sdk/onboarding-sdk-common/pom.xml
@@ -4,7 +4,7 @@
it.pagopa.selfcare
onboarding-sdk
- 0.0.4
+ 0.1.0
onboarding-sdk-common
onboarding-sdk-common
diff --git a/onboarding-sdk/onboarding-sdk-product/README.md b/onboarding-sdk/onboarding-sdk-product/README.md
index 049f28831..e11be895b 100644
--- a/onboarding-sdk/onboarding-sdk-product/README.md
+++ b/onboarding-sdk/onboarding-sdk-product/README.md
@@ -2,6 +2,10 @@
This library has been developed to provide a set of Java utility classes to simplify the work of handle **Selfcare Product** as string.
+Selfcare Products is a collection of PagoPA products available for use by institutions. Each product contains specific information, such as its status, admitted role, or a filepath template for building contract necessary for selfcare business logic.
+
+The Onboarding SDK Product offers a set of classes designed for managing this collection of records using a file.
+
## Installation
To use this library in your projects, you can add the dependency to your pom.xml if you're using Maven:
@@ -23,117 +27,7 @@ dependencies {
```
## Product JSON Schema
-Product string which are used by ProductService must follow a specific schema:
-
-```
-{
- "type" : "record",
- "name" : "Product",
- "namespace" : "it.pagopa.selfcare.product.entity",
- "fields" : [ {
- "name" : "id",
- "type" : [ "string" ]
- }, {
- "name" : "logo",
- "type" : [ "string" ]
- }, {
- "name" : "depictImageUrl",
- "type" : [ "string" ]
- }, {
- "name" : "title",
- "type" : [ "string" ]
- }, {
- "name" : "logoBgColor",
- "type" : [ "string" ]
- }, {
- "name" : "description",
- "type" : [ "string" ]
- }, {
- "name" : "urlPublic",
- "type" : [ "string" ]
- }, {
- "name" : "urlBO",
- "type" : [ "string" ]
- }, {
- "name" : "createdAt",
- "type" : [ "string" ]
- }, {
- "name" : "createdBy",
- "type" : [ "string" ]
- }, {
- "name" : "modifiedAt",
- "type" : [ "string" ]
- }, {
- "name" : "modifiedBy",
- "type" : [ "string" ]
- }, {
- "name" : "roleManagementURL",
- "type" : [ "null", "string" ]
- }, {
- "name" : "contractTemplateUpdatedAt",
- "type" : [ "string" ]
- }, {
- "name" : "contractTemplatePath",
- "type" : [ "string" ]
- }, {
- "name" : "contractTemplateVersion",
- "type" : [ "string" ]
- }, {
- "name" : "institutionContractMappings",
- "type" : [ "null", {
- "type" : "map",
- "values" : {
- "type" : "record",
- "name" : "ContractStorage",
- "fields" : [ {
- "name" : "contractTemplateUpdatedAt",
- "type" : [ "null", "string" ]
- }, {
- "name" : "contractTemplatePath",
- "type" : [ "null", "string" ]
- }, {
- "name" : "contractTemplateVersion",
- "type" : [ "null", "string" ]
- } ]
- }
- } ]
- }, {
- "name" : "enabled",
- "type" : "boolean"
- }, {
- "name" : "delegable",
- "type" : ["null","boolean"]
- }, {
- "name" : "status",
- "type" : [ "string" ]
- }, {
- "name" : "parentId",
- "type" : [ "null", "string" ]
- }, {
- "name" : "identityTokenAudience",
- "type" : [ "string" ]
- }, {
- "name" : "backOfficeEnvironmentConfigurations",
- "type" : [ "null", {
- "type" : "map",
- "values" : {
- "type" : "record",
- "name" : "BackOfficeConfigurations",
- "fields" : [ {
- "name" : "url",
- "type" : [ "null", "string" ]
- }, {
- "name" : "identityTokenAudience",
- "type" : [ "null", "string" ]
- } ]
- }
- } ]
- }, {
- "name" : "parent",
- "type" : [ "null", "Product" ]
- } ]
-}
-```
+Product string which are used by ProductService must follow a specific schema, look at src/main/schema folder.
## Usage
@@ -151,4 +45,19 @@ public class Main {
final Product product = productService.getProductIsValid(productId);
}
}
-```
\ No newline at end of file
+```
+
+Generally, you load product json string from an azure storage container, this example use onboading-sdk-azure-storage, and inject the product service in the context of Quarkus or Spring (replace @ApplicationScoped with @Bean).
+
+```java script
+ @ApplicationScoped
+ public ProductService productService(AzureStorageConfig azureStorageConfig){
+ AzureBlobClient azureBlobClient = new AzureBlobClientDefault(azureStorageConfig.connectionStringProduct(), azureStorageConfig.containerProduct());
+ String productJsonString = azureBlobClient.getFileAsText(azureStorageConfig.productFilepath());
+ try {
+ return new ProductServiceDefault(productJsonString, objectMapper());
+ } catch (JsonProcessingException e) {
+ throw new IllegalArgumentException("Found an issue when trying to serialize product json string!!");
+ }
+ }
+ ```
\ No newline at end of file
diff --git a/onboarding-sdk/onboarding-sdk-product/pom.xml b/onboarding-sdk/onboarding-sdk-product/pom.xml
index c9414ffed..4fec37a42 100644
--- a/onboarding-sdk/onboarding-sdk-product/pom.xml
+++ b/onboarding-sdk/onboarding-sdk-product/pom.xml
@@ -4,13 +4,14 @@
it.pagopa.selfcare
onboarding-sdk
- 0.0.4
+ 0.1.0
onboarding-sdk-product
onboarding-sdk-product
2.15.2
+ 5.7.2
@@ -25,7 +26,20 @@
it.pagopa.selfcare
onboarding-sdk-common
- 0.0.1
+ ${parent.version}
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ ${junit-jupiter.version}
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ ${junit-jupiter.version}
+ test
diff --git a/onboarding-sdk/onboarding-sdk-product/src/main/java/it/pagopa/selfcare/product/service/ProductService.java b/onboarding-sdk/onboarding-sdk-product/src/main/java/it/pagopa/selfcare/product/service/ProductService.java
index d25b130a9..56ead812b 100644
--- a/onboarding-sdk/onboarding-sdk-product/src/main/java/it/pagopa/selfcare/product/service/ProductService.java
+++ b/onboarding-sdk/onboarding-sdk-product/src/main/java/it/pagopa/selfcare/product/service/ProductService.java
@@ -10,11 +10,13 @@
import java.util.Map;
public interface ProductService {
- List getProducts(boolean rootOnly);
+ public List getProducts(boolean rootOnly, boolean valid) ;
void validateRoleMappings(Map roleMappings);
- Product getProduct(String id, InstitutionType institutionType);
+ Product getProduct(String productId);
- Product getProductIsValid(String id);
+ void fillContractTemplatePathAndVersion(Product product, InstitutionType institutionType);
+
+ Product getProductIsValid(String productId);
}
diff --git a/onboarding-sdk/onboarding-sdk-product/src/main/java/it/pagopa/selfcare/product/service/ProductServiceDefault.java b/onboarding-sdk/onboarding-sdk-product/src/main/java/it/pagopa/selfcare/product/service/ProductServiceDefault.java
index 320c0ee16..add7cd8b3 100644
--- a/onboarding-sdk/onboarding-sdk-product/src/main/java/it/pagopa/selfcare/product/service/ProductServiceDefault.java
+++ b/onboarding-sdk/onboarding-sdk-product/src/main/java/it/pagopa/selfcare/product/service/ProductServiceDefault.java
@@ -31,33 +31,45 @@ public ProductServiceDefault(String productString) throws JsonProcessingExceptio
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
- List productList = mapper.readValue(productString, new TypeReference>(){});
- if(Objects.isNull(productList) || productList.isEmpty()) throw new ProductNotFoundException("json string is empty!");
- this.productsMap = productList.stream()
- .collect(Collectors.toMap(Product::getId, Function.identity()));
+ this.productsMap = constructProductsMap(productString, mapper);
}
public ProductServiceDefault(String productString, ObjectMapper mapper) throws JsonProcessingException {
+ this.productsMap = constructProductsMap(productString, mapper);
+ }
+ private Map constructProductsMap(String productString, ObjectMapper mapper) throws JsonProcessingException {
List productList = mapper.readValue(productString, new TypeReference>(){});
if(Objects.isNull(productList) || productList.isEmpty()) throw new ProductNotFoundException("json string is empty!");
- this.productsMap = productList.stream()
+ return productList.stream()
.collect(Collectors.toMap(Product::getId, Function.identity()));
}
+ /**
+ * Returns the list of PagoPA products tree which are not INACTIVE
+ * @param rootOnly if true only product that has parent is null are returned
+ * @return List of PagoPA products
+ */
@Override
- public List getProducts(boolean rootOnly) {
+ public List getProducts(boolean rootOnly, boolean valid) {
return rootOnly
? productsMap.values().stream()
- .filter(product -> Objects.nonNull(product.getParentId()))
+ .filter(product -> Objects.isNull(product.getParentId()))
+ .filter(product -> !valid || !statusIsNotValid(product.getStatus()))
.collect(Collectors.toList())
- : productsMap.values().stream()
- .filter(product -> !ProductStatus.INACTIVE.equals(product.getStatus()))
+ : productsMap.values().stream()
+ .filter(product -> !valid || !statusIsNotValid(product.getStatus()))
.collect(Collectors.toList());
}
-
+ /**
+ * Utility method for validating role mappings that contains associations between Selfcare role and Product role.
+ * Each Selfcare role must be only one Product role except OPERATOR.
+ * @param roleMappings
+ * @throws IllegalArgumentException roleMappings is null or empty
+ * @throws InvalidRoleMappingException Selfcare role have more than one Product role
+ */
@Override
public void validateRoleMappings(Map roleMappings) {
@@ -76,42 +88,74 @@ public void validateRoleMappings(Map roleM
});
}
-
+ /**
+ * Return a product by productId without any filter
+ * retrieving data from institutionContractMappings map
+ * @param productId
+ * @return Product
+ * @throws IllegalArgumentException if @param id is null
+ * @throws ProductNotFoundException if product is not found
+ *
+ */
@Override
- public Product getProduct(String id, InstitutionType institutionType) {
+ public Product getProduct(String productId) {
+ return getProduct(productId, false);
+ }
+
+ private Product getProduct(String productId, boolean filterValid) {
- if(Objects.isNull(id))
- throw new IllegalArgumentException(REQUIRED_PRODUCT_ID_MESSAGE);
- Product foundProduct = Optional.ofNullable(productsMap.get(id)).orElseThrow(ProductNotFoundException::new);
- if (foundProduct.getStatus() == ProductStatus.INACTIVE) {
+ if(Objects.isNull(productId)) {
+ throw new IllegalArgumentException(REQUIRED_PRODUCT_ID_MESSAGE);
+ }
+ Product product = Optional.ofNullable(productsMap.get(productId))
+ .orElseThrow(ProductNotFoundException::new);
+ if (filterValid && statusIsNotValid(product.getStatus())) {
throw new ProductNotFoundException();
}
- if (institutionType != null && foundProduct.getInstitutionContractMappings() != null && foundProduct.getInstitutionContractMappings().containsKey(institutionType)) {
- foundProduct.setContractTemplatePath(foundProduct.getInstitutionContractMappings().get(institutionType).getContractTemplatePath());
- foundProduct.setContractTemplateVersion(foundProduct.getInstitutionContractMappings().get(institutionType).getContractTemplateVersion());
+
+ if (product.getParentId() != null) {
+ Product parent = Optional.ofNullable(productsMap.get(product.getParentId()))
+ .orElseThrow(ProductNotFoundException::new);
+ if (filterValid && statusIsNotValid(parent.getStatus())) {
+ throw new ProductNotFoundException();
+ }
+
+ product.setParent(parent);
}
- return foundProduct;
+
+ return product;
}
+ /**
+ * Fills contractTemplatePath and ContractTemplateVersion based on @param institutionType.
+ * If institutionContractMappings contains institutionType, it take value from that setting inside
+ * contractTemplatePath and contractTemplateVersion of product
+ * @param product Product
+ * @param institutionType InstitutionType
+ */
@Override
- public Product getProductIsValid(String id) {
- if(Objects.isNull(id))
- throw new IllegalArgumentException(REQUIRED_PRODUCT_ID_MESSAGE);
- Product foundProduct = Optional.ofNullable(productsMap.get(id)).orElseThrow(ProductNotFoundException::new);
- Product baseProduct = null;
- if (foundProduct.getParentId() != null) {
- baseProduct = Optional.ofNullable(productsMap.get(id)).orElseThrow(ProductNotFoundException::new);
- if (baseProduct.getStatus() == ProductStatus.PHASE_OUT) {
- return null;
- } else if (foundProduct.getStatus() != ProductStatus.PHASE_OUT){
- foundProduct.setParent(baseProduct);
- return foundProduct;
- }
- } else if (foundProduct.getStatus() != ProductStatus.PHASE_OUT) {
- return foundProduct;
+ public void fillContractTemplatePathAndVersion(Product product, InstitutionType institutionType) {
+ if (institutionType != null && product.getInstitutionContractMappings() != null && product.getInstitutionContractMappings().containsKey(institutionType)) {
+ product.setContractTemplatePath(product.getInstitutionContractMappings().get(institutionType).getContractTemplatePath());
+ product.setContractTemplateVersion(product.getInstitutionContractMappings().get(institutionType).getContractTemplateVersion());
}
+ }
+
+
+ /**
+ * Returns the information for a single product if it has not PHASE_OUT,INACTIVE status and its parent has not PHASE_OUT,INACTIVE status
+ * @param productId
+ * @return Product if it is valid or null if it has PHASE_OUT,INACTIVE status
+ * @throws IllegalArgumentException product id is null
+ * @throws ProductNotFoundException product id or its parent does not exist or have PHASE_OUT,INACTIVE status
+ */
+ @Override
+ public Product getProductIsValid(String productId) {
+ return getProduct(productId, true);
+ }
- return null;
+ private static boolean statusIsNotValid(ProductStatus status) {
+ return List.of(ProductStatus.INACTIVE, ProductStatus.PHASE_OUT).contains(status);
}
diff --git a/onboarding-sdk/onboarding-sdk-product/src/test/java/ProductServiceDefaultTest.java b/onboarding-sdk/onboarding-sdk-product/src/test/java/ProductServiceDefaultTest.java
new file mode 100644
index 000000000..84438221a
--- /dev/null
+++ b/onboarding-sdk/onboarding-sdk-product/src/test/java/ProductServiceDefaultTest.java
@@ -0,0 +1,116 @@
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import it.pagopa.selfcare.onboarding.common.PartyRole;
+import it.pagopa.selfcare.product.entity.ProductRole;
+import it.pagopa.selfcare.product.entity.ProductRoleInfo;
+import it.pagopa.selfcare.product.exception.InvalidRoleMappingException;
+import it.pagopa.selfcare.product.exception.ProductNotFoundException;
+import it.pagopa.selfcare.product.service.ProductServiceDefault;
+import org.junit.jupiter.api.Test;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class ProductServiceDefaultTest {
+
+ final private String PRODUCT_JSON_STRING_EMPTY = "[]";
+ final private String PRODUCT_JSON_STRING = "[{\"id\":\"prod-test-parent\",\"status\":\"ACTIVE\"}," +
+ "{\"id\":\"prod-test\", \"parentId\":\"prod-test-parent\",\"status\":\"ACTIVE\"}," +
+ "{\"id\":\"prod-inactive\",\"status\":\"INACTIVE\"}]";
+
+ @Test
+ void productServiceDefault_shouldThrowProductNotFoundExceptionIfJsonIsEmpty() {
+ assertThrows(ProductNotFoundException.class, () -> new ProductServiceDefault(PRODUCT_JSON_STRING_EMPTY));
+ }
+ @Test
+ void productServiceDefault_shouldNotThrowProductNotFoundExceptionIfJsonIsValid() {
+ assertDoesNotThrow(() -> new ProductServiceDefault(PRODUCT_JSON_STRING));
+ }
+ @Test
+ void productServiceDefault_shouldThrowProductNotFoundExceptionIfJsonIsEmptyAndMapper() {
+ assertThrows(ProductNotFoundException.class, () -> new ProductServiceDefault(PRODUCT_JSON_STRING_EMPTY, new ObjectMapper()));
+ }
+
+ @Test
+ void getProducts_shouldReturnProductsRootOnly() throws JsonProcessingException {
+ ProductServiceDefault productService = new ProductServiceDefault(PRODUCT_JSON_STRING);
+ assertEquals( 2, productService.getProducts(true, false).size());
+ }
+
+ @Test
+ void getProducts_shouldReturnProductsAll() throws JsonProcessingException {
+ ProductServiceDefault productService = new ProductServiceDefault(PRODUCT_JSON_STRING);
+ assertEquals(3, productService.getProducts(false, false).size());
+ }
+
+ @Test
+ void getProducts_shouldReturnProductsRootOnlyAndValid() throws JsonProcessingException {
+ ProductServiceDefault productService = new ProductServiceDefault(PRODUCT_JSON_STRING);
+ assertEquals( 1, productService.getProducts(true, true).size());
+ }
+
+ @Test
+ void getProducts_shouldReturnProductsAllAndValid() throws JsonProcessingException {
+ ProductServiceDefault productService = new ProductServiceDefault(PRODUCT_JSON_STRING);
+ assertEquals(2, productService.getProducts(false, true).size());
+ }
+
+ @Test
+ void validateRoleMappings_shouldThrowIllegalExceptionIfRoleMappingIsNull() throws JsonProcessingException {
+ ProductServiceDefault productService = new ProductServiceDefault(PRODUCT_JSON_STRING);
+ assertThrows(IllegalArgumentException.class, () -> productService.validateRoleMappings(new HashMap<>()));
+ }
+
+ @Test
+ void validateRoleMappings_shouldThrowIllegalExceptionIfProductRoleInfoIsNull() throws JsonProcessingException {
+ ProductServiceDefault productService = new ProductServiceDefault(PRODUCT_JSON_STRING);
+ Map roleMappings = new HashMap<>();
+ roleMappings.put(PartyRole.MANAGER, null);
+ assertThrows(IllegalArgumentException.class, () -> productService.validateRoleMappings(roleMappings));
+ }
+
+ @Test
+ void validateRoleMappings_shouldThrowIllegalExceptionIfProductRoleInfoIsRolesEmpty() throws JsonProcessingException {
+ ProductServiceDefault productService = new ProductServiceDefault(PRODUCT_JSON_STRING);
+ Map roleMappings = new HashMap<>();
+ roleMappings.put(PartyRole.MANAGER, new ProductRoleInfo());
+ assertThrows(IllegalArgumentException.class, () -> productService.validateRoleMappings(roleMappings));
+ }
+
+ @Test
+ void validateRoleMappings_shouldThrowInvalidRoleMappingExceptionIfProductRoleInfoIsRolesEmpty() throws JsonProcessingException {
+ ProductServiceDefault productService = new ProductServiceDefault(PRODUCT_JSON_STRING);
+ Map roleMappings = new HashMap<>();
+ ProductRoleInfo productRoleInfo1 = new ProductRoleInfo();
+ productRoleInfo1.setRoles(List.of(new ProductRole(), new ProductRole()));
+ roleMappings.put(PartyRole.MANAGER, productRoleInfo1);
+ assertThrows(InvalidRoleMappingException.class, () -> productService.validateRoleMappings(roleMappings));
+ }
+
+ @Test
+ void getProduct_shouldThrowIllegalExceptionIfIdNull() throws JsonProcessingException {
+ ProductServiceDefault productService = new ProductServiceDefault(PRODUCT_JSON_STRING);
+ assertThrows(IllegalArgumentException.class, () -> productService.getProduct(null));
+ }
+
+ @Test
+ void getProduct_shouldThrowProductNotFoundExceptionIfIdNotFound() throws JsonProcessingException {
+ ProductServiceDefault productService = new ProductServiceDefault(PRODUCT_JSON_STRING);
+ assertThrows(ProductNotFoundException.class, () -> productService.getProduct("prod-not-found"));
+ }
+
+ @Test
+ void getProduct_shouldGetProduct() throws JsonProcessingException {
+ ProductServiceDefault productService = new ProductServiceDefault(PRODUCT_JSON_STRING);
+ assertNotNull(productService.getProduct("prod-test"));
+ }
+
+ @Test
+ void getProductValid_shouldThrowProductNotFoundExceptionIfProductInactive() throws JsonProcessingException {
+ ProductServiceDefault productService = new ProductServiceDefault(PRODUCT_JSON_STRING);
+ assertThrows(ProductNotFoundException.class, () -> productService.getProductIsValid("prod-inactive"));
+ }
+}
diff --git a/onboarding-sdk/pom.xml b/onboarding-sdk/pom.xml
index cdd130312..6aa9c4584 100644
--- a/onboarding-sdk/pom.xml
+++ b/onboarding-sdk/pom.xml
@@ -10,11 +10,11 @@
onboarding-sdk
pom
onboarding-sdk
- 0.0.4
+ 0.1.0
- 11
- 11
+ 17
+ 17
onboarding-sdk-product