Skip to content

Commit

Permalink
Add custom alert silencing
Browse files Browse the repository at this point in the history
  • Loading branch information
louptheron committed Aug 24, 2023
1 parent e2f2d34 commit 9bb8b8f
Show file tree
Hide file tree
Showing 21 changed files with 393 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import fr.gouv.cnsp.monitorfish.domain.entities.alerts.type.AlertType
import fr.gouv.cnsp.monitorfish.domain.entities.vessel.VesselIdentifier
import java.time.ZonedDateTime

class SilencedAlert(
data class SilencedAlert(
val id: Int? = null,
val vesselId: Int? = null,
val vesselName: String? = null,
val internalReferenceNumber: String? = null,
val externalReferenceNumber: String? = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ interface SilencedAlertRepository {
silencedBeforeDate: ZonedDateTime,
isValidated: Boolean,
): SilencedAlert
fun save(silencedAlert: SilencedAlert): SilencedAlert

fun findAllCurrentSilencedAlerts(): List<SilencedAlert>
fun delete(id: Int)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import fr.gouv.cnsp.monitorfish.domain.repositories.SilencedAlertRepository
import org.slf4j.LoggerFactory

@UseCase
class DeleteSilencedOperationalAlert(private val silencedAlertRepository: SilencedAlertRepository) {
private val logger = LoggerFactory.getLogger(DeleteSilencedOperationalAlert::class.java)
class DeleteSilencedAlert(private val silencedAlertRepository: SilencedAlertRepository) {
private val logger = LoggerFactory.getLogger(DeleteSilencedAlert::class.java)

fun execute(silencedAlertId: Int) {
logger.info("Deleting silenced alert $silencedAlertId.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import fr.gouv.cnsp.monitorfish.domain.repositories.PendingAlertRepository
import org.slf4j.LoggerFactory

@UseCase
class GetOperationalAlerts(
class GetPendingAlerts(
private val pendingAlertRepository: PendingAlertRepository,
private val infractionRepository: InfractionRepository,
) {
private val logger = LoggerFactory.getLogger(GetOperationalAlerts::class.java)
private val logger = LoggerFactory.getLogger(GetPendingAlerts::class.java)

fun execute(): List<PendingAlert> {
return pendingAlertRepository.findAlertsOfTypes(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package fr.gouv.cnsp.monitorfish.domain.use_cases.alert

import fr.gouv.cnsp.monitorfish.config.UseCase
import fr.gouv.cnsp.monitorfish.domain.entities.alerts.SilencedAlert
import fr.gouv.cnsp.monitorfish.domain.entities.alerts.type.*
import fr.gouv.cnsp.monitorfish.domain.repositories.SilencedAlertRepository
import org.slf4j.LoggerFactory

@UseCase
class SilenceAlert(
private val silencedAlertRepository: SilencedAlertRepository,
) {
private val logger = LoggerFactory.getLogger(SilenceAlert::class.java)

fun execute(silencedAlert: SilencedAlert): SilencedAlert {
// We recreate the object from the `value.type` as the AlertType class from our domain contains the `natinfCode`
val silencedAlertWithNatinf = getSilencedAlertWithNatinf(silencedAlert)

val savedSilencedAlert = silencedAlertRepository.save(silencedAlertWithNatinf)

// If the silenced alert is of type MISSING_FAR_ALERT or MISSING_FAR_48_HOURS_ALERT, we need to save the other silenced alert
when (silencedAlert.value.type) {
AlertTypeMapping.MISSING_FAR_ALERT -> {
// If MISSING_FAR_ALERT was silenced, we also silence MISSING_FAR_48_HOURS_ALERT
val missingFAR48HoursSilencedAlert = silencedAlert.copy(value = MissingFAR48HoursAlert())
silencedAlertRepository.save(missingFAR48HoursSilencedAlert)
}
AlertTypeMapping.MISSING_FAR_48_HOURS_ALERT -> {
// If MISSING_FAR_48_HOURS_ALERT was silenced, we also silence MISSING_FAR_ALERT
val missingFARSilencedAlert = silencedAlert.copy(value = MissingFARAlert())
silencedAlertRepository.save(missingFARSilencedAlert)
}
else -> {}
}

logger.info(
"Alert ${savedSilencedAlert.value.type} has been silenced for vessel " +
"${savedSilencedAlert.internalReferenceNumber}/${savedSilencedAlert.ircs}/${savedSilencedAlert.externalReferenceNumber}.",
)

return savedSilencedAlert
}

private fun getSilencedAlertWithNatinf(silencedAlert: SilencedAlert): SilencedAlert {
val nextValueWithNatinf: AlertType = when (silencedAlert.value.type) {
AlertTypeMapping.THREE_MILES_TRAWLING_ALERT -> ThreeMilesTrawlingAlert()
AlertTypeMapping.FRENCH_EEZ_FISHING_ALERT -> FrenchEEZFishingAlert()
AlertTypeMapping.TWELVE_MILES_FISHING_ALERT -> TwelveMilesFishingAlert()
AlertTypeMapping.MISSING_FAR_ALERT -> MissingFARAlert()
AlertTypeMapping.MISSING_FAR_48_HOURS_ALERT -> MissingFAR48HoursAlert()
else -> {
throw IllegalArgumentException("The alert type '${silencedAlert.value.type}' could not be silenced.")
}
}
return silencedAlert.copy(value = nextValueWithNatinf)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ import org.slf4j.LoggerFactory
import java.time.ZonedDateTime

@UseCase
class SilenceOperationalAlert(
class SilencePendingAlert(
private val pendingAlertRepository: PendingAlertRepository,
private val silencedAlertRepository: SilencedAlertRepository,
private val lastPositionRepository: LastPositionRepository,
) {
private val logger = LoggerFactory.getLogger(SilenceOperationalAlert::class.java)
private val logger = LoggerFactory.getLogger(SilencePendingAlert::class.java)

fun execute(
alertId: Int,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ import org.slf4j.LoggerFactory
import java.time.ZonedDateTime

@UseCase
class ValidateOperationalAlert(
class ValidatePendingAlert(
private val pendingAlertRepository: PendingAlertRepository,
private val reportingRepository: ReportingRepository,
private val silencedAlertRepository: SilencedAlertRepository,
private val lastPositionRepository: LastPositionRepository,
) {
private val logger = LoggerFactory.getLogger(ValidateOperationalAlert::class.java)
private val logger = LoggerFactory.getLogger(ValidatePendingAlert::class.java)

fun execute(alertId: Int) {
val now = ZonedDateTime.now()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package fr.gouv.cnsp.monitorfish.infrastructure.api.bff

import com.fasterxml.jackson.databind.ObjectMapper
import fr.gouv.cnsp.monitorfish.domain.use_cases.alert.*
import fr.gouv.cnsp.monitorfish.infrastructure.api.input.SilenceOperationalAlertDataInput
import fr.gouv.cnsp.monitorfish.infrastructure.api.input.SilencedAlertDataInput
import fr.gouv.cnsp.monitorfish.infrastructure.api.outputs.PendingAlertDataOutput
import fr.gouv.cnsp.monitorfish.infrastructure.api.outputs.SilencedAlertDataOutput
import io.swagger.v3.oas.annotations.Operation
Expand All @@ -11,43 +13,45 @@ import org.springframework.web.bind.annotation.*

@RestController
@RequestMapping("/bff/v1/operational_alerts")
@Tag(name = "APIs for Operational alerts")
class OperationalAlertController(
private val getOperationalAlerts: GetOperationalAlerts,
private val validateOperationalAlert: ValidateOperationalAlert,
private val silenceOperationalAlert: SilenceOperationalAlert,
@Tag(name = "APIs for pending Operational alerts")
class PendingAlertController(
private val getPendingAlerts: GetPendingAlerts,
private val validatePendingAlert: ValidatePendingAlert,
private val silencePendingAlert: SilencePendingAlert,
private val getSilencedAlerts: GetSilencedAlerts,
private val deleteSilencedOperationalAlert: DeleteSilencedOperationalAlert,
private val deleteSilencedAlert: DeleteSilencedAlert,
private val silenceAlert: SilenceAlert,
private val objectMapper: ObjectMapper,
) {

@GetMapping("")
@Operation(summary = "Get operational alerts")
@Operation(summary = "Get pending operational alerts")
fun getOperationalAlerts(): List<PendingAlertDataOutput> {
return getOperationalAlerts.execute().map {
return getPendingAlerts.execute().map {
PendingAlertDataOutput.fromPendingAlert(it)
}
}

@PutMapping(value = ["/{id}/validate"])
@Operation(summary = "Validate an operational alert")
@Operation(summary = "Validate a pending operational alert")
fun validateAlert(
@PathParam("Alert id")
@PathVariable(name = "id")
id: Int,
) {
return validateOperationalAlert.execute(id)
return validatePendingAlert.execute(id)
}

@PutMapping(value = ["/{id}/silence"], consumes = ["application/json"])
@Operation(summary = "Silence an operational alert")
@Operation(summary = "Silence a pending operational alert")
fun silenceAlert(
@PathParam("Alert id")
@PathVariable(name = "id")
id: Int,
@RequestBody
silenceOperationalAlertData: SilenceOperationalAlertDataInput,
): SilencedAlertDataOutput {
val silencedAlert = silenceOperationalAlert.execute(
val silencedAlert = silencePendingAlert.execute(
id,
silenceOperationalAlertData.silencedAlertPeriod,
silenceOperationalAlertData.beforeDateTime,
Expand All @@ -71,6 +75,17 @@ class OperationalAlertController(
@PathVariable(name = "id")
id: Int,
) {
return deleteSilencedOperationalAlert.execute(id)
return deleteSilencedAlert.execute(id)
}

@PostMapping(value = ["/silenced"], consumes = ["application/json"])
@Operation(summary = "Silence an operational alert")
fun silenceAlert(
@RequestBody
silenceAlertData: SilencedAlertDataInput,
): SilencedAlertDataOutput {
val silencedAlert = silenceAlert.execute(silenceAlertData.toSilencedAlert(objectMapper))

return SilencedAlertDataOutput.fromSilencedAlert(silencedAlert)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package fr.gouv.cnsp.monitorfish.infrastructure.api.input

import com.fasterxml.jackson.databind.ObjectMapper
import com.neovisionaries.i18n.CountryCode
import fr.gouv.cnsp.monitorfish.domain.entities.alerts.SilencedAlert
import fr.gouv.cnsp.monitorfish.domain.entities.alerts.type.AlertType
import fr.gouv.cnsp.monitorfish.domain.entities.vessel.VesselIdentifier
import java.time.ZonedDateTime

class SilencedAlertDataInput(
val vesselId: Int? = null,
val vesselName: String? = null,
val internalReferenceNumber: String? = null,
val externalReferenceNumber: String? = null,
val ircs: String? = null,
val vesselIdentifier: VesselIdentifier,
val flagState: CountryCode,
val silencedBeforeDate: ZonedDateTime,
val value: String,
) {
fun toSilencedAlert(objectMapper: ObjectMapper) = SilencedAlert(
vesselId = this.vesselId,
vesselName = this.vesselName,
internalReferenceNumber = this.internalReferenceNumber,
externalReferenceNumber = this.externalReferenceNumber,
ircs = this.ircs,
vesselIdentifier = this.vesselIdentifier,
flagState = this.flagState,
silencedBeforeDate = this.silencedBeforeDate,
value = objectMapper.readValue(value, AlertType::class.java),
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import java.time.ZonedDateTime

class SilencedAlertDataOutput(
val id: Int? = null,
val vesselId: Int? = null,
val vesselName: String? = null,
val internalReferenceNumber: String? = null,
val externalReferenceNumber: String? = null,
Expand All @@ -20,6 +21,7 @@ class SilencedAlertDataOutput(
companion object {
fun fromSilencedAlert(silencedAlert: SilencedAlert) = SilencedAlertDataOutput(
id = silencedAlert.id,
vesselId = silencedAlert.vesselId,
vesselName = silencedAlert.vesselName,
internalReferenceNumber = silencedAlert.internalReferenceNumber,
externalReferenceNumber = silencedAlert.externalReferenceNumber,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package fr.gouv.cnsp.monitorfish.infrastructure.api.public_api

import fr.gouv.cnsp.monitorfish.domain.use_cases.alert.ValidateOperationalAlert
import fr.gouv.cnsp.monitorfish.domain.use_cases.alert.ValidatePendingAlert
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.tags.Tag
import jakarta.websocket.server.PathParam
Expand All @@ -13,7 +13,7 @@ import org.springframework.web.bind.annotation.RestController
@RequestMapping("/api/v1/operational_alerts")
@Tag(name = "Public APIs for Operational alerts")
class PublicOperationalAlertController(
private val validateOperationalAlert: ValidateOperationalAlert,
private val validatePendingAlert: ValidatePendingAlert,
) {

@PutMapping(value = ["/{id}/validate"])
Expand All @@ -23,6 +23,6 @@ class PublicOperationalAlertController(
@PathVariable(name = "id")
id: Int,
) {
return validateOperationalAlert.execute(id)
return validatePendingAlert.execute(id)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ data class SilencedAlertEntity(
@Basic(optional = false)
@Column(name = "id", unique = true)
val id: Int? = null,
@Column(name = "vessel_id")
val vesselId: Int? = null,
@Column(name = "vessel_name")
val vesselName: String? = null,
@Column(name = "internal_reference_number")
Expand Down Expand Up @@ -55,6 +57,7 @@ data class SilencedAlertEntity(
flagState = flagState,
silencedBeforeDate = silencedBeforeDate,
value = mapper.readValue(value, AlertType::class.java),
vesselId = vesselId,
wasValidated = wasValidated,
)
}
Expand All @@ -74,7 +77,24 @@ data class SilencedAlertEntity(
flagState = alert.flagState,
silencedBeforeDate = silencedBeforeDate,
value = mapper.writeValueAsString(alert.value),
vesselId = alert.vesselId,
wasValidated = isValidated,
)

fun fromSilencedAlert(
mapper: ObjectMapper,
silencedAlert: SilencedAlert,
) = SilencedAlertEntity(
vesselName = silencedAlert.vesselName,
internalReferenceNumber = silencedAlert.internalReferenceNumber,
externalReferenceNumber = silencedAlert.externalReferenceNumber,
ircs = silencedAlert.ircs,
vesselIdentifier = silencedAlert.vesselIdentifier,
flagState = silencedAlert.flagState,
silencedBeforeDate = silencedAlert.silencedBeforeDate,
value = mapper.writeValueAsString(silencedAlert.value),
vesselId = silencedAlert.vesselId,
wasValidated = silencedAlert.wasValidated,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ class JpaSilencedAlertRepository(
).toSilencedAlert(mapper)
}

override fun save(
silencedAlert: SilencedAlert,
): SilencedAlert {
return dbSilencedAlertRepository.save(
SilencedAlertEntity.fromSilencedAlert(mapper, silencedAlert),
).toSilencedAlert(mapper)
}

override fun findAllCurrentSilencedAlerts(): List<SilencedAlert> {
val now = ZonedDateTime.now()
return dbSilencedAlertRepository.findAllBySilencedBeforeDateAfter(now).map { it.toSilencedAlert(mapper) }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE public.silenced_alerts
ADD COLUMN vessel_id integer;
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import fr.gouv.cnsp.monitorfish.domain.entities.mission_actions.InfractionCatego
import fr.gouv.cnsp.monitorfish.domain.entities.vessel.VesselIdentifier
import fr.gouv.cnsp.monitorfish.domain.repositories.InfractionRepository
import fr.gouv.cnsp.monitorfish.domain.repositories.PendingAlertRepository
import fr.gouv.cnsp.monitorfish.domain.use_cases.alert.GetOperationalAlerts
import fr.gouv.cnsp.monitorfish.domain.use_cases.alert.GetPendingAlerts
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
Expand All @@ -22,7 +22,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension
import java.time.ZonedDateTime

@ExtendWith(SpringExtension::class)
class GetOperationalAlertsUTests {
class GetPendingAlertsUTests {

@MockBean
private lateinit var pendingAlertRepository: PendingAlertRepository
Expand Down Expand Up @@ -50,7 +50,7 @@ class GetOperationalAlertsUTests {
given(pendingAlertRepository.findAlertsOfTypes(any())).willReturn(listOf(pendingAlert))

// When
val alerts = GetOperationalAlerts(pendingAlertRepository, infractionRepository).execute()
val alerts = GetPendingAlerts(pendingAlertRepository, infractionRepository).execute()

// Then
assertThat(alerts.first().value.natinfCode).isEqualTo(7059)
Expand Down
Loading

0 comments on commit 9bb8b8f

Please sign in to comment.