diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml index 0b010253..cc3f5305 100644 --- a/.github/workflows/trivy.yml +++ b/.github/workflows/trivy.yml @@ -7,7 +7,7 @@ name: trivy on: push: - branches: ["CHANGEME"] + branches: [ "main" ] # pull_request: # # The branches below must be a subset of the branches above # branches: [ "main" ] @@ -50,7 +50,7 @@ jobs: with: context: . builder: ${{ steps.buildx.outputs.name }} - file: infra/docker/app/Dockerfile + file: infra/docker/app/DockerfileCI push: true tags: | ghcr.io/mtes-mct/rapportnav2/rapportnav-app:${{ github.sha }} diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 154a9bcd..00be0ff1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -11,6 +11,9 @@ variables: PROJECT_NAME: value: rapportnav-v2 description: "Nom du projet à déployer" + BDD_IAMGE: + value: postgres:15-bookworm + description: "Image de la base de données" PROJECT_VERSION: value: "0.0.1-alpha.10" description: "Version du projet à déployer" diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/repositories/mission/infraction/IInfractionEnvTargetRepository.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/repositories/mission/infraction/IInfractionEnvTargetRepository.kt new file mode 100644 index 00000000..12d03df7 --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/repositories/mission/infraction/IInfractionEnvTargetRepository.kt @@ -0,0 +1,11 @@ +package fr.gouv.dgampa.rapportnav.domain.repositories.mission.infraction + +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.infraction.InfractionEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.infraction.InfractionEnvTargetEntity +import fr.gouv.dgampa.rapportnav.infrastructure.database.model.mission.infraction.InfractionEnvTargetModel + +interface IInfractionEnvTargetRepository { + fun save(infractionTarget: InfractionEnvTargetEntity, infraction: InfractionEntity): InfractionEnvTargetModel + + fun findByActionIdAndVesselIdentifier(actionId: String, vesselIdentifier: String): List +} diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/infraction/AddOrUpdateInfractionEnvTarget.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/infraction/AddOrUpdateInfractionEnvTarget.kt new file mode 100644 index 00000000..55322321 --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/infraction/AddOrUpdateInfractionEnvTarget.kt @@ -0,0 +1,16 @@ +package fr.gouv.dgampa.rapportnav.domain.use_cases.mission.infraction + +import fr.gouv.dgampa.rapportnav.config.UseCase +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.infraction.InfractionEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.infraction.InfractionEnvTargetEntity +import fr.gouv.dgampa.rapportnav.domain.repositories.mission.infraction.IInfractionEnvTargetRepository + +@UseCase +class AddOrUpdateInfractionEnvTarget( + private val repo: IInfractionEnvTargetRepository, +) { + fun execute(infractionTarget: InfractionEnvTargetEntity, infraction: InfractionEntity): InfractionEnvTargetEntity { + val savedData = repo.save(infractionTarget, infraction).toInfractionEnvTargetEntity() + return savedData + } +} diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/infraction/GetInfractionById.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/infraction/GetInfractionById.kt index 4af51b98..369f6e1e 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/infraction/GetInfractionById.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/infraction/GetInfractionById.kt @@ -8,7 +8,11 @@ import java.util.* @UseCase class GetInfractionById(private val repo: IInfractionRepository) { fun execute(id: UUID): InfractionEntity? { - val optionalInfractionModel = repo.findById(id) - return optionalInfractionModel.map { it.toInfractionEntity() }.orElse(null) + val infractionModel = repo.findById(id) + if (infractionModel.isPresent) { + return infractionModel.get().toInfractionEntity() + } else { + return null + } } } diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/infraction/IsControlledAllowedForTarget.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/infraction/IsControlledAllowedForTarget.kt new file mode 100644 index 00000000..2e59ebe6 --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/infraction/IsControlledAllowedForTarget.kt @@ -0,0 +1,20 @@ +package fr.gouv.dgampa.rapportnav.domain.use_cases.mission.infraction + +import fr.gouv.dgampa.rapportnav.config.UseCase +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.control.ControlType +import fr.gouv.dgampa.rapportnav.domain.repositories.mission.infraction.IInfractionEnvTargetRepository + +@UseCase +class IsControlledAllowedForTarget( + private val repo: IInfractionEnvTargetRepository, + private val getInfractionById: GetInfractionById, +) { + fun execute(actionId: String, vesselIdentifier: String, controlType: ControlType): Boolean { + val targets = repo.findByActionIdAndVesselIdentifier(actionId = actionId, vesselIdentifier = vesselIdentifier) + val controlledAllowed = targets.map { + val infraction = it.infraction?.toInfractionEntity() + return controlType != infraction?.controlType + } + return controlledAllowed.all { true } + } +} diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/ActionEnvController.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/ActionEnvController.kt index e3b7b7a9..e9da99b7 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/ActionEnvController.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/ActionEnvController.kt @@ -86,14 +86,14 @@ class ActionEnvController( infractionsByVessel = infractionsByVessel.map { byVessel -> // a target reported by monitorEnv will have an infraction with controlType null // the next field is to help the frontend hide/show information according to whom has reported the target - val targetAddedInRapportNav = !byVessel.infractions.any { it.controlType == null } + val targetAddedByUnit = !byVessel.infractions.any { it.controlType == null } // the next field is to help the frontend show different options for which control type to report an infraction val reportedControlTypes = byVessel.infractions.mapNotNull { it.controlType } val updatedByVessel = byVessel.copy( controlTypesWithInfraction = reportedControlTypes, - targetAddedInRapportNav = targetAddedInRapportNav + targetAddedByUnit = targetAddedByUnit ) updatedByVessel } diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/InfractionController.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/InfractionController.kt index dc0899ec..54ecc5d9 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/InfractionController.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/InfractionController.kt @@ -1,14 +1,15 @@ package fr.gouv.dgampa.rapportnav.infrastructure.bff +import fr.gouv.dgampa.rapportnav.domain.entities.mission.env.envActions.VesselSizeEnum +import fr.gouv.dgampa.rapportnav.domain.entities.mission.env.envActions.VesselTypeEnum +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.control.ControlType import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.infraction.InfractionEnvTargetEntity -import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.infraction.AddOrUpdateInfraction -import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.infraction.DeleteInfraction -import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.infraction.GetInfractionById -import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.infraction.GetNatinfs +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.infraction.* import fr.gouv.dgampa.rapportnav.infrastructure.bff.adapters.infraction.InfractionInput import fr.gouv.dgampa.rapportnav.infrastructure.bff.adapters.infraction.InfractionWithNewTargetInput import fr.gouv.dgampa.rapportnav.infrastructure.bff.model.infraction.Infraction import fr.gouv.dgampa.rapportnav.infrastructure.bff.model.infraction.Natinf +import org.slf4j.LoggerFactory import org.springframework.graphql.data.method.annotation.Argument import org.springframework.graphql.data.method.annotation.MutationMapping import org.springframework.graphql.data.method.annotation.QueryMapping @@ -22,8 +23,13 @@ class InfractionController( private val deleteInfraction: DeleteInfraction, private val getInfractionById: GetInfractionById, private val getNatinfs: GetNatinfs, + private val addOrUpdateInfractionEnvTarget: AddOrUpdateInfractionEnvTarget, + private val isControlledAllowedForTarget: IsControlledAllowedForTarget, ) { + private val logger = LoggerFactory.getLogger(InfractionController::class.java) + + @QueryMapping fun natinfs(): List { return getNatinfs.execute().map { Natinf.fromNatinfEntity(it) } @@ -49,22 +55,57 @@ class InfractionController( @MutationMapping fun addOrUpdateInfractionForEnvTarget(@Argument infraction: InfractionWithNewTargetInput): Infraction? { - val target: InfractionEnvTargetEntity? + var target: InfractionEnvTargetEntity? + + + if (infraction.id == null) { + // check if there is not already an infraction with the same controlType for this target + val isAllowed = isControlledAllowedForTarget.execute( + actionId = infraction.actionId, + vesselIdentifier = infraction.vesselIdentifier, + controlType = ControlType.valueOf(infraction.controlType) + ) + if (!isAllowed) { + logger.error("addOrUpdateInfractionForEnvTarget - controlType=${infraction.controlType} already exists for target name=${infraction.vesselIdentifier}") + throw Exception("Ce type de contrôle a déjà été reporté pour cette cible. Veuillez annuler et modifier le contrôle déjà existant.") + } else { + target = null + } + } else { - // get the infraction if it already exists and set its target - if (infraction.id != null) { + // get the infraction if it already exists and set its target val repoInfraction = getInfractionById.execute(id = UUID.fromString(infraction.id)) target = repoInfraction?.target - } else { - target = null - } - // TODO must also verify that there is not already an infraction for this target and this controlType + // Check if target has changed or not by crossing fields between InfractionWithNewTargetInput and InfractionEnvTargetEntity + if (target != null && hasTargetChanged(infraction, target)) { + // Update the target + val newTarget = InfractionEnvTargetEntity( + id = target.id, + missionId = target.missionId, + actionId = target.actionId, + infractionId = target.infractionId, + vesselIdentifier = infraction.vesselIdentifier, + vesselSize = VesselSizeEnum.valueOf(infraction.vesselSize), + vesselType = VesselTypeEnum.valueOf(infraction.vesselType), + identityControlledPerson = infraction.identityControlledPerson + ) + target = addOrUpdateInfractionEnvTarget.execute(newTarget, infraction.toInfractionEntity(newTarget)) + } + } val input = infraction.toInfractionEntity(target) val infractionEntity = addOrUpdateInfraction.execute(input) return Infraction.fromInfractionEntity(infractionEntity) } + // Function to check if the target has changed + private fun hasTargetChanged(input: InfractionWithNewTargetInput, target: InfractionEnvTargetEntity): Boolean { + return input.vesselType != target.vesselType.name || + input.vesselSize != target.vesselSize.name || + input.vesselIdentifier != target.vesselIdentifier || + input.identityControlledPerson != target.identityControlledPerson + } + } diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/model/infraction/InfractionsByVessel.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/model/infraction/InfractionsByVessel.kt index ec42d271..fa06d092 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/model/infraction/InfractionsByVessel.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/model/infraction/InfractionsByVessel.kt @@ -8,7 +8,7 @@ data class InfractionsByVessel( val vesselIdentifier: String? = null, val vesselType: VesselTypeEnum? = null, val controlTypesWithInfraction: List? = null, - val targetAddedInRapportNav: Boolean? = null, + val targetAddedByUnit: Boolean? = null, val infractions: List ) { fun groupInfractionsByVesselIdentifier(infractions: List): List { @@ -20,7 +20,7 @@ data class InfractionsByVessel( vesselType = infractions.firstOrNull()?.target?.vesselType, infractions = infractions, controlTypesWithInfraction = controlTypesWithInfraction, - targetAddedInRapportNav = targetAddedInRapportNav, + targetAddedByUnit = targetAddedByUnit, ) } } diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/database/model/mission/infraction/InfractionEnvTargetModel.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/database/model/mission/infraction/InfractionEnvTargetModel.kt index f6406818..ef01a86a 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/database/model/mission/infraction/InfractionEnvTargetModel.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/database/model/mission/infraction/InfractionEnvTargetModel.kt @@ -3,6 +3,7 @@ package fr.gouv.dgampa.rapportnav.infrastructure.database.model.mission.infracti import com.fasterxml.jackson.annotation.JsonIgnore import fr.gouv.dgampa.rapportnav.domain.entities.mission.env.envActions.VesselSizeEnum import fr.gouv.dgampa.rapportnav.domain.entities.mission.env.envActions.VesselTypeEnum +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.infraction.InfractionEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.infraction.InfractionEnvTargetEntity import jakarta.persistence.* import java.util.* @@ -53,14 +54,19 @@ data class InfractionEnvTargetModel( } companion object { - fun fromInfractionEnvTargetEntity(infraction: InfractionEnvTargetEntity) = InfractionEnvTargetModel( - id = infraction.id, - missionId = infraction.missionId, - actionId = infraction.actionId, - vesselIdentifier = infraction.vesselIdentifier, - identityControlledPerson = infraction.identityControlledPerson, - vesselType = infraction.vesselType.toString(), - vesselSize = infraction.vesselSize.toString(), - ) + fun fromInfractionEnvTargetEntity( + infractionTarget: InfractionEnvTargetEntity, + infraction: InfractionEntity? = null + ) = + InfractionEnvTargetModel( + id = infractionTarget.id, + missionId = infractionTarget.missionId, + actionId = infractionTarget.actionId, + infraction = infraction?.let { it -> InfractionModel.fromInfractionEntity(it) }, + vesselIdentifier = infractionTarget.vesselIdentifier, + identityControlledPerson = infractionTarget.identityControlledPerson, + vesselType = infractionTarget.vesselType.toString(), + vesselSize = infractionTarget.vesselSize.toString(), + ) } } diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/database/model/mission/infraction/InfractionModel.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/database/model/mission/infraction/InfractionModel.kt index d5686bc0..afb38af8 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/database/model/mission/infraction/InfractionModel.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/database/model/mission/infraction/InfractionModel.kt @@ -69,21 +69,20 @@ class InfractionModel( } companion object { - fun fromInfractionEntity(infraction: InfractionEntity) = InfractionModel( - id = infraction.id, - missionId = infraction.missionId, - actionId = infraction.actionId, - controlType = infraction.controlType.toString(), - infractionType = infraction.infractionType?.toString(), - natinfs = infraction.natinfs, - observations = infraction.observations, - target = infraction.target?.let { - listOf( - InfractionEnvTargetModel.fromInfractionEnvTargetEntity( - infraction.target!! - ) - ) - } - ) + fun fromInfractionEntity(infraction: InfractionEntity): InfractionModel { + return InfractionModel( + id = infraction.id, + missionId = infraction.missionId, + actionId = infraction.actionId, + controlType = infraction.controlType.toString(), + infractionType = infraction.infractionType?.toString(), + natinfs = infraction.natinfs, + observations = infraction.observations, + target = infraction.target?.let { + listOf(InfractionEnvTargetModel.fromInfractionEnvTargetEntity(it)) + } ?: emptyList() // Provide an empty list if target is null + ) + } + } } diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/database/repositories/interfaces/mission/infraction/IDBInfractionEnvTargetRepository.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/database/repositories/interfaces/mission/infraction/IDBInfractionEnvTargetRepository.kt new file mode 100644 index 00000000..e91c4245 --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/database/repositories/interfaces/mission/infraction/IDBInfractionEnvTargetRepository.kt @@ -0,0 +1,9 @@ +package fr.gouv.dgampa.rapportnav.infrastructure.database.repositories.interfaces.mission.infraction + +import fr.gouv.dgampa.rapportnav.infrastructure.database.model.mission.infraction.InfractionEnvTargetModel +import org.springframework.data.jpa.repository.JpaRepository +import java.util.* + +interface IDBInfractionEnvTargetRepository : JpaRepository { + fun findByActionIdAndVesselIdentifier(actionId: String, vesselIdentifier: String): List +} diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/database/repositories/mission/infraction/JPAInfractionEnvTargetRepository.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/database/repositories/mission/infraction/JPAInfractionEnvTargetRepository.kt new file mode 100644 index 00000000..0fa9d53a --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/database/repositories/mission/infraction/JPAInfractionEnvTargetRepository.kt @@ -0,0 +1,44 @@ +package fr.gouv.dgampa.rapportnav.infrastructure.database.repositories.mission.infraction + +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.infraction.InfractionEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.infraction.InfractionEnvTargetEntity +import fr.gouv.dgampa.rapportnav.domain.repositories.mission.infraction.IInfractionEnvTargetRepository +import fr.gouv.dgampa.rapportnav.infrastructure.database.model.mission.infraction.InfractionEnvTargetModel +import fr.gouv.dgampa.rapportnav.infrastructure.database.repositories.interfaces.mission.infraction.IDBInfractionEnvTargetRepository +import org.springframework.dao.InvalidDataAccessApiUsageException +import org.springframework.stereotype.Repository +import org.springframework.transaction.annotation.Transactional + + +@Repository +class JPAInfractionEnvTargetRepository( + private val dbRepo: IDBInfractionEnvTargetRepository, +) : IInfractionEnvTargetRepository { + + @Transactional + override fun save( + infractionTarget: InfractionEnvTargetEntity, + infraction: InfractionEntity + ): InfractionEnvTargetModel { + return try { + val model = InfractionEnvTargetModel.fromInfractionEnvTargetEntity(infractionTarget, infraction) + dbRepo.save(model) + } catch (e: InvalidDataAccessApiUsageException) { + throw Exception("Error saving or updating Infraction Env Target", e) + } + } + + override fun findByActionIdAndVesselIdentifier( + actionId: String, + vesselIdentifier: String + ): List { + return try { + val models = dbRepo.findByActionIdAndVesselIdentifier(actionId, vesselIdentifier) + models + } catch (e: InvalidDataAccessApiUsageException) { + throw Exception("Error findByActionIdAndVesselIdentifier Infraction Env Target", e) + } + } + + +} diff --git a/backend/src/main/resources/graphql/infraction.graphqls b/backend/src/main/resources/graphql/infraction.graphqls index 152ef611..840bcc42 100644 --- a/backend/src/main/resources/graphql/infraction.graphqls +++ b/backend/src/main/resources/graphql/infraction.graphqls @@ -37,7 +37,7 @@ type InfractionByTarget { vesselIdentifier: String vesselType: VesselType controlTypesWithInfraction: [ControlType] - targetAddedInRapportNav: Boolean + targetAddedByUnit: Boolean infractions: [Infraction] } diff --git a/ci/jobs/analyse_trivy.yml b/ci/jobs/analyse_trivy.yml index 7fbbffc1..242f0d42 100644 --- a/ci/jobs/analyse_trivy.yml +++ b/ci/jobs/analyse_trivy.yml @@ -12,6 +12,7 @@ analyse_trivy: TRIVY_NON_SSL: "true" TRIVY_SERVER: "http://trivy.dsi.damgm.i2" FULL_IMAGE_NAME: $TAG_LATEST + BDD_IMAGE_NAME: $NEXUS_DOCKER_PROXY/$BDD_IAMGE HTTP_PROXY: "http://172.27.229.197:8090" HTTPS_PROXY: "http://172.27.229.197:8090" NO_PROXY: "gitlab-sml.din.developpement-durable.gouv.fr,int-docker01,localhost,127.0.0.1,0.0.0.0,.dsi.damgm.i2" @@ -22,10 +23,20 @@ analyse_trivy: - | if [[ "$FAIL_TRIVY_CONDITION" != "" && "$KEEP_RUNNING" != "true" ]]; then # Fail on critical vulnerabilities - time trivy image -d --server $TRIVY_SERVER --exit-code 1 --cache-dir .trivycache/ $FAIL_TRIVY_CONDITION --no-progress --format template --template "@/contrib/html.tpl" --output "container-scanning-report.html" "$FULL_IMAGE_NAME" + time trivy image -d --server $TRIVY_SERVER --exit-code 1 --cache-dir .trivycache/ $FAIL_TRIVY_CONDITION --no-progress --format template --template "@/contrib/html.tpl" --output "container-scanning-report-app.html" "$FULL_IMAGE_NAME" else # Builds report and puts it in the default workdir $CI_PROJECT_DIR, so `artifacts:` can take it from there - time trivy image -d --server $TRIVY_SERVER --exit-code 0 --cache-dir .trivycache/ --no-progress --format template --template "@/contrib/html.tpl" --output "container-scanning-report.html" "$FULL_IMAGE_NAME" + time trivy image -d --server $TRIVY_SERVER --exit-code 0 --cache-dir .trivycache/ --no-progress --format template --template "@/contrib/html.tpl" --output "container-scanning-report-app.html" "$FULL_IMAGE_NAME" + fi + + - time trivy image -d --server $TRIVY_SERVER $BDD_IMAGE_NAME --clear-cache --timeout 120m --cache-dir .trivycache/ + - | + if [[ "$FAIL_TRIVY_CONDITION" != "" && "$KEEP_RUNNING" != "true" ]]; then + # Fail on critical vulnerabilities + time trivy image -d --server $TRIVY_SERVER --exit-code 1 --cache-dir .trivycache/ $FAIL_TRIVY_CONDITION --no-progress --format template --template "@/contrib/html.tpl" --output "container-scanning-report-bdd.html" "$BDD_IMAGE_NAME" + else + # Builds report and puts it in the default workdir $CI_PROJECT_DIR, so `artifacts:` can take it from there + time trivy image -d --server $TRIVY_SERVER --exit-code 0 --cache-dir .trivycache/ --no-progress --format template --template "@/contrib/html.tpl" --output "container-scanning-report-bdd.html" "$BDD_IMAGE_NAME" fi cache: paths: @@ -33,7 +44,8 @@ analyse_trivy: artifacts: when: always paths: - - container-scanning-report.html + - container-scanning-report-app.html + - container-scanning-report-bdd.html tags: - analyze diff --git a/ci/jobs/construction_image.yml b/ci/jobs/construction_image.yml index 4d7fc1e3..ffd9506f 100644 --- a/ci/jobs/construction_image.yml +++ b/ci/jobs/construction_image.yml @@ -28,6 +28,9 @@ construction_image: - docker login $NEXUS_DOCKER_REPO -u $NEXUS_USER -p $NEXUS_PWD - docker image push --all-tags $NEXUS_DOCKER_REPO/$PROJECT_NAME - docker logout $NEXUS_DOCKER_REPO + - docker login $NEXUS_DOCKER_PROXY -u $NEXUS_USER -p $NEXUS_PWD + - docker pull $NEXUS_DOCKER_PROXY/$BDD_IAMGE + - docker logout $NEXUS_DOCKER_PROXY tags: - build diff --git a/ci/roles/start_docker_containers/templates/docker-compose.yml.j2 b/ci/roles/start_docker_containers/templates/docker-compose.yml.j2 index 1ab40030..433fc66a 100644 --- a/ci/roles/start_docker_containers/templates/docker-compose.yml.j2 +++ b/ci/roles/start_docker_containers/templates/docker-compose.yml.j2 @@ -26,7 +26,7 @@ services: max-size: "1024m" db: - image: postgres:15-bookworm + image: {{ image_bdd }} volumes: # - "${RAPPORTNAV_BACKUPS_FOLDER}:/opt/rapportnav_backups" - db:/var/lib/postgresql/data diff --git a/ci/roles/start_docker_containers/vars/main.yml b/ci/roles/start_docker_containers/vars/main.yml index 0d80fe8a..0b01c001 100644 --- a/ci/roles/start_docker_containers/vars/main.yml +++ b/ci/roles/start_docker_containers/vars/main.yml @@ -4,3 +4,4 @@ docker_image_path: "{{ lookup('env', 'TAG_VERSION') }}" docker_compose_path: "/opt/dockers/{{ project_name }}" project_ports: "{{ lookup('env', 'PROJECT_PORTS') }}" project_volumes: "{{ lookup('env', 'PROJECT_VOLUMES') }}" +image_bdd: "{{ lookup('env', 'BDD_IAMGE') }}" \ No newline at end of file diff --git a/frontend/src/auth/test-server.ts b/frontend/src/auth/test-server.ts index 8802fa1e..00abda01 100644 --- a/frontend/src/auth/test-server.ts +++ b/frontend/src/auth/test-server.ts @@ -5,17 +5,11 @@ import { LOGIN_ENDPOINT } from "./login.tsx"; export const loginSuccessHandler = [ http.post(LOGIN_ENDPOINT, () => { - // return new HttpResponse(null, { - // status: 202, - // }).json({ token: 'jwt' }) return HttpResponse.json({token: 'jwt'}, {status: 200}) - // return res(ctx.status(200), ctx.json({token: 'jwt'})) }) ] export const loginFailedHandler = http.post(LOGIN_ENDPOINT, (_req, res, ctx) => { - // return HttpResponse.error().json({ message: 'Login Failed' }) return HttpResponse.json({message: 'Login Failed'}, {status: 400}) - // return res(ctx.status(400), ctx.json({message: 'Login Failed'})) }) // This configures a request mocking server with the given request handlers. diff --git a/frontend/src/pam/mission/actions/action-control-env.tsx b/frontend/src/pam/mission/actions/action-control-env.tsx index 8b3948bb..4f66d77e 100644 --- a/frontend/src/pam/mission/actions/action-control-env.tsx +++ b/frontend/src/pam/mission/actions/action-control-env.tsx @@ -10,242 +10,218 @@ import { Action } from '../../../types/action-types' import { ControlType } from '../../../types/control-types' import { actionTargetTypeLabels, EnvActionControl, vehicleTypeLabels } from '../../../types/env-mission-types' import { useParams } from 'react-router-dom' -import EnvInfractionNewTarget from '../infractions/env-infraction-new-target' -import EnvInfractionExistingTarget from '../infractions/env-infraction-existing-target' +import EnvInfractionAddNewTarget from '../infractions/env-infraction-add-new-target.tsx' +import EnvInfractionExistingTargets from '../infractions/env-infraction-existing-targets.tsx' import useActionById from "./use-action-by-id.tsx"; import { extractLatLonFromMultiPoint } from "../../../utils/geometry.ts"; interface ActionControlPropsEnv { - action: Action + action: Action } const ActionControlEnv: React.FC = ({action}) => { - const {missionId, actionId} = useParams() + const {missionId, actionId} = useParams() - const { - data: envAction, - loading, - error, - } = useActionById(actionId, missionId, action.source, action.type) + const { + data: envAction, + loading, + error, + } = useActionById(actionId, missionId, action.source, action.type) - if (loading) { - return ( -
loading
- ) - } - if (error) { - return ( -
error
- ) - } - if (envAction) { - const actionData: EnvActionControl = envAction.data - return ( - - - - - - - - - Contrôles {envAction.startDateTimeUtc && `(${formatDateTimeForFrenchHumans(envAction.startDateTimeUtc)})`} - - - - - - - - {actionData?.themes[0].theme ?? 'inconnue'} - - - - - - {actionData?.themes[0].subThemes?.length - ? actionData?.themes[0].subThemes?.join(', ') - : 'inconnues'} - - - - - - - - - {true && (() => { - const [latitude, longitude] = extractLatLonFromMultiPoint(actionData?.geom); - return ( - - ); - })()} - - - {/**/} - - - - - - - - - - - - - - {actionData?.actionNumberOfControls ?? 'inconnu'} - - - - - - {!!actionData?.actionTargetType - ? actionTargetTypeLabels[actionData?.actionTargetType].libelle - : 'inconnu'} - - - - - - {!!actionData?.vehicleType ? vehicleTypeLabels[actionData?.vehicleType].libelle : 'inconnu'} - - - - - - {actionData?.observations ?? 'aucunes'} - - - + if (loading) { + return ( +
loading
+ ) + } + if (error) { + return ( +
error
+ ) + } + if (envAction) { + const actionData: EnvActionControl = envAction.data + return ( + + + + + + + + + Contrôles {envAction.startDateTimeUtc && `(${formatDateTimeForFrenchHumans(envAction.startDateTimeUtc)})`} + + + + + + + + {actionData?.themes[0].theme ?? 'inconnue'} + + + + + + {actionData?.themes[0].subThemes?.length + ? actionData?.themes[0].subThemes?.join(', ') + : 'inconnues'} + + + + + + + + + {true && (() => { + const [latitude, longitude] = extractLatLonFromMultiPoint(actionData?.geom); + return ( + + ); + })()} + + + + + + + + + + + + + {actionData?.actionNumberOfControls ?? 'inconnu'} + + + + + + {!!actionData?.actionTargetType + ? actionTargetTypeLabels[actionData?.actionTargetType].libelle + : 'inconnu'} + + + + + + {!!actionData?.vehicleType ? vehicleTypeLabels[actionData?.vehicleType].libelle : 'inconnu'} + + + + + + {actionData?.observations ?? 'aucunes'} + + + + - - - {(actionData?.controlsToComplete?.length || 0) > 0 && ( - - - - )} + + + {(actionData?.controlsToComplete?.length || 0) > 0 && ( + + + + )} - - - dont... - - - - - - - - - - - - - - - + + + dont... + + + + + + + + + + + + + + + + + + + + + + + + + - -
- - - - - - -
-
-
- ) - } +
+ ) + } - return null + return null } export default ActionControlEnv diff --git a/frontend/src/pam/mission/actions/action-control-nav.tsx b/frontend/src/pam/mission/actions/action-control-nav.tsx index d7776154..8801ae73 100644 --- a/frontend/src/pam/mission/actions/action-control-nav.tsx +++ b/frontend/src/pam/mission/actions/action-control-nav.tsx @@ -1,20 +1,20 @@ import React, { useEffect, useState } from 'react' import { - Accent, - Button, - Coordinates, - CoordinatesFormat, - CoordinatesInput, - DateRange, - DateRangePicker, - Icon, - Label, - OptionValue, - Select, - Size, - Textarea, - TextInput, - THEME + Accent, + Button, + Coordinates, + CoordinatesFormat, + CoordinatesInput, + DateRange, + DateRangePicker, + Icon, + Label, + OptionValue, + Select, + Size, + Textarea, + TextInput, + THEME } from '@mtes-mct/monitor-ui' import { VesselTypeEnum } from '../../../types/mission-types' import { Action, ActionControl } from '../../../types/action-types' @@ -34,266 +34,266 @@ import { GET_MISSION_TIMELINE } from "../timeline/use-mission-timeline.tsx"; import useActionById from "./use-action-by-id.tsx"; interface ActionControlNavProps { - action: Action + action: Action } const ActionControlNav: React.FC = ({action}) => { - const navigate = useNavigate() - const {missionId, actionId} = useParams() - const [observationsValue, setObservationsValue] = useState( - undefined - ) - const [identityControlledPersonValue, setIdentityControlledPersonValue] = useState( - undefined - ) - - const [mutateControl] = useMutation( - MUTATION_ADD_OR_UPDATE_ACTION_CONTROL, - { - refetchQueries: [GET_MISSION_TIMELINE] - } - ) - const [deleteControl] = useMutation(DELETE_ACTION_CONTROL, { - refetchQueries: [GET_MISSION_TIMELINE] - }) - - const {data: navAction, loading, error} = useActionById(actionId, missionId, action.source, action.type) - - useEffect(() => { - setObservationsValue(navAction?.data.observations) - setIdentityControlledPersonValue(navAction?.data.identityControlledPerson) - }, [navAction]) - - - if (loading) { - return ( -
loading
+ const navigate = useNavigate() + const {missionId, actionId} = useParams() + const [observationsValue, setObservationsValue] = useState( + undefined ) - } - if (error) { - return ( -
error
+ const [identityControlledPersonValue, setIdentityControlledPersonValue] = useState( + undefined ) - } - if (navAction) { - const control = navAction?.data as ActionControl + const [mutateControl] = useMutation( + MUTATION_ADD_OR_UPDATE_ACTION_CONTROL, + { + refetchQueries: [GET_MISSION_TIMELINE] + } + ) + const [deleteControl] = useMutation(DELETE_ACTION_CONTROL, { + refetchQueries: [GET_MISSION_TIMELINE] + }) - const handleObservationsChange = (nextValue?: string) => { - setObservationsValue(nextValue) - } - const handleObservationsBlur = () => { - onChange('observations', observationsValue) - } + const {data: navAction, loading, error} = useActionById(actionId, missionId, action.source, action.type) - const handleIdentityControlledPersonChange = (nextValue?: string) => { - setIdentityControlledPersonValue(nextValue) + useEffect(() => { + setObservationsValue(navAction?.data.observations) + setIdentityControlledPersonValue(navAction?.data.identityControlledPerson) + }, [navAction]) + + + if (loading) { + return ( +
loading
+ ) } - const handleIdentityControlledPersonBlur = () => { - onChange('identityControlledPerson', identityControlledPersonValue) + if (error) { + return ( +
error
+ ) } + if (navAction) { + const control = navAction?.data as ActionControl - const onChange = (field: string, value: any) => { - let updatedField = {} - if (field === 'dates') { - debugger - const startDateTimeUtc = value[0].toISOString() - const endDateTimeUtc = value[1].toISOString() - updatedField = { - startDateTimeUtc, - endDateTimeUtc + const handleObservationsChange = (nextValue?: string) => { + setObservationsValue(nextValue) } - } else if (field === 'geom') { - updatedField = { - latitude: value[0], - longitude: value[1] + const handleObservationsBlur = () => { + onChange('observations', observationsValue) } - } else { - updatedField = { - [field]: value + + const handleIdentityControlledPersonChange = (nextValue?: string) => { + setIdentityControlledPersonValue(nextValue) + } + const handleIdentityControlledPersonBlur = () => { + onChange('identityControlledPerson', identityControlledPersonValue) } - } - const updatedData = { - missionId: missionId, - ...omit(control, [ - '__typename', - 'controlAdministrative', - 'controlGensDeMer', - 'controlNavigation', - 'controlSecurity' - ]), - startDateTimeUtc: action.startDateTimeUtc, - endDateTimeUtc: action.endDateTimeUtc, - ...updatedField - } - mutateControl({variables: {controlAction: updatedData}}) - } + const onChange = (field: string, value: any) => { + let updatedField = {} + if (field === 'dates') { + debugger + const startDateTimeUtc = value[0].toISOString() + const endDateTimeUtc = value[1].toISOString() + updatedField = { + startDateTimeUtc, + endDateTimeUtc + } + } else if (field === 'geom') { + updatedField = { + latitude: value[0], + longitude: value[1] + } + } else { + updatedField = { + [field]: value + } + } + + const updatedData = { + missionId: missionId, + ...omit(control, [ + '__typename', + 'controlAdministrative', + 'controlGensDeMer', + 'controlNavigation', + 'controlSecurity' + ]), + startDateTimeUtc: action.startDateTimeUtc, + endDateTimeUtc: action.endDateTimeUtc, + ...updatedField + } - const deleteAction = () => { - deleteControl({ - variables: { - id: action.id! + mutateControl({variables: {controlAction: updatedData}}) } - }) - navigate(`/pam/missions/${missionId}`) - } + const deleteAction = () => { + deleteControl({ + variables: { + id: action.id! + } + }) + navigate(`/pam/missions/${missionId}`) + } - return ( - - {/* TITLE AND BUTTONS */} - - - - - - - - - - Contrôles {action.startDateTimeUtc && `(${formatDateTimeForFrenchHumans(action.startDateTimeUtc)})`} - + + return ( + + {/* TITLE AND BUTTONS */} + + + + + + + + + + Contrôles {action.startDateTimeUtc && `(${formatDateTimeForFrenchHumans(action.startDateTimeUtc)})`} + + + + + {controlMethodToHumanString(control.controlMethod)} - {vesselTypeToHumanString(control.vesselType)} + + + + + + + + + + + + + + + + {/* INFO TEXT */} - - {controlMethodToHumanString(control.controlMethod)} - {vesselTypeToHumanString(control.vesselType)} - + + + + + + + Pour la saisie des contrôles de la pêche et de l’environnement marin, veuillez appeler + les + centres + concernés. +
+ Pêche : CNSP / Environnement Marin : CACEM +
+
+
-
-
- - + {/* DATE FIELDS */} - + { + onChange('dates', nextUtcDateRange) + }} + /> + {/* CONTROL ZONES FIELD */} - + onChange('geom', nextCoordinates)} + /> - - -
-
- {/* INFO TEXT */} - - - - - - - - Pour la saisie des contrôles de la pêche et de l’environnement marin, veuillez appeler - les - centres - concernés. -
- Pêche : CNSP / Environnement Marin : CACEM -
-
-
-
- {/* DATE FIELDS */} - - { - onChange('dates', nextUtcDateRange) - }} - /> - - {/* CONTROL ZONES FIELD */} - - onChange('geom', nextCoordinates)} - /> - - {/* VESSEL INFORMATION */} - - - -