Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PIN-4414 Archive older descriptor on publish if there are no other agreements #223

Merged
merged 6 commits into from
Jan 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading