diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/missions/CreateOrUpdateMissionWithAttachedReporting.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/missions/CreateOrUpdateMissionWithAttachedReporting.kt index a88451318..6b8ccfc62 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/missions/CreateOrUpdateMissionWithAttachedReporting.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/missions/CreateOrUpdateMissionWithAttachedReporting.kt @@ -18,8 +18,8 @@ import fr.gouv.cacem.monitorenv.infrastructure.api.adapters.bff.inputs.missions. @UseCase class CreateOrUpdateMissionWithAttachedReporting( private val departmentRepository: IDepartmentAreaRepository, - private val missionRepository: IMissionRepository, private val facadeRepository: IFacadeAreasRepository, + private val missionRepository: IMissionRepository, private val reportingRepository: IReportingRepository, ) { diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/outputs/missions/MissionDataOutput.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/outputs/missions/MissionDataOutput.kt index fb019f246..9e5617787 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/outputs/missions/MissionDataOutput.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/outputs/missions/MissionDataOutput.kt @@ -3,7 +3,6 @@ package fr.gouv.cacem.monitorenv.infrastructure.api.adapters.bff.outputs.mission import fr.gouv.cacem.monitorenv.domain.entities.controlUnit.LegacyControlUnitEntity import fr.gouv.cacem.monitorenv.domain.entities.mission.MissionSourceEnum import fr.gouv.cacem.monitorenv.domain.entities.mission.MissionTypeEnum -import fr.gouv.cacem.monitorenv.domain.entities.mission.envAction.EnvActionEntity import fr.gouv.cacem.monitorenv.domain.use_cases.missions.dtos.MissionDTO import org.locationtech.jts.geom.MultiPolygon import java.time.ZonedDateTime @@ -20,7 +19,7 @@ data class MissionDataOutput( val geom: MultiPolygon? = null, val startDateTimeUtc: ZonedDateTime, val endDateTimeUtc: ZonedDateTime? = null, - val envActions: List? = null, + val envActions: List? = null, val missionSource: MissionSourceEnum, val isClosed: Boolean, val hasMissionOrder: Boolean, @@ -48,7 +47,7 @@ data class MissionDataOutput( geom = dto.mission.geom, startDateTimeUtc = dto.mission.startDateTimeUtc, endDateTimeUtc = dto.mission.endDateTimeUtc, - envActions = dto.mission.envActions, + envActions = dto.mission.envActions?.map { MissionEnvActionDataOutput.fromEnvActionEntity(it) }, missionSource = dto.mission.missionSource, isClosed = dto.mission.isClosed, hasMissionOrder = dto.mission.hasMissionOrder, diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/outputs/missions/MissionEnvActionDataOutput.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/outputs/missions/MissionEnvActionDataOutput.kt index 5baacbf8d..5968ba830 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/outputs/missions/MissionEnvActionDataOutput.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/adapters/bff/outputs/missions/MissionEnvActionDataOutput.kt @@ -1,3 +1,45 @@ package fr.gouv.cacem.monitorenv.infrastructure.api.adapters.bff.outputs.missions -data class MissionEnvActionDataOutput() +import com.fasterxml.jackson.annotation.JsonSubTypes +import com.fasterxml.jackson.annotation.JsonTypeInfo +import fr.gouv.cacem.monitorenv.domain.entities.mission.envAction.ActionTypeEnum +import fr.gouv.cacem.monitorenv.domain.entities.mission.envAction.EnvActionEntity +import fr.gouv.cacem.monitorenv.domain.entities.mission.envAction.EnvActionNoteEntity +import fr.gouv.cacem.monitorenv.domain.entities.mission.envAction.EnvActionSurveillanceEntity +import fr.gouv.cacem.monitorenv.domain.entities.mission.envAction.envActionControl.EnvActionControlEntity +import org.locationtech.jts.geom.Geometry +import java.time.ZonedDateTime +import java.util.UUID + +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.PROPERTY, + property = "actionType", + visible = true, +) +@JsonSubTypes( + JsonSubTypes.Type(EnvActionControlEntity::class, name = "CONTROL"), + JsonSubTypes.Type(EnvActionSurveillanceEntity::class, name = "SURVEILLANCE"), + JsonSubTypes.Type(EnvActionNoteEntity::class, name = "NOTE"), +) +data class MissionEnvActionDataOutput( + val id: UUID, + val actionType: ActionTypeEnum, + val actionStartDateTimeUtc: ZonedDateTime? = null, + val actionEndDateTimeUtc: ZonedDateTime? = null, + val department: String? = null, + val facade: String? = null, + val geom: Geometry? = null, +) { + companion object { + fun fromEnvActionEntity(envActionEntity: EnvActionEntity) = MissionEnvActionDataOutput( + id = envActionEntity.id, + actionType = envActionEntity.actionType, + actionStartDateTimeUtc = envActionEntity.actionStartDateTimeUtc, + actionEndDateTimeUtc = envActionEntity.actionEndDateTimeUtc, + department = envActionEntity.department, + facade = envActionEntity.facade, + geom = envActionEntity.geom, + ) + } +} diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/EnvActionModel.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/EnvActionModel.kt index 43db310b2..9f060e906 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/EnvActionModel.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/EnvActionModel.kt @@ -65,9 +65,9 @@ data class EnvActionModel( @Column(name = "is_safety_equipment_and_standards_compliance_control") val isSafetyEquipmentAndStandardsComplianceControl: Boolean? = null, @Column(name = "is_seafarers_control") val isSeafarersControl: Boolean? = null, - @OneToMany(fetch = FetchType.EAGER, mappedBy = "attachedEnvActionId") + @OneToMany(fetch = FetchType.EAGER, mappedBy = "attachedEnvAction") @JsonManagedReference - val attachedReporting: List? = null, + val attachedReporting: List? = listOf(), ) { fun toActionEntity(mapper: ObjectMapper): EnvActionEntity { diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/ReportingModel.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/ReportingModel.kt index a847a88b4..8e7b291d6 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/ReportingModel.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/ReportingModel.kt @@ -36,7 +36,6 @@ import org.n52.jackson.datatype.jts.GeometryDeserializer import org.n52.jackson.datatype.jts.GeometrySerializer import java.time.Instant import java.time.ZoneOffset.UTC -import java.util.UUID @Entity @Table(name = "reportings") @@ -45,6 +44,7 @@ data class ReportingModel( @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", unique = true, nullable = false) val id: Int? = null, + @Generated(GenerationTime.INSERT) @Column( name = "reporting_id", @@ -54,62 +54,100 @@ data class ReportingModel( insertable = false, ) val reportingId: Long? = null, + @Column(name = "source_type", columnDefinition = "reportings_source_type") @Enumerated(EnumType.STRING) @Type(PostgreSQLEnumType::class) val sourceType: SourceTypeEnum? = null, + @ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "semaphore_id", nullable = true) @JsonBackReference val semaphore: SemaphoreModel? = null, + @ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "control_unit_id", nullable = true) @JsonBackReference val controlUnit: ControlUnitModel? = null, - @Column(name = "source_name") val sourceName: String? = null, + @Column(name = "source_name") + val sourceName: String? = null, + @Column(name = "target_type", columnDefinition = "reportings_target_type") @Enumerated(EnumType.STRING) @Type(PostgreSQLEnumType::class) val targetType: TargetTypeEnum? = null, + @Column(name = "vehicle_type", columnDefinition = "reportings_vehicle_type") @Enumerated(EnumType.STRING) @Type(PostgreSQLEnumType::class) val vehicleType: VehicleTypeEnum? = null, + @Column(name = "target_details", columnDefinition = "jsonb") @Type(JsonBinaryType::class) val targetDetails: List? = listOf(), + @JsonSerialize(using = GeometrySerializer::class) @JsonDeserialize(contentUsing = GeometryDeserializer::class) @Column(name = "geom") val geom: Geometry? = null, - @Column(name = "sea_front") val seaFront: String? = null, - @Column(name = "description") val description: String? = null, + + @Column(name = "sea_front") + val seaFront: String? = null, + + @Column(name = "description") + val description: String? = null, + @Column(name = "report_type", columnDefinition = "reportings_report_type") @Enumerated(EnumType.STRING) @Type(PostgreSQLEnumType::class) val reportType: ReportingTypeEnum? = null, - @Column(name = "theme") val theme: String? = null, + + @Column(name = "theme") + val theme: String? = null, + @Column(name = "sub_themes") @Type(ListArrayType::class) val subThemes: List? = listOf(), - @Column(name = "action_taken") val actionTaken: String? = null, - @Column(name = "is_control_required") val isControlRequired: Boolean? = null, - @Column(name = "has_no_unit_available") val hasNoUnitAvailable: Boolean? = null, - @Column(name = "created_at") val createdAt: Instant, - @Column(name = "validity_time") val validityTime: Int? = null, - @Column(name = "is_archived", nullable = false) val isArchived: Boolean, - @Column(name = "is_deleted", nullable = false) val isDeleted: Boolean, - @Column(name = "open_by") val openBy: String? = null, + + @Column(name = "action_taken") + val actionTaken: String? = null, + + @Column(name = "is_control_required") + val isControlRequired: Boolean? = null, + + @Column(name = "has_no_unit_available") + val hasNoUnitAvailable: Boolean? = null, + + @Column(name = "created_at") + val createdAt: Instant, + + @Column(name = "validity_time") + val validityTime: Int? = null, + + @Column(name = "is_archived", nullable = false) + val isArchived: Boolean, + + @Column(name = "is_deleted", nullable = false) + val isDeleted: Boolean, + + @Column(name = "open_by") + val openBy: String? = null, + @ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "mission_id", nullable = true) @JsonBackReference val mission: MissionModel? = null, - @Column(name = "attached_to_mission_at_utc") val attachedToMissionAtUtc: Instant? = null, + + @Column(name = "attached_to_mission_at_utc") + val attachedToMissionAtUtc: Instant? = null, + @Column(name = "detached_from_mission_at_utc") val detachedFromMissionAtUtc: Instant? = null, + @JdbcType(UUIDJdbcType::class) - @Column(name = "attached_env_action_id", columnDefinition = "uuid") - val attachedEnvActionId: UUID? = null, + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "attached_env_action_id", columnDefinition = "uuid", referencedColumnName = "id") + val attachedEnvAction: EnvActionModel? = null, ) { fun toReporting() = @@ -140,7 +178,7 @@ data class ReportingModel( missionId = mission?.id, attachedToMissionAtUtc = attachedToMissionAtUtc?.atZone(UTC), detachedFromMissionAtUtc = detachedFromMissionAtUtc?.atZone(UTC), - attachedEnvActionId = attachedEnvActionId, + attachedEnvActionId = attachedEnvAction?.id, ) fun toReportingDTO(objectMapper: ObjectMapper) = ReportingDTO( @@ -182,6 +220,7 @@ data class ReportingModel( semaphoreReference: SemaphoreModel?, controlUnitReference: ControlUnitModel?, missionReference: MissionModel?, + envActionReference: EnvActionModel?, ) = ReportingModel( id = reporting.id, @@ -210,7 +249,7 @@ data class ReportingModel( mission = missionReference, attachedToMissionAtUtc = reporting.attachedToMissionAtUtc?.toInstant(), detachedFromMissionAtUtc = reporting.detachedFromMissionAtUtc?.toInstant(), - attachedEnvActionId = reporting.attachedEnvActionId, + attachedEnvAction = envActionReference, ) } } diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaReportingRepository.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaReportingRepository.kt index 4185438f6..54e55369a 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaReportingRepository.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/JpaReportingRepository.kt @@ -9,6 +9,7 @@ import fr.gouv.cacem.monitorenv.domain.repositories.IReportingRepository import fr.gouv.cacem.monitorenv.domain.use_cases.reportings.dtos.ReportingDTO import fr.gouv.cacem.monitorenv.infrastructure.database.model.ReportingModel import fr.gouv.cacem.monitorenv.infrastructure.database.repositories.interfaces.IDBControlUnitRepository +import fr.gouv.cacem.monitorenv.infrastructure.database.repositories.interfaces.IDBEnvActionRepository import fr.gouv.cacem.monitorenv.infrastructure.database.repositories.interfaces.IDBMissionRepository import fr.gouv.cacem.monitorenv.infrastructure.database.repositories.interfaces.IDBReportingRepository import fr.gouv.cacem.monitorenv.infrastructure.database.repositories.interfaces.IDBSemaphoreRepository @@ -27,6 +28,7 @@ class JpaReportingRepository( private val dbMissionRepository: IDBMissionRepository, private val dbSemaphoreRepository: IDBSemaphoreRepository, private val dbControlUnitRepository: IDBControlUnitRepository, + private val dbEnvActionRepository: IDBEnvActionRepository, private val mapper: ObjectMapper, ) : IReportingRepository { @@ -103,12 +105,22 @@ class JpaReportingRepository( } else { null } + val envActionReference = + if (reporting.attachedEnvActionId != null) { + dbEnvActionRepository.getReferenceById( + reporting.attachedEnvActionId, + ) + } else { + null + } + val reportingModel = ReportingModel.fromReportingEntity( reporting = reporting, semaphoreReference = semaphoreReference, controlUnitReference = controlUnitReference, missionReference = missionReference, + envActionReference = envActionReference, ) dbReportingRepository.saveAndFlush(reportingModel).toReportingDTO(mapper) } catch (e: JpaObjectRetrievalFailureException) { diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/interfaces/IDBEnvActionRepository.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/interfaces/IDBEnvActionRepository.kt new file mode 100644 index 000000000..4d6bce672 --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/interfaces/IDBEnvActionRepository.kt @@ -0,0 +1,7 @@ +package fr.gouv.cacem.monitorenv.infrastructure.database.repositories.interfaces + +import fr.gouv.cacem.monitorenv.infrastructure.database.model.EnvActionModel +import org.springframework.data.jpa.repository.JpaRepository +import java.util.UUID + +interface IDBEnvActionRepository : JpaRepository diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/interfaces/IDBReportingRepository.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/interfaces/IDBReportingRepository.kt index d2c0bb057..a89f02ee2 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/interfaces/IDBReportingRepository.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/interfaces/IDBReportingRepository.kt @@ -48,14 +48,6 @@ interface IDBReportingRepository : JpaRepository { ) fun attachReportingsToMission(reportingIds: List, missionId: Int) - // ['envActionUUID' ,[reportingIds]] - // [['uu1', [1,2]]] - - // reporting_id, attached_env_action_id - // 1, 'uu1' - // 2, 'uu2' - // 3, null - @Modifying(clearAutomatically = true, flushAutomatically = true) @Query( value = diff --git a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/CreateOrUpdateMissionUTests.kt b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/CreateOrUpdateMissionUTests.kt index f412c874e..854f41ae6 100644 --- a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/CreateOrUpdateMissionUTests.kt +++ b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/CreateOrUpdateMissionUTests.kt @@ -11,11 +11,9 @@ import fr.gouv.cacem.monitorenv.domain.entities.mission.* import fr.gouv.cacem.monitorenv.domain.entities.mission.envAction.EnvActionNoteEntity import fr.gouv.cacem.monitorenv.domain.entities.mission.envAction.EnvActionSurveillanceEntity import fr.gouv.cacem.monitorenv.domain.entities.mission.envAction.envActionControl.EnvActionControlEntity -import fr.gouv.cacem.monitorenv.domain.repositories.IBaseRepository import fr.gouv.cacem.monitorenv.domain.repositories.IDepartmentAreaRepository import fr.gouv.cacem.monitorenv.domain.repositories.IFacadeAreasRepository import fr.gouv.cacem.monitorenv.domain.repositories.IMissionRepository -import fr.gouv.cacem.monitorenv.domain.repositories.IReportingRepository import fr.gouv.cacem.monitorenv.domain.use_cases.missions.CreateOrUpdateMission import fr.gouv.cacem.monitorenv.domain.use_cases.missions.dtos.MissionDTO import org.assertj.core.api.Assertions @@ -32,8 +30,6 @@ import java.util.* @ExtendWith(SpringExtension::class) class CreateOrUpdateMissionUTests { - @MockBean - private lateinit var baseRepository: IBaseRepository @MockBean private lateinit var departmentRepository: IDepartmentAreaRepository @@ -44,19 +40,14 @@ class CreateOrUpdateMissionUTests { @MockBean private lateinit var facadeAreasRepository: IFacadeAreasRepository - @MockBean - private lateinit var reportingRepository: IReportingRepository - @Test - fun `execute Should throw an exception When input mission is null`() { + fun `execute Should throw an exception when input mission is null`() { // When val throwable = Assertions.catchThrowable { CreateOrUpdateMission( - baseRepository = baseRepository, departmentRepository = departmentRepository, missionRepository = missionRepository, facadeRepository = facadeAreasRepository, - reportingRepository = reportingRepository, ) .execute(null) } @@ -145,11 +136,9 @@ class CreateOrUpdateMissionUTests { // When val createdMissionDTO = CreateOrUpdateMission( - baseRepository = baseRepository, departmentRepository = departmentRepository, missionRepository = missionRepository, facadeRepository = facadeAreasRepository, - reportingRepository = reportingRepository, ).execute( missionToCreate, ) @@ -182,68 +171,4 @@ class CreateOrUpdateMissionUTests { verify(missionRepository, times(1)).findById(100) assertThat(createdMissionDTO.mission).isEqualTo(expectedCreatedMission) } - - @Test - fun `should attach mission to specified reportings`() { - // Given - val wktReader = WKTReader() - - val multipolygonString = "MULTIPOLYGON(((-2.7335 47.6078, -2.7335 47.8452, -3.6297 47.8452, -3.6297 47.6078, -2.7335 47.6078)))" - val polygon = wktReader.read(multipolygonString) as MultiPolygon - - val missionToCreate = MissionEntity( - missionTypes = listOf(MissionTypeEnum.LAND), - facade = "Outre-Mer", - geom = polygon, - startDateTimeUtc = ZonedDateTime.parse("2022-01-15T04:50:09Z"), - endDateTimeUtc = ZonedDateTime.parse("2022-01-23T20:29:03Z"), - isClosed = false, - isDeleted = false, - missionSource = MissionSourceEnum.MONITORENV, - hasMissionOrder = false, - isUnderJdp = false, - isGeometryComputedFromControls = false, - - ) - val attachedReportingIds = listOf(1, 2, 3) - - val expectedCreatedMission = - MissionDTO( - mission = MissionEntity( - id = 100, - missionTypes = listOf(MissionTypeEnum.LAND), - facade = "Outre-Mer", - startDateTimeUtc = ZonedDateTime.parse("2022-01-15T04:50:09Z"), - endDateTimeUtc = ZonedDateTime.parse("2022-01-23T20:29:03Z"), - isClosed = false, - isDeleted = false, - missionSource = MissionSourceEnum.MONITORENV, - hasMissionOrder = false, - isUnderJdp = false, - isGeometryComputedFromControls = false, - ), - attachedReportingIds = attachedReportingIds, - ) - - given(missionRepository.save(anyOrNull())).willReturn(MissionDTO(mission = missionToCreate.copy(id = 100))) - given(missionRepository.findById(100)).willReturn(expectedCreatedMission) - - // When - val createdMissionDTO = CreateOrUpdateMission( - baseRepository = baseRepository, - departmentRepository = departmentRepository, - missionRepository = missionRepository, - facadeRepository = facadeAreasRepository, - reportingRepository = reportingRepository, - ).execute( - mission = missionToCreate, - attachedReportingIds = attachedReportingIds, - ) - - // Then - - verify(reportingRepository, times(1)).attachReportingsToMission(attachedReportingIds, 100) - verify(missionRepository, times(1)).findById(100) - assertThat(createdMissionDTO).isEqualTo(expectedCreatedMission) - } } diff --git a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/CreateOrUpdateMissionWithAttachedReportingUTests.kt b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/CreateOrUpdateMissionWithAttachedReportingUTests.kt new file mode 100644 index 000000000..7158f0d50 --- /dev/null +++ b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/CreateOrUpdateMissionWithAttachedReportingUTests.kt @@ -0,0 +1,248 @@ +@file:Suppress("ktlint:standard:package-name") + +package fr.gouv.cacem.monitorenv.domain.use_cases + +import com.nhaarman.mockitokotlin2.anyOrNull +import com.nhaarman.mockitokotlin2.argThat +import com.nhaarman.mockitokotlin2.given +import com.nhaarman.mockitokotlin2.times +import com.nhaarman.mockitokotlin2.verify +import fr.gouv.cacem.monitorenv.domain.entities.mission.* +import fr.gouv.cacem.monitorenv.domain.entities.mission.envAction.EnvActionNoteEntity +import fr.gouv.cacem.monitorenv.domain.entities.mission.envAction.EnvActionSurveillanceEntity +import fr.gouv.cacem.monitorenv.domain.entities.mission.envAction.envActionControl.EnvActionControlEntity +import fr.gouv.cacem.monitorenv.domain.repositories.IDepartmentAreaRepository +import fr.gouv.cacem.monitorenv.domain.repositories.IFacadeAreasRepository +import fr.gouv.cacem.monitorenv.domain.repositories.IMissionRepository +import fr.gouv.cacem.monitorenv.domain.repositories.IReportingRepository +import fr.gouv.cacem.monitorenv.domain.use_cases.missions.CreateOrUpdateMissionWithAttachedReporting +import fr.gouv.cacem.monitorenv.domain.use_cases.missions.dtos.MissionDTO +import org.assertj.core.api.Assertions +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.locationtech.jts.geom.MultiPoint +import org.locationtech.jts.geom.MultiPolygon +import org.locationtech.jts.io.WKTReader +import org.springframework.boot.test.mock.mockito.MockBean +import org.springframework.test.context.junit.jupiter.SpringExtension +import java.time.ZonedDateTime +import java.util.* + +@ExtendWith(SpringExtension::class) +class CreateOrUpdateMissionWithAttachedReportingUTests { + + @MockBean + private lateinit var departmentRepository: IDepartmentAreaRepository + + @MockBean + private lateinit var missionRepository: IMissionRepository + + @MockBean + private lateinit var facadeAreasRepository: IFacadeAreasRepository + + @MockBean + private lateinit var reportingRepository: IReportingRepository + + @Test + fun `execute Should throw an exception when input mission is null`() { + // When + val throwable = Assertions.catchThrowable { + CreateOrUpdateMissionWithAttachedReporting( + departmentRepository = departmentRepository, + facadeRepository = facadeAreasRepository, + missionRepository = missionRepository, + reportingRepository = reportingRepository, + ) + .execute( + mission = null, + envActionsAttachedToReportingIds = listOf(), + ) + } + + // Then + assertThat(throwable).isInstanceOf(IllegalArgumentException::class.java) + assertThat(throwable.message).contains("No mission to create or update") + } + + @Test + fun `should return the mission to create or update with computed facade and department info`() { + // Given + val wktReader = WKTReader() + + val multipolygonString = + "MULTIPOLYGON(((-2.7335 47.6078, -2.7335 47.8452, -3.6297 47.8452, -3.6297 47.6078, -2.7335 47.6078)))" + val polygon = wktReader.read(multipolygonString) as MultiPolygon + + val multipointString = "MULTIPOINT((49.354105 -0.427455))" + val point = wktReader.read(multipointString) as MultiPoint + + val missionToCreate = MissionEntity( + missionTypes = listOf(MissionTypeEnum.LAND), + facade = "Outre-Mer", + geom = polygon, + startDateTimeUtc = ZonedDateTime.parse("2022-01-15T04:50:09Z"), + endDateTimeUtc = ZonedDateTime.parse("2022-01-23T20:29:03Z"), + isClosed = false, + isDeleted = false, + missionSource = MissionSourceEnum.MONITORENV, + hasMissionOrder = false, + isUnderJdp = false, + envActions = listOf( + EnvActionControlEntity( + id = UUID.fromString("33310163-4e22-4d3d-b585-dac4431eb4b5"), + geom = point, + ), + EnvActionSurveillanceEntity( + id = UUID.fromString("a6c4bd17-eb45-4504-ab15-7a18ea714a10"), + geom = polygon, + ), + EnvActionNoteEntity( + id = UUID.fromString("a6c4bd17-eb45-4504-ab15-7a18ea714a10"), + observations = "Quelqu'un aurait vu quelque chose quelque part à un certain moment.", + ), + ), + isGeometryComputedFromControls = false, + ) + + val expectedCreatedMission = MissionEntity( + id = 100, + missionTypes = listOf(MissionTypeEnum.LAND), + facade = "Outre-Mer", + startDateTimeUtc = ZonedDateTime.parse("2022-01-15T04:50:09Z"), + endDateTimeUtc = ZonedDateTime.parse("2022-01-23T20:29:03Z"), + isClosed = false, + isDeleted = false, + missionSource = MissionSourceEnum.MONITORENV, + hasMissionOrder = false, + isUnderJdp = false, + isGeometryComputedFromControls = false, + envActions = listOf( + EnvActionControlEntity( + id = UUID.fromString("33310163-4e22-4d3d-b585-dac4431eb4b5"), + geom = point, + facade = "La Face Ade", + department = "Quequ'part", + ), + EnvActionSurveillanceEntity( + id = UUID.fromString("a6c4bd17-eb45-4504-ab15-7a18ea714a10"), + geom = polygon, + facade = "La Face Ade", + department = "Quequ'part", + ), + EnvActionNoteEntity( + id = UUID.fromString("a6c4bd17-eb45-4504-ab15-7a18ea714a10"), + observations = "Quelqu'un aurait vu quelque chose quelque part à un certain moment.", + ), + ), + ) + + given(facadeAreasRepository.findFacadeFromGeometry(anyOrNull())).willReturn("La Face Ade") + given(departmentRepository.findDepartmentFromGeometry(anyOrNull())).willReturn("Quequ'part") + given(missionRepository.save(anyOrNull())).willReturn(MissionDTO(mission = expectedCreatedMission)) + given(missionRepository.findById(100)).willReturn(MissionDTO(mission = expectedCreatedMission)) + + // When + val createdMissionDTO = CreateOrUpdateMissionWithAttachedReporting( + departmentRepository = departmentRepository, + facadeRepository = facadeAreasRepository, + missionRepository = missionRepository, + reportingRepository = reportingRepository, + ).execute( + missionToCreate, + envActionsAttachedToReportingIds = listOf(), + ) + + // Then + verify(facadeAreasRepository, times(2)).findFacadeFromGeometry(argThat { this == polygon }) + verify(facadeAreasRepository, times(1)).findFacadeFromGeometry(argThat { this == point }) + + verify(missionRepository, times(1)) + .save( + argThat { + this == missionToCreate.copy( + facade = "La Face Ade", + envActions = missionToCreate.envActions?.map { + when (it) { + is EnvActionControlEntity -> it.copy( + facade = "La Face Ade", + department = "Quequ'part", + ) + is EnvActionSurveillanceEntity -> it.copy( + facade = "La Face Ade", + department = "Quequ'part", + ) + else -> it + } + }, + ) + }, + ) + verify(missionRepository, times(1)).findById(100) + assertThat(createdMissionDTO.mission).isEqualTo(expectedCreatedMission) + } + + @Test + fun `should attach mission to specified reportings`() { + // Given + val wktReader = WKTReader() + + val multipolygonString = "MULTIPOLYGON(((-2.7335 47.6078, -2.7335 47.8452, -3.6297 47.8452, -3.6297 47.6078, -2.7335 47.6078)))" + val polygon = wktReader.read(multipolygonString) as MultiPolygon + + val missionToCreate = MissionEntity( + missionTypes = listOf(MissionTypeEnum.LAND), + facade = "Outre-Mer", + geom = polygon, + startDateTimeUtc = ZonedDateTime.parse("2022-01-15T04:50:09Z"), + endDateTimeUtc = ZonedDateTime.parse("2022-01-23T20:29:03Z"), + isClosed = false, + isDeleted = false, + missionSource = MissionSourceEnum.MONITORENV, + hasMissionOrder = false, + isUnderJdp = false, + isGeometryComputedFromControls = false, + + ) + val attachedReportingIds = listOf(1, 2, 3) + + val expectedCreatedMission = + MissionDTO( + mission = MissionEntity( + id = 100, + missionTypes = listOf(MissionTypeEnum.LAND), + facade = "Outre-Mer", + startDateTimeUtc = ZonedDateTime.parse("2022-01-15T04:50:09Z"), + endDateTimeUtc = ZonedDateTime.parse("2022-01-23T20:29:03Z"), + isClosed = false, + isDeleted = false, + missionSource = MissionSourceEnum.MONITORENV, + hasMissionOrder = false, + isUnderJdp = false, + isGeometryComputedFromControls = false, + ), + attachedReportingIds = attachedReportingIds, + ) + + given(missionRepository.save(anyOrNull())).willReturn(MissionDTO(mission = missionToCreate.copy(id = 100))) + given(missionRepository.findById(100)).willReturn(expectedCreatedMission) + + // When + val createdMissionDTO = CreateOrUpdateMissionWithAttachedReporting( + departmentRepository = departmentRepository, + facadeRepository = facadeAreasRepository, + missionRepository = missionRepository, + reportingRepository = reportingRepository, + ).execute( + mission = missionToCreate, + attachedReportingIds = attachedReportingIds, + envActionsAttachedToReportingIds = listOf(), + ) + + // Then + + verify(reportingRepository, times(1)).attachReportingsToMission(attachedReportingIds, 100) + verify(missionRepository, times(1)).findById(100) + assertThat(createdMissionDTO).isEqualTo(expectedCreatedMission) + } +} diff --git a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/bff/MissionsControllerITests.kt b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/bff/MissionsControllerITests.kt index 979396719..5a979c8a3 100644 --- a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/bff/MissionsControllerITests.kt +++ b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/bff/MissionsControllerITests.kt @@ -10,14 +10,14 @@ import fr.gouv.cacem.monitorenv.domain.entities.controlUnit.LegacyControlUnitEnt import fr.gouv.cacem.monitorenv.domain.entities.mission.MissionEntity import fr.gouv.cacem.monitorenv.domain.entities.mission.MissionSourceEnum import fr.gouv.cacem.monitorenv.domain.entities.mission.MissionTypeEnum +import fr.gouv.cacem.monitorenv.domain.entities.mission.envAction.ActionTypeEnum import fr.gouv.cacem.monitorenv.domain.entities.mission.envAction.envActionControl.ActionTargetTypeEnum -import fr.gouv.cacem.monitorenv.domain.entities.mission.envAction.envActionControl.EnvActionControlEntity import fr.gouv.cacem.monitorenv.domain.entities.reporting.ReportingEntity import fr.gouv.cacem.monitorenv.domain.entities.reporting.ReportingTypeEnum import fr.gouv.cacem.monitorenv.domain.entities.reporting.SourceTypeEnum import fr.gouv.cacem.monitorenv.domain.entities.reporting.TargetTypeEnum import fr.gouv.cacem.monitorenv.domain.entities.semaphore.SemaphoreEntity -import fr.gouv.cacem.monitorenv.domain.use_cases.missions.CreateOrUpdateMission +import fr.gouv.cacem.monitorenv.domain.use_cases.missions.CreateOrUpdateMissionWithAttachedReporting import fr.gouv.cacem.monitorenv.domain.use_cases.missions.DeleteMission import fr.gouv.cacem.monitorenv.domain.use_cases.missions.GetEngagedControlUnits import fr.gouv.cacem.monitorenv.domain.use_cases.missions.GetMissionById @@ -25,6 +25,8 @@ import fr.gouv.cacem.monitorenv.domain.use_cases.missions.GetMonitorEnvMissions import fr.gouv.cacem.monitorenv.domain.use_cases.missions.dtos.MissionDTO import fr.gouv.cacem.monitorenv.domain.use_cases.reportings.dtos.ReportingDTO import fr.gouv.cacem.monitorenv.infrastructure.api.adapters.bff.inputs.missions.CreateOrUpdateMissionDataInput +import fr.gouv.cacem.monitorenv.infrastructure.api.adapters.bff.inputs.missions.EnvActionAttachedToReportingIds +import fr.gouv.cacem.monitorenv.infrastructure.api.adapters.bff.inputs.missions.MissionEnvActionDataInput import org.hamcrest.Matchers.equalTo import org.junit.jupiter.api.Test import org.locationtech.jts.geom.MultiPolygon @@ -56,7 +58,7 @@ class MissionsControllerITests { private lateinit var mockMvc: MockMvc @MockBean - private lateinit var createOrUpdateMission: CreateOrUpdateMission + private lateinit var createOrUpdateMissionWithAttachedReporting: CreateOrUpdateMissionWithAttachedReporting @MockBean private lateinit var getMonitorEnvMissions: GetMonitorEnvMissions @@ -108,7 +110,13 @@ class MissionsControllerITests { missionSource = MissionSourceEnum.MONITORENV, ) val requestbody = objectMapper.writeValueAsString(newMissionRequest) - given(createOrUpdateMission.execute(newMissionRequest.toMissionEntity(), null)).willReturn(expectedNewMission) + given( + createOrUpdateMissionWithAttachedReporting.execute( + mission = newMissionRequest.toMissionEntity(), + attachedReportingIds = null, + envActionsAttachedToReportingIds = listOf(), + ), + ).willReturn(expectedNewMission) // When mockMvc.perform( put("/bff/v1/missions") @@ -290,11 +298,14 @@ class MissionsControllerITests { isGeometryComputedFromControls = false, ), ) - val envAction = EnvActionControlEntity( + val envAction = MissionEnvActionDataInput( id = UUID.fromString("bf9f4062-83d3-4a85-b89b-76c0ded6473d"), + actionType = ActionTypeEnum.CONTROL, + actionStartDateTimeUtc = ZonedDateTime.parse("2022-01-15T04:50:09Z"), actionTargetType = ActionTargetTypeEnum.VEHICLE, vehicleType = VehicleTypeEnum.VESSEL, actionNumberOfControls = 4, + reportingIds = listOf(1), ) val requestBody = CreateOrUpdateMissionDataInput( id = 14, @@ -304,8 +315,19 @@ class MissionsControllerITests { envActions = listOf(envAction), missionSource = MissionSourceEnum.MONITORENV, isClosed = false, + attachedReportingIds = listOf(1), ) - given(createOrUpdateMission.execute(requestBody.toMissionEntity(), null)).willReturn(expectedUpdatedMission) + + val envActionsAttachedToReportingIds = listOf( + Pair(UUID.fromString("bf9f4062-83d3-4a85-b89b-76c0ded6473d"), listOf(1)), + ) as List + given( + createOrUpdateMissionWithAttachedReporting.execute( + mission = requestBody.toMissionEntity(), + attachedReportingIds = listOf(1), + envActionsAttachedToReportingIds = envActionsAttachedToReportingIds, + ), + ).willReturn(expectedUpdatedMission) // When mockMvc.perform( put("/bff/v1/missions/14")