Skip to content

Commit

Permalink
PIN-4414 Archive older descriptor on publish if there are no other ag…
Browse files Browse the repository at this point in the history
…reements (#223)
  • Loading branch information
nttdata-rtorsoli authored Jan 29, 2024
1 parent 4a4dd5a commit 8785ab9
Show file tree
Hide file tree
Showing 6 changed files with 238 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,14 @@ import it.pagopa.interop.commons.utils.AkkaUtils._
import it.pagopa.interop.commons.utils.OpenapiUtils.parseArrayParameters
import it.pagopa.interop.commons.utils.TypeConversions._
import it.pagopa.interop.commons.utils.errors.{ComponentError, GenericComponentErrors}
import it.pagopa.interop.catalogmanagement.model.CatalogItemMode
import it.pagopa.interop.agreementmanagement.model.agreement.{
Active => AgreementActive,
Suspended => AgreementSuspended
}

import java.util.UUID
import scala.concurrent.{ExecutionContext, Future}
import it.pagopa.interop.catalogmanagement.model.CatalogItemMode

final case class ProcessApiServiceImpl(
catalogManagementService: CatalogManagementService,
Expand Down Expand Up @@ -178,6 +182,7 @@ final case class ProcessApiServiceImpl(
eServicesIds = eServicesIds,
producersIds = Nil,
consumersIds = Seq(organizationId),
descriptorsIds = Nil,
states = agreementStates
)
.map(_.map(_.eserviceId))
Expand Down Expand Up @@ -235,6 +240,32 @@ final case class ProcessApiServiceImpl(
val operationLabel = s"Publishing descriptor $descriptorId for EService $eServiceId"
logger.info(operationLabel)

def changeStateOfOldDescriptorOrCancelPublication(
eServiceId: UUID,
oldDescriptorId: UUID,
descriptorId: UUID,
validStates: Seq[PersistentAgreementState]
): Future[Unit] = for {
validAgreements <- agreementManagementService.getAgreements(
List(eServiceId),
Nil,
Nil,
List(oldDescriptorId),
validStates
)
_ <- validAgreements.headOption match {
case Some(_) =>
deprecateDescriptor(oldDescriptorId.toString, eServiceId.toString).recoverWith(error =>
resetDescriptorToDraft(eServiceId.toString, descriptorId.toString).flatMap(_ => Future.failed(error))
)
case None =>
catalogManagementService.archiveDescriptor(eServiceId.toString, oldDescriptorId.toString) recoverWith (
error =>
resetDescriptorToDraft(eServiceId.toString, descriptorId.toString).flatMap(_ => Future.failed(error))
)
}
} yield ()

def verifyRiskAnalysisForPublication(catalogItem: CatalogItem): Future[Unit] = catalogItem.mode match {
case Deliver => Future.unit
case Receive =>
Expand Down Expand Up @@ -265,10 +296,11 @@ final case class ProcessApiServiceImpl(
_ <- catalogManagementService.publishDescriptor(eServiceId, descriptorId)
_ <- currentActiveDescriptor
.map(oldDescriptor =>
deprecateDescriptorOrCancelPublication(
eServiceId = eServiceId,
descriptorIdToDeprecate = oldDescriptor.id.toString,
descriptorIdToCancel = descriptorId
changeStateOfOldDescriptorOrCancelPublication(
eServiceId = eServiceUuid,
oldDescriptorId = oldDescriptor.id,
descriptorId = descriptorUuid,
validStates = List(AgreementActive, AgreementSuspended)
)
)
.sequence
Expand Down Expand Up @@ -467,13 +499,6 @@ final case class ProcessApiServiceImpl(
EServiceCannotBeUpdated(eService.id.toString)
)

private[this] def deprecateDescriptorOrCancelPublication(
eServiceId: String,
descriptorIdToDeprecate: String,
descriptorIdToCancel: String
)(implicit contexts: Seq[(String, String)]): Future[Unit] = deprecateDescriptor(descriptorIdToDeprecate, eServiceId)
.recoverWith(error => resetDescriptorToDraft(eServiceId, descriptorIdToCancel).flatMap(_ => Future.failed(error)))

private[this] def deprecateDescriptor(descriptorId: String, eServiceId: String)(implicit
contexts: Seq[(String, String)]
): Future[Unit] = catalogManagementService
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@ object ReadModelAgreementQueries extends ReadModelQuery {
eServicesIds: Seq[UUID],
consumersIds: Seq[UUID],
producersIds: Seq[UUID],
descriptorsIds: Seq[UUID],
states: Seq[PersistentAgreementState],
offset: Int,
limit: Int
)(implicit ec: ExecutionContext, readModel: ReadModelService): Future[Seq[PersistentAgreement]] = {

val query: Bson =
getAgreementsFilters(eServicesIds, consumersIds, producersIds, states)
getAgreementsFilters(eServicesIds, consumersIds, producersIds, descriptorsIds, states)

for {
agreements <- readModel.aggregate[PersistentAgreement](
Expand All @@ -40,20 +41,23 @@ object ReadModelAgreementQueries extends ReadModelQuery {
eServicesIds: Seq[UUID],
consumersIds: Seq[UUID],
producersIds: Seq[UUID],
descriptorsIds: Seq[UUID],
states: Seq[PersistentAgreementState]
): Bson = {

val statesFilter = listStatesFilter(states)

val eServicesIdsFilter =
val eServicesIdsFilter =
mapToVarArgs(eServicesIds.map(id => Filters.eq("data.eserviceId", id.toString)))(Filters.or)
val consumersIdsFilter =
val consumersIdsFilter =
mapToVarArgs(consumersIds.map(id => Filters.eq("data.consumerId", id.toString)))(Filters.or)
val producersIdsFilter =
val producersIdsFilter =
mapToVarArgs(producersIds.map(id => Filters.eq("data.producerId", id.toString)))(Filters.or)
val descriptorsIdsFilter =
mapToVarArgs(descriptorsIds.map(id => Filters.eq("data.descriptorId", id.toString)))(Filters.or)

mapToVarArgs(
eServicesIdsFilter.toList ++ consumersIdsFilter.toList ++ producersIdsFilter.toList ++ statesFilter.toList
eServicesIdsFilter.toList ++ consumersIdsFilter.toList ++ producersIdsFilter.toList ++ descriptorsIdsFilter.toList ++ statesFilter.toList
)(Filters.and).getOrElse(Filters.empty())
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ trait AgreementManagementService {
eServicesIds: Seq[UUID],
consumersIds: Seq[UUID],
producersIds: Seq[UUID],
descriptorsIds: Seq[UUID],
states: Seq[PersistentAgreementState]
)(implicit ec: ExecutionContext, readModel: ReadModelService): Future[Seq[PersistentAgreement]]
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,35 @@ object AgreementManagementServiceImpl extends AgreementManagementService {
eServicesIds: Seq[UUID],
consumersIds: Seq[UUID],
producersIds: Seq[UUID],
descriptorsIds: Seq[UUID],
states: Seq[PersistentAgreementState]
)(implicit ec: ExecutionContext, readModel: ReadModelService): Future[Seq[PersistentAgreement]] =
getAllAgreements(eServicesIds, consumersIds, producersIds, states)
getAllAgreements(eServicesIds, consumersIds, producersIds, descriptorsIds, states)

private def getAgreements(
eServicesIds: Seq[UUID],
consumersIds: Seq[UUID],
producersIds: Seq[UUID],
descriptorsIds: Seq[UUID],
states: Seq[PersistentAgreementState],
offset: Int,
limit: Int
)(implicit ec: ExecutionContext, readModel: ReadModelService): Future[Seq[PersistentAgreement]] =
ReadModelAgreementQueries.getAgreements(eServicesIds, consumersIds, producersIds, states, offset, limit)
ReadModelAgreementQueries.getAgreements(
eServicesIds,
consumersIds,
producersIds,
descriptorsIds,
states,
offset,
limit
)

private def getAllAgreements(
eServicesIds: Seq[UUID],
consumersIds: Seq[UUID],
producersIds: Seq[UUID],
descriptorsIds: Seq[UUID],
states: Seq[PersistentAgreementState]
)(implicit ec: ExecutionContext, readModel: ReadModelService): Future[Seq[PersistentAgreement]] = {

Expand All @@ -40,6 +51,7 @@ object AgreementManagementServiceImpl extends AgreementManagementService {
eServicesIds = eServicesIds,
consumersIds = consumersIds,
producersIds = producersIds,
descriptorsIds = descriptorsIds,
states = states,
offset = offset,
limit = 50
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ package it.pagopa.interop.catalogprocess

import akka.http.scaladsl.model.StatusCodes
import akka.http.scaladsl.testkit.ScalatestRouteTest
import it.pagopa.interop.agreementmanagement.model.agreement.{Active, PersistentAgreementState}
import it.pagopa.interop.agreementmanagement.model.agreement.{
Active => AgreementActive,
Suspended => AgreementSuspended,
PersistentAgreementState
}
import it.pagopa.interop.authorizationmanagement.client.{model => AuthorizationManagementDependency}
import it.pagopa.interop.catalogmanagement.client.model.AgreementApprovalPolicy.AUTOMATIC
import it.pagopa.interop.catalogmanagement.client.{model => CatalogManagementDependency}
Expand Down Expand Up @@ -135,11 +139,11 @@ class CatalogProcessSpec extends SpecHelper with AnyWordSpecLike with ScalatestR
val limit = 50

(mockAgreementManagementService
.getAgreements(_: Seq[UUID], _: Seq[UUID], _: Seq[UUID], _: Seq[PersistentAgreementState])(
.getAgreements(_: Seq[UUID], _: Seq[UUID], _: Seq[UUID], _: Seq[UUID], _: Seq[PersistentAgreementState])(
_: ExecutionContext,
_: ReadModelService
))
.expects(eServicesIds, Seq(requesterId), producersIds, Seq(Active), *, *)
.expects(eServicesIds, Seq(requesterId), producersIds, Nil, Seq(AgreementActive), *, *)
.once()
.returns(Future.successful(Seq(SpecData.agreement)))

Expand Down Expand Up @@ -204,11 +208,11 @@ class CatalogProcessSpec extends SpecHelper with AnyWordSpecLike with ScalatestR
val limit = 50

(mockAgreementManagementService
.getAgreements(_: Seq[UUID], _: Seq[UUID], _: Seq[UUID], _: Seq[PersistentAgreementState])(
.getAgreements(_: Seq[UUID], _: Seq[UUID], _: Seq[UUID], _: Seq[UUID], _: Seq[PersistentAgreementState])(
_: ExecutionContext,
_: ReadModelService
))
.expects(eServicesIds, Seq(requesterId), producersIds, Seq(Active), *, *)
.expects(eServicesIds, Seq(requesterId), producersIds, Nil, Seq(AgreementActive), *, *)
.once()
.returns(Future.successful(Seq.empty))

Expand Down Expand Up @@ -1409,6 +1413,173 @@ class CatalogProcessSpec extends SpecHelper with AnyWordSpecLike with ScalatestR
status shouldEqual StatusCodes.NoContent
}
}

"succeed if descriptor is Draft and archive the previous one" in {
val requesterId = UUID.randomUUID()
val descriptorId1 = UUID.randomUUID()
val descriptorId2 = UUID.randomUUID()

implicit val context: Seq[(String, String)] =
Seq("bearer" -> bearerToken, USER_ROLES -> "admin", ORGANIZATION_ID_CLAIM -> requesterId.toString)

(mockCatalogManagementService
.getEServiceById(_: UUID)(_: ExecutionContext, _: ReadModelService))
.expects(SpecData.catalogItem.id, *, *)
.once()
.returns(
Future.successful(
SpecData.catalogItem.copy(
producerId = requesterId,
descriptors = Seq(
SpecData.catalogDescriptor
.copy(
id = descriptorId1,
state = Published,
interface = Option(SpecData.catalogDocument),
version = "2"
),
SpecData.catalogDescriptor
.copy(id = descriptorId2, state = Draft, interface = Option(SpecData.catalogDocument))
)
)
)
)

(mockAgreementManagementService
.getAgreements(_: Seq[UUID], _: Seq[UUID], _: Seq[UUID], _: Seq[UUID], _: Seq[PersistentAgreementState])(
_: ExecutionContext,
_: ReadModelService
))
.expects(
Seq(SpecData.catalogItem.id),
Nil,
Nil,
List(descriptorId1),
Seq(AgreementActive, AgreementSuspended),
*,
*
)
.once()
.returns(Future.successful(Seq.empty))

(mockCatalogManagementService
.archiveDescriptor(_: String, _: String)(_: Seq[(String, String)]))
.expects(SpecData.catalogItem.id.toString, descriptorId1.toString, *)
.returning(Future.unit)
.once()

(mockCatalogManagementService
.publishDescriptor(_: String, _: String)(_: Seq[(String, String)]))
.expects(SpecData.catalogItem.id.toString, descriptorId2.toString, *)
.returning(Future.unit)
.once()

(mockAuthorizationManagementService
.updateStateOnClients(
_: UUID,
_: UUID,
_: AuthorizationManagementDependency.ClientComponentState,
_: Seq[String],
_: Int
)(_: Seq[(String, String)]))
.expects(
SpecData.catalogItem.id,
descriptorId2,
AuthorizationManagementDependency.ClientComponentState.ACTIVE,
SpecData.catalogDescriptor.audience,
SpecData.catalogDescriptor.voucherLifespan,
*
)
.returning(Future.unit)
.once()

Post() ~> service.publishDescriptor(SpecData.catalogItem.id.toString, descriptorId2.toString) ~> check {
status shouldEqual StatusCodes.NoContent
}
}
"succeed if descriptor is Draft and deprecate the previous one" in {
val requesterId = UUID.randomUUID()
val descriptorId1 = UUID.randomUUID()
val descriptorId2 = UUID.randomUUID()

implicit val context: Seq[(String, String)] =
Seq("bearer" -> bearerToken, USER_ROLES -> "admin", ORGANIZATION_ID_CLAIM -> requesterId.toString)

(mockCatalogManagementService
.getEServiceById(_: UUID)(_: ExecutionContext, _: ReadModelService))
.expects(SpecData.catalogItem.id, *, *)
.once()
.returns(
Future.successful(
SpecData.catalogItem.copy(
producerId = requesterId,
descriptors = Seq(
SpecData.catalogDescriptor
.copy(
id = descriptorId1,
state = Published,
interface = Option(SpecData.catalogDocument),
version = "2"
),
SpecData.catalogDescriptor
.copy(id = descriptorId2, state = Draft, interface = Option(SpecData.catalogDocument))
)
)
)
)

(mockAgreementManagementService
.getAgreements(_: Seq[UUID], _: Seq[UUID], _: Seq[UUID], _: Seq[UUID], _: Seq[PersistentAgreementState])(
_: ExecutionContext,
_: ReadModelService
))
.expects(
Seq(SpecData.catalogItem.id),
Nil,
Nil,
List(descriptorId1),
Seq(AgreementActive, AgreementSuspended),
*,
*
)
.once()
.returns(Future.successful(Seq(SpecData.agreement)))

(mockCatalogManagementService
.deprecateDescriptor(_: String, _: String)(_: Seq[(String, String)]))
.expects(SpecData.catalogItem.id.toString, descriptorId1.toString, *)
.returning(Future.unit)
.once()

(mockCatalogManagementService
.publishDescriptor(_: String, _: String)(_: Seq[(String, String)]))
.expects(SpecData.catalogItem.id.toString, descriptorId2.toString, *)
.returning(Future.unit)
.once()

(mockAuthorizationManagementService
.updateStateOnClients(
_: UUID,
_: UUID,
_: AuthorizationManagementDependency.ClientComponentState,
_: Seq[String],
_: Int
)(_: Seq[(String, String)]))
.expects(
SpecData.catalogItem.id,
descriptorId2,
AuthorizationManagementDependency.ClientComponentState.ACTIVE,
SpecData.catalogDescriptor.audience,
SpecData.catalogDescriptor.voucherLifespan,
*
)
.returning(Future.unit)
.once()

Post() ~> service.publishDescriptor(SpecData.catalogItem.id.toString, descriptorId2.toString) ~> check {
status shouldEqual StatusCodes.NoContent
}
}
"fail if descriptor has not interface" in {
val requesterId = UUID.randomUUID()

Expand Down
Loading

0 comments on commit 8785ab9

Please sign in to comment.