diff --git a/connector-api/src/main/java/it/pagopa/selfcare/dashboard/connector/api/UserApiConnector.java b/connector-api/src/main/java/it/pagopa/selfcare/dashboard/connector/api/UserApiConnector.java new file mode 100644 index 000000000..d18a0700d --- /dev/null +++ b/connector-api/src/main/java/it/pagopa/selfcare/dashboard/connector/api/UserApiConnector.java @@ -0,0 +1,15 @@ +package it.pagopa.selfcare.dashboard.connector.api; + +import it.pagopa.selfcare.dashboard.connector.model.institution.InstitutionInfo; +import it.pagopa.selfcare.dashboard.connector.model.user.MutableUserFieldsDto; + +import java.util.List; + + +public interface UserApiConnector { + + void updateUser(String userId, String institutionId, MutableUserFieldsDto userDto); + + List getUserProducts(String userId); + +} diff --git a/connector/rest/docs/openapi/api-user-docs.json b/connector/rest/docs/openapi/api-user-docs.json new file mode 100644 index 000000000..054a77d7d --- /dev/null +++ b/connector/rest/docs/openapi/api-user-docs.json @@ -0,0 +1,1094 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "User API (development)", + "version": "1.0.0" + }, + "paths": { + "/institutions/{institutionId}/products/{productId}/createdAt": { + "put": { + "tags": [ + "Institution Controller" + ], + "summary": "The API updates user's onboarded product with createdAt passed in input", + "parameters": [ + { + "name": "institutionId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "productId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "createdAt", + "in": "query", + "required": true, + "schema": { + "$ref": "#/components/schemas/LocalDateTime" + } + }, + { + "name": "userIds", + "in": "query", + "required": true, + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": {} + } + }, + "401": { + "description": "Not Authorized" + }, + "403": { + "description": "Not Allowed" + } + }, + "security": [ + { + "SecurityScheme": [] + } + ] + } + }, + "/institutions/{institutionId}/user-institutions": { + "get": { + "tags": [ + "Institution Controller" + ], + "summary": "The API retrieves users with optional filters in input as query params", + "parameters": [ + { + "name": "institutionId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "productRoles", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + { + "name": "products", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + { + "name": "roles", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + { + "name": "states", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + { + "name": "userId", + "in": "query", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/UserInstitutionResponse" + } + } + } + } + }, + "401": { + "description": "Not Authorized" + }, + "403": { + "description": "Not Allowed" + } + }, + "security": [ + { + "SecurityScheme": [] + } + ] + } + }, + "/institutions/{institutionId}/users": { + "get": { + "tags": [ + "Institution Controller" + ], + "summary": "The API retrieves user's info including details of roles on products", + "parameters": [ + { + "name": "institutionId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/UserProductResponse" + } + } + } + } + }, + "401": { + "description": "Not Authorized" + }, + "403": { + "description": "Not Allowed" + } + }, + "security": [ + { + "SecurityScheme": [] + } + ] + } + }, + "/users": { + "get": { + "tags": [ + "User Controller" + ], + "summary": "The API retrieves paged users with optional filters in input as query params", + "parameters": [ + { + "name": "institutionId", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "page", + "in": "query", + "schema": { + "format": "int32", + "default": "0", + "type": "integer" + } + }, + { + "name": "productRoles", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + { + "name": "products", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + { + "name": "roles", + "in": "query", + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PartyRole" + } + } + }, + { + "name": "size", + "in": "query", + "schema": { + "format": "int32", + "default": "100", + "type": "integer" + } + }, + { + "name": "states", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + { + "name": "userId", + "in": "query", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/UserInstitutionResponse" + } + } + } + } + }, + "401": { + "description": "Not Authorized" + }, + "403": { + "description": "Not Allowed" + } + }, + "security": [ + { + "SecurityScheme": [] + } + ] + } + }, + "/users/emails": { + "get": { + "tags": [ + "User Controller" + ], + "summary": "The API retrieves Users' emails using institution id and product id", + "parameters": [ + { + "name": "institutionId", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "productId", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "401": { + "description": "Not Authorized" + }, + "403": { + "description": "Not Allowed" + } + }, + "security": [ + { + "SecurityScheme": [] + } + ] + } + }, + "/users/ids": { + "get": { + "tags": [ + "User Controller" + ], + "summary": "Retrieve all users given their userIds", + "parameters": [ + { + "name": "userIds", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/UserInstitutionResponse" + } + } + } + } + }, + "401": { + "description": "Not Authorized" + }, + "403": { + "description": "Not Allowed" + } + }, + "security": [ + { + "SecurityScheme": [] + } + ] + } + }, + "/users/notification": { + "get": { + "tags": [ + "User Controller" + ], + "summary": "Retrieve all SC-User for DataLake filtered by optional productId", + "parameters": [ + { + "name": "page", + "in": "query", + "schema": { + "format": "int32", + "default": "0", + "type": "integer" + } + }, + { + "name": "productId", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "size", + "in": "query", + "schema": { + "format": "int32", + "default": "100", + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UsersNotificationResponse" + } + } + } + }, + "401": { + "description": "Not Authorized" + }, + "403": { + "description": "Not Allowed" + } + }, + "security": [ + { + "SecurityScheme": [] + } + ] + } + }, + "/users/{id}": { + "get": { + "tags": [ + "User Controller" + ], + "summary": "Retrieves user given userId and optional ProductId", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "institutionId", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "productId", + "in": "query", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserResponse" + } + } + } + }, + "401": { + "description": "Not Authorized" + }, + "403": { + "description": "Not Allowed" + } + }, + "security": [ + { + "SecurityScheme": [] + } + ] + } + }, + "/users/{id}/status": { + "put": { + "tags": [ + "User Controller" + ], + "summary": "Update user status with optional filter for institution, product, role and productRole", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "institutionId", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "productId", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "productRole", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "role", + "in": "query", + "schema": { + "$ref": "#/components/schemas/PartyRole" + } + }, + { + "name": "status", + "in": "query", + "schema": { + "$ref": "#/components/schemas/OnboardedProductState" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": {} + } + }, + "401": { + "description": "Not Authorized" + }, + "403": { + "description": "Not Allowed" + } + }, + "security": [ + { + "SecurityScheme": [] + } + ] + } + }, + "/users/{id}/user-registry": { + "put": { + "tags": [ + "User Controller" + ], + "summary": "Service to update user in user-registry and send notification when user data gets updated", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "institutionId", + "in": "query", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserRegistryFieldsDto" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": {} + } + }, + "401": { + "description": "Not Authorized" + }, + "403": { + "description": "Not Allowed" + } + }, + "security": [ + { + "SecurityScheme": [] + } + ] + } + }, + "/users/{userId}/institutions/{institutionId}/products/{productId}": { + "delete": { + "tags": [ + "User Controller" + ], + "summary": "Delete logically the association institution and product", + "parameters": [ + { + "name": "institutionId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "productId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "userId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "401": { + "description": "Not Authorized" + }, + "403": { + "description": "Not Allowed" + } + }, + "security": [ + { + "SecurityScheme": [] + } + ] + } + }, + "/users/{userId}/products": { + "get": { + "tags": [ + "User Controller" + ], + "summary": "Retrieves products info and role which the user is enabled", + "parameters": [ + { + "name": "userId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "institutionId", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "states", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserProductsResponse" + } + } + } + }, + "401": { + "description": "Not Authorized" + }, + "403": { + "description": "Not Allowed" + } + }, + "security": [ + { + "SecurityScheme": [] + } + ] + } + } + }, + "components": { + "schemas": { + "CertifiableFieldResourceOfLocalDate": { + "type": "object", + "properties": { + "certification": { + "$ref": "#/components/schemas/CertificationEnum1" + }, + "value": { + "$ref": "#/components/schemas/LocalDate" + } + } + }, + "CertifiableFieldResourceOfstring": { + "type": "object", + "properties": { + "certification": { + "$ref": "#/components/schemas/CertificationEnum" + }, + "value": { + "type": "string" + } + } + }, + "CertificationEnum": { + "enum": [ + "NONE", + "SPID" + ], + "type": "string" + }, + "CertificationEnum1": { + "enum": [ + "NONE", + "SPID" + ], + "type": "string" + }, + "Env": { + "enum": [ + "ROOT", + "DEV", + "COLL", + "PROD" + ], + "type": "string" + }, + "InstitutionProducts": { + "type": "object", + "properties": { + "institutionId": { + "type": "string" + }, + "institutionName": { + "type": "string" + }, + "institutionRootName": { + "type": "string" + }, + "products": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OnboardedProductResponse" + } + } + } + }, + "LocalDate": { + "format": "date", + "type": "string", + "example": "2022-03-10T00:00:00.000Z" + }, + "LocalDateTime": { + "format": "date-time", + "type": "string", + "example": "2022-03-10T12:15:50.000Z" + }, + "UserRegistryFieldsDto": { + "type": "object", + "properties": { + "birthDate": { + "$ref": "#/components/schemas/CertifiableFieldResourceOfLocalDate" + }, + "email": { + "$ref": "#/components/schemas/CertifiableFieldResourceOfstring" + }, + "familyName": { + "$ref": "#/components/schemas/CertifiableFieldResourceOfstring" + }, + "name": { + "$ref": "#/components/schemas/CertifiableFieldResourceOfstring" + }, + "workContacts": { + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/WorkContactResource" + } + } + } + }, + "OnboardedProductResponse": { + "type": "object", + "properties": { + "productId": { + "type": "string" + }, + "relationshipId": { + "type": "string" + }, + "tokenId": { + "type": "string" + }, + "status": { + "$ref": "#/components/schemas/OnboardedProductState" + }, + "productRole": { + "type": "string" + }, + "role": { + "$ref": "#/components/schemas/PartyRole" + }, + "env": { + "$ref": "#/components/schemas/Env" + }, + "createdAt": { + "$ref": "#/components/schemas/LocalDateTime" + }, + "updatedAt": { + "$ref": "#/components/schemas/LocalDateTime" + } + } + }, + "OnboardedProductState": { + "enum": [ + "ACTIVE", + "PENDING", + "TOBEVALIDATED", + "SUSPENDED", + "DELETED", + "REJECTED" + ], + "type": "string" + }, + "PartyRole": { + "enum": [ + "MANAGER", + "DELEGATE", + "SUB_DELEGATE", + "OPERATOR" + ], + "type": "string" + }, + "QueueEvent": { + "enum": [ + "ADD", + "UPDATE" + ], + "type": "string" + }, + "UserInstitutionResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "userId": { + "type": "string" + }, + "institutionId": { + "type": "string" + }, + "institutionDescription": { + "type": "string" + }, + "products": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OnboardedProductResponse" + } + } + } + }, + "UserNotificationResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "institutionId": { + "type": "string" + }, + "productId": { + "type": "string" + }, + "onboardingTokenId": { + "type": "string" + }, + "createdAt": { + "$ref": "#/components/schemas/LocalDateTime" + }, + "updatedAt": { + "$ref": "#/components/schemas/LocalDateTime" + }, + "eventType": { + "$ref": "#/components/schemas/QueueEvent" + }, + "user": { + "$ref": "#/components/schemas/UserToNotify" + } + } + }, + "UserProductResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "taxCode": { + "type": "string" + }, + "name": { + "type": "string" + }, + "surname": { + "type": "string" + }, + "email": { + "type": "string" + }, + "products": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OnboardedProductResponse" + } + } + } + }, + "UserProductsResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "bindings": { + "type": "array", + "items": { + "$ref": "#/components/schemas/InstitutionProducts" + } + } + } + }, + "UserResponse": { + "required": [ + "id", + "name", + "surname" + ], + "type": "object", + "properties": { + "id": { + "minLength": 1, + "type": "string" + }, + "taxCode": { + "type": "string" + }, + "name": { + "pattern": "\\S", + "type": "string" + }, + "surname": { + "pattern": "\\S", + "type": "string" + }, + "email": { + "type": "string" + } + } + }, + "UserToNotify": { + "type": "object", + "properties": { + "userId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "familyName": { + "type": "string" + }, + "email": { + "type": "string" + }, + "role": { + "$ref": "#/components/schemas/PartyRole" + }, + "productRole": { + "type": "string" + }, + "relationshipStatus": { + "$ref": "#/components/schemas/OnboardedProductState" + } + } + }, + "UsersNotificationResponse": { + "type": "object", + "properties": { + "users": { + "type": "array", + "items": { + "$ref": "#/components/schemas/UserNotificationResponse" + } + } + } + }, + "WorkContactResource": { + "type": "object", + "properties": { + "email": { + "$ref": "#/components/schemas/CertifiableFieldResourceOfstring" + } + } + } + }, + "securitySchemes": { + "SecurityScheme": { + "type": "http", + "description": "Authentication", + "scheme": "bearer", + "bearerFormat": "JWT" + } + } + } +} \ No newline at end of file diff --git a/connector/rest/pom.xml b/connector/rest/pom.xml index 10b0b17df..10a358b9f 100644 --- a/connector/rest/pom.xml +++ b/connector/rest/pom.xml @@ -112,6 +112,44 @@ + + org.openapitools + openapi-generator-maven-plugin + 6.3.0 + + + user-ms + + generate + + process-resources + + ${project.basedir}/docs/openapi/api-user-docs.json + spring + spring-cloud + + false + false + + true + ${project.groupId}.user.generated.openapi.v1 + ${project.groupId}.user.generated.openapi.v1.dto + ${project.groupId}.user.generated.openapi.v1.api + ${project.groupId}.user.generated.openapi.v1.config + @lombok.Builder; @lombok.NoArgsConstructor; @lombok.AllArgsConstructor + java8 + true + true + none + source + false + false + true + + + + + diff --git a/connector/rest/src/main/java/it/pagopa/selfcare/dashboard/connector/rest/UserConnectorImpl.java b/connector/rest/src/main/java/it/pagopa/selfcare/dashboard/connector/rest/UserConnectorImpl.java new file mode 100644 index 000000000..a2645959f --- /dev/null +++ b/connector/rest/src/main/java/it/pagopa/selfcare/dashboard/connector/rest/UserConnectorImpl.java @@ -0,0 +1,57 @@ +package it.pagopa.selfcare.dashboard.connector.rest; + +import it.pagopa.selfcare.dashboard.connector.api.UserApiConnector; +import it.pagopa.selfcare.dashboard.connector.model.institution.InstitutionInfo; +import it.pagopa.selfcare.dashboard.connector.model.user.MutableUserFieldsDto; +import it.pagopa.selfcare.dashboard.connector.rest.client.UserApiRestClient; +import it.pagopa.selfcare.dashboard.connector.rest.model.mapper.InstitutionMapper; +import it.pagopa.selfcare.dashboard.connector.rest.model.mapper.UserMapper; +import it.pagopa.selfcare.user.generated.openapi.v1.dto.*; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Objects; + +import static it.pagopa.selfcare.dashboard.connector.model.institution.RelationshipState.*; + +@Slf4j +@Service +@ConditionalOnProperty(value = "dashboard.user.client.api-version", havingValue = "v2") +@RequiredArgsConstructor +public class UserConnectorImpl implements UserApiConnector { + + + private final UserApiRestClient userApiRestClient; + private final InstitutionMapper institutionMapper; + private final UserMapper userMapper; + + + @Override + public void updateUser(String userId, String institutionId, MutableUserFieldsDto userDto) { + log.trace("updateUser start"); + log.debug("updateUser userId = {}, institutionId = {}", userId, institutionId); + userApiRestClient._usersIdUserRegistryPut(userId, institutionId, userMapper.toMutableUserFieldsDto(userDto)); + log.trace("updateUser end"); + } + + @Override + @ConditionalOnProperty(value = "dashboard.user.client.api-version", havingValue = "v2") + public List getUserProducts(String userId) { + log.trace("getUserProducts start"); + UserProductsResponse productsInfoUsingGET = userApiRestClient._usersUserIdProductsGet(userId, null, + List.of(ACTIVE.name(), PENDING.name(), TOBEVALIDATED.name())).getBody(); + + if(Objects.isNull(productsInfoUsingGET) || + Objects.isNull(productsInfoUsingGET.getBindings())) return List.of(); + + List result = productsInfoUsingGET.getBindings().stream() + .map(institutionMapper::toInstitutionInfo) + .toList(); + log.debug("getUserProducts result = {}", result); + log.trace("getUserProducts end"); + return result; + } +} diff --git a/connector/rest/src/main/java/it/pagopa/selfcare/dashboard/connector/rest/client/UserApiRestClient.java b/connector/rest/src/main/java/it/pagopa/selfcare/dashboard/connector/rest/client/UserApiRestClient.java new file mode 100644 index 000000000..eeb4e8f63 --- /dev/null +++ b/connector/rest/src/main/java/it/pagopa/selfcare/dashboard/connector/rest/client/UserApiRestClient.java @@ -0,0 +1,8 @@ +package it.pagopa.selfcare.dashboard.connector.rest.client; + +import it.pagopa.selfcare.user.generated.openapi.v1.api.UserControllerApi; +import org.springframework.cloud.openfeign.FeignClient; + +@FeignClient(name = "${rest-client.user-api.serviceCode}", url = "${rest-client.user-api.base-url}") +public interface UserApiRestClient extends UserControllerApi { +} diff --git a/connector/rest/src/main/java/it/pagopa/selfcare/dashboard/connector/rest/config/UserApiRestClientConfig.java b/connector/rest/src/main/java/it/pagopa/selfcare/dashboard/connector/rest/config/UserApiRestClientConfig.java new file mode 100644 index 000000000..5c4a97e16 --- /dev/null +++ b/connector/rest/src/main/java/it/pagopa/selfcare/dashboard/connector/rest/config/UserApiRestClientConfig.java @@ -0,0 +1,15 @@ +package it.pagopa.selfcare.dashboard.connector.rest.config; + +import it.pagopa.selfcare.commons.connector.rest.config.RestClientBaseConfig; +import it.pagopa.selfcare.dashboard.connector.rest.client.UserApiRestClient; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.PropertySource; + +@Configuration +@Import(RestClientBaseConfig.class) +@EnableFeignClients(clients = UserApiRestClient.class) +@PropertySource("classpath:config/user-rest-client.properties") +public class UserApiRestClientConfig { +} diff --git a/connector/rest/src/main/java/it/pagopa/selfcare/dashboard/connector/rest/model/mapper/InstitutionMapper.java b/connector/rest/src/main/java/it/pagopa/selfcare/dashboard/connector/rest/model/mapper/InstitutionMapper.java index 2479cefe0..02d5d0021 100644 --- a/connector/rest/src/main/java/it/pagopa/selfcare/dashboard/connector/rest/model/mapper/InstitutionMapper.java +++ b/connector/rest/src/main/java/it/pagopa/selfcare/dashboard/connector/rest/model/mapper/InstitutionMapper.java @@ -2,9 +2,9 @@ import it.pagopa.selfcare.core.generated.openapi.v1.dto.InstitutionProducts; import it.pagopa.selfcare.core.generated.openapi.v1.dto.Product; -import it.pagopa.selfcare.core.generated.openapi.v1.dto.UserProductsResponse; import it.pagopa.selfcare.dashboard.connector.model.institution.InstitutionInfo; import it.pagopa.selfcare.dashboard.connector.model.institution.RelationshipState; +import it.pagopa.selfcare.user.generated.openapi.v1.dto.OnboardedProductResponse; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.Named; @@ -27,4 +27,20 @@ default RelationshipState toStatus(InstitutionProducts institutionProducts) { .map(statusEnum -> RelationshipState.valueOf(statusEnum.name())) .orElse(null); } + + @Mapping(target = "id", source = "institutionId") + @Mapping(target = "description", source = "institutionName") + @Mapping(target = "parentDescription", source = "institutionRootName") + @Mapping(target = "status", source = ".", qualifiedByName = "toStatus") + InstitutionInfo toInstitutionInfo(it.pagopa.selfcare.user.generated.openapi.v1.dto.InstitutionProducts institutionProducts); + + @Named("toStatus") + default RelationshipState toStatus(it.pagopa.selfcare.user.generated.openapi.v1.dto.InstitutionProducts institutionProducts) { + return institutionProducts.getProducts().stream() + .map(OnboardedProductResponse::getStatus) + .sorted() + .findFirst() + .map(statusEnum -> RelationshipState.valueOf(statusEnum.name())) + .orElse(null); + } } diff --git a/connector/rest/src/main/java/it/pagopa/selfcare/dashboard/connector/rest/model/mapper/UserMapper.java b/connector/rest/src/main/java/it/pagopa/selfcare/dashboard/connector/rest/model/mapper/UserMapper.java new file mode 100644 index 000000000..1c7c7ecd0 --- /dev/null +++ b/connector/rest/src/main/java/it/pagopa/selfcare/dashboard/connector/rest/model/mapper/UserMapper.java @@ -0,0 +1,46 @@ +package it.pagopa.selfcare.dashboard.connector.rest.model.mapper; + +import it.pagopa.selfcare.dashboard.connector.model.user.CertifiedField; +import it.pagopa.selfcare.dashboard.connector.model.user.MutableUserFieldsDto; +import it.pagopa.selfcare.dashboard.connector.model.user.WorkContact; +import it.pagopa.selfcare.user.generated.openapi.v1.dto.CertifiableFieldResourceOfstring; +import it.pagopa.selfcare.user.generated.openapi.v1.dto.CertificationEnum; +import it.pagopa.selfcare.user.generated.openapi.v1.dto.UserRegistryFieldsDto; +import it.pagopa.selfcare.user.generated.openapi.v1.dto.WorkContactResource; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Named; + +import java.util.HashMap; +import java.util.Map; + +@Mapper(componentModel = "spring") +public interface UserMapper { + + @Mapping(target = "email", expression = "java(toCertifiableFieldResourceOfString(userDto.getEmail()))") + @Mapping(target = "name", expression = "java(toCertifiableFieldResourceOfString(userDto.getName()))") + @Mapping(target = "familyName", expression = "java(toCertifiableFieldResourceOfString(userDto.getFamilyName()))") + @Mapping(target = "workContacts", expression = "java(toWorkContacts(userDto.getWorkContacts()))") + UserRegistryFieldsDto toMutableUserFieldsDto(MutableUserFieldsDto userDto); + + @Named("toCertifiableFieldResourceOfString") + default CertifiableFieldResourceOfstring toCertifiableFieldResourceOfString(CertifiedField field){ + if(field != null && field.getCertification() != null) { + return CertifiableFieldResourceOfstring.builder() + .certification(CertificationEnum.valueOf(field.getCertification().name())) + .value(field.getValue()) + .build(); + } + return null; + } + + @Named("toWorkContacts") + default Map toWorkContacts(Map workContactMap){ + Map resourceMap = new HashMap<>(); + if(workContactMap != null && !workContactMap.isEmpty()) { + workContactMap.forEach((s, workContact) -> resourceMap.put(s, WorkContactResource.builder().email(toCertifiableFieldResourceOfString(workContact.getEmail())).build())); + } + return resourceMap; + } + +} diff --git a/connector/rest/src/main/resources/config/user-rest-client.properties b/connector/rest/src/main/resources/config/user-rest-client.properties new file mode 100644 index 000000000..6b55d7af4 --- /dev/null +++ b/connector/rest/src/main/resources/config/user-rest-client.properties @@ -0,0 +1,5 @@ +rest-client.user-ms.base-url=${SELFCARE_USER_URL:http://localhost:8080} +rest-client.user-ms.serviceCode=user-ms +feign.client.config.user-ms.connectTimeout=${SELFCARE_USER_REST_CLIENT_CONNECT_TIMEOUT:${REST_CLIENT_CONNECT_TIMEOUT:5000}} +feign.client.config.user-ms.readTimeout=${SELFCARE_USER_REST_CLIENT_READ_TIMEOUT:${REST_CLIENT_READ_TIMEOUT:5000}} +feign.client.config.user-ms.loggerLevel=${SELFCARE_USER_REST_CLIENT_LOGGER_LEVEL:${REST_CLIENT_LOGGER_LEVEL:FULL}} \ No newline at end of file diff --git a/connector/rest/src/test/java/it/pagopa/selfcare/dashboard/connector/rest/UserConnectorImplTest.java b/connector/rest/src/test/java/it/pagopa/selfcare/dashboard/connector/rest/UserConnectorImplTest.java new file mode 100644 index 000000000..5e574bf8d --- /dev/null +++ b/connector/rest/src/test/java/it/pagopa/selfcare/dashboard/connector/rest/UserConnectorImplTest.java @@ -0,0 +1,95 @@ +package it.pagopa.selfcare.dashboard.connector.rest; + +import it.pagopa.selfcare.dashboard.connector.exception.ResourceNotFoundException; +import it.pagopa.selfcare.dashboard.connector.model.institution.InstitutionInfo; +import it.pagopa.selfcare.dashboard.connector.model.user.MutableUserFieldsDto; +import it.pagopa.selfcare.dashboard.connector.rest.client.UserApiRestClient; +import it.pagopa.selfcare.dashboard.connector.rest.model.mapper.InstitutionMapperImpl; +import it.pagopa.selfcare.dashboard.connector.rest.model.mapper.UserMapper; +import it.pagopa.selfcare.dashboard.connector.rest.model.mapper.UserMapperImpl; +import it.pagopa.selfcare.user.generated.openapi.v1.dto.*; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.ContextConfiguration; + +import java.util.List; + +import static it.pagopa.selfcare.dashboard.connector.model.institution.RelationshipState.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +@ContextConfiguration(classes = {UserConnectorImpl.class, InstitutionMapperImpl.class, UserMapper.class}) +class UserConnectorImplTest { + + @Mock + UserApiRestClient userApiRestClient; + + + UserConnectorImpl userConnector; + + @Test + void updateUserOK() { + userConnector = new UserConnectorImpl(userApiRestClient, new InstitutionMapperImpl(), new UserMapperImpl()); + when(userApiRestClient._usersIdUserRegistryPut(eq("userID"),eq("InstitutionId"), any(UserRegistryFieldsDto.class))) + .thenReturn(ResponseEntity.ok().build()); + Assertions.assertDoesNotThrow(() -> userConnector.updateUser("userID", "InstitutionId", new MutableUserFieldsDto())); + } + + @Test + void updateUserKO() { + userConnector = new UserConnectorImpl(userApiRestClient, new InstitutionMapperImpl(), new UserMapperImpl()); + when(userApiRestClient._usersIdUserRegistryPut(eq("userID"),eq("InstitutionId"), any(UserRegistryFieldsDto.class))) + .thenThrow(ResourceNotFoundException.class); + Assertions.assertThrows(ResourceNotFoundException.class, () -> userConnector.updateUser("userID", "InstitutionId", new MutableUserFieldsDto())); + } + + @Test + void getUserProductsNotFound() { + userConnector = new UserConnectorImpl(userApiRestClient, new InstitutionMapperImpl(), new UserMapperImpl()); + when(userApiRestClient._usersUserIdProductsGet("userID", null, + List.of(ACTIVE.name(), PENDING.name(), TOBEVALIDATED.name()))).thenThrow(ResourceNotFoundException.class); + Assertions.assertThrows(ResourceNotFoundException.class, () -> userConnector.getUserProducts("userID")); + } + + @Test + void getUserProductsFound() { + userConnector = new UserConnectorImpl(userApiRestClient, new InstitutionMapperImpl(), new UserMapperImpl()); + UserProductsResponse userProductsResponse = getUserProductsResponse(); + when(userApiRestClient._usersUserIdProductsGet("userID", null, + List.of(ACTIVE.name(), PENDING.name(), TOBEVALIDATED.name()))).thenReturn(ResponseEntity.ok(userProductsResponse)); + List result = userConnector.getUserProducts("userID"); + Assertions.assertEquals(1, result.size()); + Assertions.assertEquals(ACTIVE, result.get(0).getStatus()); + Assertions.assertEquals("institutionId", result.get(0).getId()); + Assertions.assertEquals("institutionName", result.get(0).getDescription()); + } + + private static UserProductsResponse getUserProductsResponse() { + UserProductsResponse userProductsResponse = new UserProductsResponse(); + userProductsResponse.setId("userID"); + InstitutionProducts institutionProducts = new InstitutionProducts(); + institutionProducts.setInstitutionId("institutionId"); + institutionProducts.setInstitutionName("institutionName"); + institutionProducts.setProducts(getOnboardedProduct()); + userProductsResponse.setBindings(List.of(institutionProducts)); + return userProductsResponse; + } + + private static List getOnboardedProduct() { + OnboardedProductResponse onboardedProductResponse = new OnboardedProductResponse(); + onboardedProductResponse.setProductId("prod-pagopa"); + onboardedProductResponse.setRole(PartyRole.MANAGER); + onboardedProductResponse.setStatus(OnboardedProductState.ACTIVE); + OnboardedProductResponse onboardedProductResponse2 = new OnboardedProductResponse(); + onboardedProductResponse2.setProductId("prod-pagopa"); + onboardedProductResponse2.setRole(PartyRole.MANAGER); + onboardedProductResponse2.setStatus(OnboardedProductState.PENDING); + return List.of(onboardedProductResponse, onboardedProductResponse2); + } +}