From 9706a837cee3db26facab08d91c72220b7c1bc29 Mon Sep 17 00:00:00 2001 From: Riccardo Torsoli <122275960+nttdata-rtorsoli@users.noreply.github.com> Date: Tue, 28 Nov 2023 15:02:52 +0100 Subject: [PATCH] PIN-4084 - tenant-process: Added digital address and onboarded date (#82) Co-authored-by: nttdata-rtorsoli --- .../resources/interface-specification.yml | 23 ++++++++++- .../api/adapters/AdaptableSeed.scala | 3 +- .../api/adapters/ApiAdapters.scala | 5 ++- .../adapters/ReadModelTenantAdapters.scala | 8 +++- .../adapters/TenantManagementAdapters.scala | 6 ++- .../api/impl/TenantApiMarshallerImpl.scala | 2 + .../api/impl/TenantApiServiceImpl.scala | 15 +++++--- .../tenantprocess/api/impl/package.scala | 5 ++- .../provider/TenantCreationSpec.scala | 38 +++++++++++++++++-- .../utils/FakeDependencies.scala | 3 +- .../tenantprocess/utils/SpecData.scala | 34 ++++++++++++++--- 11 files changed, 115 insertions(+), 27 deletions(-) diff --git a/src/main/resources/interface-specification.yml b/src/main/resources/interface-specification.yml index e7eb206..52e3ca5 100644 --- a/src/main/resources/interface-specification.yml +++ b/src/main/resources/interface-specification.yml @@ -221,7 +221,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Tenant' + $ref: '#/components/schemas/ResourceId' '409': description: Selfcare Id is already assigned and is different from the request content: @@ -817,6 +817,15 @@ components: maxLength: 64 required: - code + ResourceId: + description: Modified resource id + type: object + properties: + id: + type: string + format: uuid + required: + - id Tenant: description: Tenant model type: object @@ -849,7 +858,10 @@ components: name: type: string kind: - $ref: '#/components/schemas/TenantKind' + $ref: '#/components/schemas/TenantKind' + onboardedAt: + type: string + format: date-time required: - id - certifier @@ -1068,10 +1080,16 @@ components: type: string minLength: 1 maxLength: 1000 + digitalAddress: + $ref: '#/components/schemas/MailSeed' + onboardedAt: + type: string + format: date-time required: - externalId - selfcareId - name + - onboardedAt MailSeed: description: A specific kind of mail type: object @@ -1115,6 +1133,7 @@ components: type: string enum: - CONTACT_EMAIL + - DIGITAL_ADDRESS Problem: properties: type: diff --git a/src/main/scala/it/pagopa/interop/tenantprocess/api/adapters/AdaptableSeed.scala b/src/main/scala/it/pagopa/interop/tenantprocess/api/adapters/AdaptableSeed.scala index d79871b..c2cd25f 100644 --- a/src/main/scala/it/pagopa/interop/tenantprocess/api/adapters/AdaptableSeed.scala +++ b/src/main/scala/it/pagopa/interop/tenantprocess/api/adapters/AdaptableSeed.scala @@ -58,7 +58,8 @@ object AdaptableSeed { features = Nil, attributes = attributes, name = a.name, - kind = kind + kind = kind, + onboardedAt = Some(a.onboardedAt) ) } } diff --git a/src/main/scala/it/pagopa/interop/tenantprocess/api/adapters/ApiAdapters.scala b/src/main/scala/it/pagopa/interop/tenantprocess/api/adapters/ApiAdapters.scala index 51bceac..b6bc35c 100644 --- a/src/main/scala/it/pagopa/interop/tenantprocess/api/adapters/ApiAdapters.scala +++ b/src/main/scala/it/pagopa/interop/tenantprocess/api/adapters/ApiAdapters.scala @@ -2,7 +2,7 @@ package it.pagopa.interop.tenantprocess.api.adapters import cats.syntax.all._ import it.pagopa.interop.tenantmanagement.model.tenant.PersistentExternalId -import it.pagopa.interop.tenantmanagement.client.model.MailKind.CONTACT_EMAIL +import it.pagopa.interop.tenantmanagement.client.model.MailKind.{DIGITAL_ADDRESS, CONTACT_EMAIL} import it.pagopa.interop.tenantmanagement.client.model.{ Certifier => DependencyCertifier, DeclaredTenantAttribute => DependencyDeclaredTenantAttribute, @@ -33,7 +33,8 @@ object ApiAdapters { implicit class MailKindWrapper(private val mk: MailKind) extends AnyVal { def fromAPI: DependencyMailKind = mk match { - case MailKind.CONTACT_EMAIL => CONTACT_EMAIL + case MailKind.CONTACT_EMAIL => CONTACT_EMAIL + case MailKind.DIGITAL_ADDRESS => DIGITAL_ADDRESS } } diff --git a/src/main/scala/it/pagopa/interop/tenantprocess/api/adapters/ReadModelTenantAdapters.scala b/src/main/scala/it/pagopa/interop/tenantprocess/api/adapters/ReadModelTenantAdapters.scala index 8714c12..2d2493f 100644 --- a/src/main/scala/it/pagopa/interop/tenantprocess/api/adapters/ReadModelTenantAdapters.scala +++ b/src/main/scala/it/pagopa/interop/tenantprocess/api/adapters/ReadModelTenantAdapters.scala @@ -20,6 +20,7 @@ object ReadModelTenantAdapters extends SprayJsonSupport with DefaultJsonProtocol attributes = t.attributes.map(_.toApi), createdAt = t.createdAt, updatedAt = t.updatedAt, + onboardedAt = t.onboardedAt, mails = t.mails.map(_.toApi) ) def toManagement: Management.Tenant = Management.Tenant( @@ -32,6 +33,7 @@ object ReadModelTenantAdapters extends SprayJsonSupport with DefaultJsonProtocol attributes = t.attributes.map(_.toManagement), createdAt = t.createdAt, updatedAt = t.updatedAt, + onboardedAt = t.onboardedAt, mails = t.mails.map(_.toManagement) ) } @@ -50,10 +52,12 @@ object ReadModelTenantAdapters extends SprayJsonSupport with DefaultJsonProtocol implicit class PersistentMailKindWrapper(private val k: PersistentTenantMailKind) extends AnyVal { def toApi: MailKind = k match { - case PersistentTenantMailKind.ContactEmail => MailKind.CONTACT_EMAIL + case PersistentTenantMailKind.ContactEmail => MailKind.CONTACT_EMAIL + case PersistentTenantMailKind.DigitalAddress => MailKind.DIGITAL_ADDRESS } def toManagement: Management.MailKind = k match { - case PersistentTenantMailKind.ContactEmail => Management.MailKind.CONTACT_EMAIL + case PersistentTenantMailKind.ContactEmail => Management.MailKind.CONTACT_EMAIL + case PersistentTenantMailKind.DigitalAddress => Management.MailKind.DIGITAL_ADDRESS } } diff --git a/src/main/scala/it/pagopa/interop/tenantprocess/api/adapters/TenantManagementAdapters.scala b/src/main/scala/it/pagopa/interop/tenantprocess/api/adapters/TenantManagementAdapters.scala index c4ef426..e7bcf87 100644 --- a/src/main/scala/it/pagopa/interop/tenantprocess/api/adapters/TenantManagementAdapters.scala +++ b/src/main/scala/it/pagopa/interop/tenantprocess/api/adapters/TenantManagementAdapters.scala @@ -34,7 +34,8 @@ object TenantManagementAdapters extends SprayJsonSupport with DefaultJsonProtoco updatedAt = t.updatedAt, mails = t.mails.map(_.toApi), name = t.name, - kind = t.kind.map(_.toApi) + kind = t.kind.map(_.toApi), + onboardedAt = t.onboardedAt ) } @@ -53,7 +54,8 @@ object TenantManagementAdapters extends SprayJsonSupport with DefaultJsonProtoco implicit class DependencyMailKindWrapper(private val k: DependencyMailKind) extends AnyVal { def toApi: MailKind = k match { - case DependencyMailKind.CONTACT_EMAIL => MailKind.CONTACT_EMAIL + case DependencyMailKind.CONTACT_EMAIL => MailKind.CONTACT_EMAIL + case DependencyMailKind.DIGITAL_ADDRESS => MailKind.DIGITAL_ADDRESS } } diff --git a/src/main/scala/it/pagopa/interop/tenantprocess/api/impl/TenantApiMarshallerImpl.scala b/src/main/scala/it/pagopa/interop/tenantprocess/api/impl/TenantApiMarshallerImpl.scala index 9082004..eb39a40 100644 --- a/src/main/scala/it/pagopa/interop/tenantprocess/api/impl/TenantApiMarshallerImpl.scala +++ b/src/main/scala/it/pagopa/interop/tenantprocess/api/impl/TenantApiMarshallerImpl.scala @@ -39,4 +39,6 @@ object TenantApiMarshallerImpl extends TenantApiMarshaller with SprayJsonSupport override implicit def fromEntityUnmarshallerMailSeed: FromEntityUnmarshaller[MailSeed] = sprayJsonUnmarshaller[MailSeed] + override implicit def toEntityMarshallerResourceId: ToEntityMarshaller[ResourceId] = + sprayJsonMarshaller[ResourceId] } diff --git a/src/main/scala/it/pagopa/interop/tenantprocess/api/impl/TenantApiServiceImpl.scala b/src/main/scala/it/pagopa/interop/tenantprocess/api/impl/TenantApiServiceImpl.scala index 15c6c0f..7c6ecf7 100644 --- a/src/main/scala/it/pagopa/interop/tenantprocess/api/impl/TenantApiServiceImpl.scala +++ b/src/main/scala/it/pagopa/interop/tenantprocess/api/impl/TenantApiServiceImpl.scala @@ -207,8 +207,8 @@ final case class TenantApiServiceImpl( override def selfcareUpsertTenant(seed: SelfcareTenantSeed)(implicit contexts: Seq[(String, String)], - toEntityMarshallerProblem: ToEntityMarshaller[Problem], - toEntityMarshallerTenant: ToEntityMarshaller[Tenant] + toEntityMarshallerTenant: ToEntityMarshaller[ResourceId], + toEntityMarshallerProblem: ToEntityMarshaller[Problem] ): Route = authorize(ADMIN_ROLE, API_ROLE, SECURITY_ROLE, INTERNAL_ROLE) { val operationLabel = s"Creating tenant with external id ${seed.externalId} via SelfCare request" logger.info(operationLabel) @@ -229,17 +229,20 @@ final case class TenantApiServiceImpl( tenant.selfcareId.fold(updateTenant())(verifyConflict) } - val result: Future[Tenant] = for { + val result: Future[ResourceId] = for { existingTenant <- findTenant(seed.externalId) _ <- existingTenant.traverse(t => assertResourceAllowed(t.id)) tenant <- existingTenant .fold(createTenant(seed, Nil, now, getTenantKind(Nil, seed.externalId).fromAPI))(Future.successful) tenantKind <- getTenantKindLoadingCertifiedAttributes(tenant.attributes, tenant.externalId) - updatedTenant <- updateSelfcareId(tenant, tenantKind) - } yield updatedTenant.toApi + _ <- updateSelfcareId(tenant, tenantKind) + _ <- seed.digitalAddress.traverse(digitaAddress => + tenantManagementService.addTenantMail(tenant.id, digitaAddress.toDependency) + ) + } yield ResourceId(tenant.id) onComplete(result) { - selfcareUpsertTenantResponse[Tenant](operationLabel)(selfcareUpsertTenant200) + selfcareUpsertTenantResponse[ResourceId](operationLabel)(selfcareUpsertTenant200) } } diff --git a/src/main/scala/it/pagopa/interop/tenantprocess/api/impl/package.scala b/src/main/scala/it/pagopa/interop/tenantprocess/api/impl/package.scala index 4de8bd2..850fdf2 100644 --- a/src/main/scala/it/pagopa/interop/tenantprocess/api/impl/package.scala +++ b/src/main/scala/it/pagopa/interop/tenantprocess/api/impl/package.scala @@ -19,7 +19,7 @@ package object impl extends SprayJsonSupport with DefaultJsonProtocol { implicit def m2mAttributeSeedFormat: RootJsonFormat[M2MAttributeSeed] = jsonFormat1(M2MAttributeSeed) implicit def m2mTenantSeedFormat: RootJsonFormat[M2MTenantSeed] = jsonFormat3(M2MTenantSeed) - implicit def selfcareTenantSeedFormat: RootJsonFormat[SelfcareTenantSeed] = jsonFormat3(SelfcareTenantSeed) + implicit def selfcareTenantSeedFormat: RootJsonFormat[SelfcareTenantSeed] = jsonFormat5(SelfcareTenantSeed) implicit def certifierFormat: RootJsonFormat[Certifier] = jsonFormat1(Certifier) implicit def tenantFeatureFormat: RootJsonFormat[TenantFeature] = jsonFormat1(TenantFeature) @@ -42,7 +42,8 @@ package object impl extends SprayJsonSupport with DefaultJsonProtocol { implicit def tenantVerifierFormat: RootJsonFormat[TenantVerifier] = jsonFormat4(TenantVerifier) implicit def tenantRevokerFormat: RootJsonFormat[TenantRevoker] = jsonFormat5(TenantRevoker) - implicit def tenantFormat: RootJsonFormat[Tenant] = jsonFormat10(Tenant) + implicit def resourceIdFormat: RootJsonFormat[ResourceId] = jsonFormat1(ResourceId) + implicit def tenantFormat: RootJsonFormat[Tenant] = jsonFormat11(Tenant) implicit def tenantsFormat: RootJsonFormat[Tenants] = jsonFormat2(Tenants) implicit def problemErrorFormat: RootJsonFormat[ProblemError] = jsonFormat2(ProblemError) implicit def problemFormat: RootJsonFormat[Problem] = jsonFormat6(Problem) diff --git a/src/test/scala/it/pagopa/interop/tenantprocess/provider/TenantCreationSpec.scala b/src/test/scala/it/pagopa/interop/tenantprocess/provider/TenantCreationSpec.scala index 4c800ff..ceb7cbf 100644 --- a/src/test/scala/it/pagopa/interop/tenantprocess/provider/TenantCreationSpec.scala +++ b/src/test/scala/it/pagopa/interop/tenantprocess/provider/TenantCreationSpec.scala @@ -898,7 +898,8 @@ class TenantCreationSpec extends AnyWordSpecLike with SpecHelper with ScalatestR features = Nil, attributes = Nil, name = "test_name", - kind = TenantKind.PA + kind = TenantKind.PA, + onboardedAt = Some(timestamp) ) val expectedTenantUpdate = TenantDelta(selfcareId = Some(seed.selfcareId), features = Nil, kind = TenantKind.PA) @@ -929,7 +930,8 @@ class TenantCreationSpec extends AnyWordSpecLike with SpecHelper with ScalatestR features = Nil, attributes = Nil, name = "test_name", - kind = TenantKind.PRIVATE + kind = TenantKind.PRIVATE, + onboardedAt = Some(timestamp) ) val expectedTenantUpdate = TenantDelta(selfcareId = Some(seed.selfcareId), features = Nil, kind = TenantKind.PRIVATE) @@ -947,7 +949,7 @@ class TenantCreationSpec extends AnyWordSpecLike with SpecHelper with ScalatestR } } - "SelfCare request - Update of an existing tenant must succeed if SelfCare ID is not set" in { + "SelfCare request - Update of an existing tenant must succeed if SelfCare ID is not set without mail seed" in { implicit val context: Seq[(String, String)] = selfcareContext val tenantId = organizationId @@ -975,6 +977,36 @@ class TenantCreationSpec extends AnyWordSpecLike with SpecHelper with ScalatestR } } + "SelfCare request - Update of an existing tenant must succeed if SelfCare ID is not set but with digitalAddress" in { + implicit val context: Seq[(String, String)] = selfcareContext + + val tenantId = organizationId + val seed = selfcareTenantSeed.copy(digitalAddress = Some(mailSeed)) + val tenant = persistentTenant.copy( + id = tenantId, + selfcareId = None, + features = List(PersistentTenantFeature.PersistentCertifier("something")) + ) + + val expectedTenantUpdate = + TenantDelta( + selfcareId = Some(seed.selfcareId), + features = Seq(TenantFeature(certifier = Some(Certifier("something")))), + kind = TenantKind.PA + ) + + mockDateTimeGet() + + mockGetTenantByExternalId(PersistentExternalId(seed.externalId.origin, seed.externalId.value), tenant) + mockUpdateTenant(tenantId, expectedTenantUpdate) + + mockAddTenantMail(tenantId, dependencyMailSeed) + + Get() ~> tenantService.selfcareUpsertTenant(seed) ~> check { + assert(status == StatusCodes.OK) + } + } + "SelfCare request - Update should not be performed if existing SelfCare ID is equal to the request" in { implicit val context: Seq[(String, String)] = selfcareContext diff --git a/src/test/scala/it/pagopa/interop/tenantprocess/utils/FakeDependencies.scala b/src/test/scala/it/pagopa/interop/tenantprocess/utils/FakeDependencies.scala index a4f02f0..c7f80c7 100644 --- a/src/test/scala/it/pagopa/interop/tenantprocess/utils/FakeDependencies.scala +++ b/src/test/scala/it/pagopa/interop/tenantprocess/utils/FakeDependencies.scala @@ -196,7 +196,8 @@ object FakeDependencies extends SpecData { updatedAt = None, mails = Nil, name = "test_name", - kind = Some(PersistentTenantKind.PA) + kind = Some(PersistentTenantKind.PA), + onboardedAt = None ) val fakeAttribute: PersistentCertifiedAttribute = diff --git a/src/test/scala/it/pagopa/interop/tenantprocess/utils/SpecData.scala b/src/test/scala/it/pagopa/interop/tenantprocess/utils/SpecData.scala index fe5cd85..e0147d6 100644 --- a/src/test/scala/it/pagopa/interop/tenantprocess/utils/SpecData.scala +++ b/src/test/scala/it/pagopa/interop/tenantprocess/utils/SpecData.scala @@ -31,6 +31,7 @@ import it.pagopa.interop.catalogmanagement.model.{ Rest, Deliver } +import it.pagopa.interop.commons.utils.Digester.toSha256 import it.pagopa.interop.commons.utils.service.OffsetDateTimeSupplier import java.time.{OffsetDateTime, ZoneOffset} import java.util.UUID @@ -40,7 +41,13 @@ trait SpecData { val tenantId = UUID.randomUUID() val internalAttributeSeed: InternalAttributeSeed = InternalAttributeSeed("IPA", s"int-attribute-${UUID.randomUUID()}") val m2mAttributeSeed: M2MAttributeSeed = M2MAttributeSeed(s"m2m-attribute-${UUID.randomUUID()}") - + val mailSeed: MailSeed = MailSeed(MailKind.DIGITAL_ADDRESS, address = "foo@bar.it", None) + val dependencyMailSeed: Dependency.MailSeed = Dependency.MailSeed( + toSha256("foo@bar.it".getBytes()), + Dependency.MailKind.DIGITAL_ADDRESS, + address = "foo@bar.it", + None + ) val internalTenantSeed: InternalTenantSeed = InternalTenantSeed(ExternalId("IPA", s"tenant-${UUID.randomUUID()}"), Seq(internalAttributeSeed), "test_name") val internalTenantSeedNotIpa: InternalTenantSeed = @@ -50,9 +57,21 @@ trait SpecData { val m2mTenantSeedNotIpa: M2MTenantSeed = M2MTenantSeed(ExternalId("NOT_IPA", s"tenant-${UUID.randomUUID()}"), Seq(m2mAttributeSeed), "test_name") val selfcareTenantSeed: SelfcareTenantSeed = - SelfcareTenantSeed(ExternalId("IPA", s"tenant-${UUID.randomUUID()}"), UUID.randomUUID().toString, "test_name") + SelfcareTenantSeed( + ExternalId("IPA", s"tenant-${UUID.randomUUID()}"), + UUID.randomUUID().toString, + "test_name", + None, + timestamp + ) val selfcareTenantSeedNotIpa: SelfcareTenantSeed = - SelfcareTenantSeed(ExternalId("NOT_IPA", s"tenant-${UUID.randomUUID()}"), UUID.randomUUID().toString, "test_name") + SelfcareTenantSeed( + ExternalId("NOT_IPA", s"tenant-${UUID.randomUUID()}"), + UUID.randomUUID().toString, + "test_name", + None, + timestamp + ) val dependencyTenant: Dependency.Tenant = Dependency.Tenant( id = tenantId, @@ -64,7 +83,8 @@ trait SpecData { updatedAt = None, mails = Nil, name = "test_name", - kind = None + kind = None, + onboardedAt = Some(timestamp) ) val persistentAttribute: PersistentAttribute = PersistentAttribute( @@ -104,7 +124,8 @@ trait SpecData { updatedAt = None, mails = Nil, name = "test_name", - kind = None + kind = None, + onboardedAt = Some(timestamp) ) val persistentTenantNotIPA: PersistentTenant = PersistentTenant( @@ -117,7 +138,8 @@ trait SpecData { updatedAt = None, mails = Nil, name = "test_name", - kind = None + kind = None, + onboardedAt = Some(timestamp) ) val fakeMailSeed = MailSeed(MailKind.CONTACT_EMAIL, address = "fakeAddress", description = None)