Skip to content

Commit

Permalink
Préavis – Invalider les préavis (#3484)
Browse files Browse the repository at this point in the history
## Linked issues

- Resolve #3428
----

- [x] Tests E2E (Cypress)
  • Loading branch information
louptheron authored Aug 5, 2024
2 parents d8dc4d8 + d103248 commit 63aeee1
Show file tree
Hide file tree
Showing 57 changed files with 762 additions and 276 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,10 @@ data class LogbookMessage(
return referencedReportId ?: reportId
}

fun <T : LogbookMessageValue> toConsolidatedLogbookMessageTyped(
fun <T : LogbookMessageValue> toConsolidatedLogbookMessageAndValue(
relatedLogbookMessages: List<LogbookMessage>,
clazz: Class<T>,
): LogbookMessageTyped<T> {
): LogbookMessageAndValue<T> {
if (reportId == null) {
throw EntityConversionException(
"Logbook report $id has no `reportId`. You can only enrich a DAT or an orphan COR operation with a `reportId`.",
Expand All @@ -77,7 +77,7 @@ data class LogbookMessage(
isDeleted = historicallySortedRelatedLogbookMessages.any { it.operationType == LogbookOperationType.DEL },
)

return LogbookMessageTyped(
return LogbookMessageAndValue(
logbookMessage = finalLogbookMessage,
clazz = clazz,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ package fr.gouv.cnsp.monitorfish.domain.entities.logbook
import fr.gouv.cnsp.monitorfish.domain.entities.logbook.messages.LogbookMessageValue
import fr.gouv.cnsp.monitorfish.domain.exceptions.EntityConversionException

class LogbookMessageTyped<T : LogbookMessageValue>(
class LogbookMessageAndValue<T : LogbookMessageValue>(
/** Logbook report DAT operation, or last COR one if any, enriched with RET & DEL information if any. */
val logbookMessage: LogbookMessage,
private val clazz: Class<T>,
) {
val typedMessage: T
val value: T
get() = if (clazz.isInstance(logbookMessage.message)) {
clazz.cast(logbookMessage.message)
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class PNO() : LogbookMessageValue {
* Is it a prior notification requiring a manual verification?
*
* It should stay `true` even after the manual verification is done (`isVerified == true`)
* to differanciate mandatory-verification prior notifications from non-mandatory-verification prior notifications.
* to differentiate mandatory-verification prior notifications from non-mandatory-verification prior notifications.
*
* # Example
*
Expand All @@ -43,6 +43,7 @@ class PNO() : LogbookMessageValue {
var isVerified: Boolean? = null
var isBeingSent: Boolean? = null
var isSent: Boolean? = null
var isInvalidated: Boolean? = null
var latitude: Double? = null
var longitude: Double? = null
var note: String? = null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package fr.gouv.cnsp.monitorfish.domain.entities.prior_notification

import fr.gouv.cnsp.monitorfish.domain.entities.facade.Seafront
import fr.gouv.cnsp.monitorfish.domain.entities.gear.Gear
import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookMessageTyped
import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookMessageAndValue
import fr.gouv.cnsp.monitorfish.domain.entities.logbook.messages.PNO
import fr.gouv.cnsp.monitorfish.domain.entities.port.Port
import fr.gouv.cnsp.monitorfish.domain.entities.reporting.ReportingType
Expand All @@ -25,7 +25,7 @@ data class PriorNotification(
val createdAt: ZonedDateTime?,
val didNotFishAfterZeroNotice: Boolean,
val isManuallyCreated: Boolean,
var logbookMessageTyped: LogbookMessageTyped<PNO>,
var logbookMessageAndValue: LogbookMessageAndValue<PNO>,
var port: Port?,
var reportingCount: Int?,
var seafront: Seafront?,
Expand All @@ -43,7 +43,7 @@ data class PriorNotification(
* See /adrs/0006-prior-notification-states-specifications.md for more details.
*/
get() = run {
val pnoMessage = logbookMessageTyped.typedMessage
val pnoMessage = logbookMessageAndValue.value

val isInVerificationScope = pnoMessage.isInVerificationScope
val isVerified = pnoMessage.isVerified
Expand Down Expand Up @@ -79,8 +79,8 @@ data class PriorNotification(
allVessels: List<Vessel>,
isManuallyCreated: Boolean,
) {
val logbookMessage = logbookMessageTyped.logbookMessage
val pnoMessage = logbookMessageTyped.typedMessage
val logbookMessage = logbookMessageAndValue.logbookMessage
val pnoMessage = logbookMessageAndValue.value

port = try {
pnoMessage.port?.let { portLocode ->
Expand Down Expand Up @@ -121,7 +121,7 @@ data class PriorNotification(
allSpecies: List<Species>,
logbookRawMessageRepository: LogbookRawMessageRepository,
) {
val logbookMessage = logbookMessageTyped.logbookMessage
val logbookMessage = logbookMessageAndValue.logbookMessage
val logbookMessageWithRawMessage = logbookMessage.operationNumber?.let { operationNumber ->
logbookMessage.copy(
rawMessage = try {
Expand All @@ -135,7 +135,7 @@ data class PriorNotification(
} ?: logbookMessage
logbookMessageWithRawMessage.enrichGearPortAndSpecyNames(allGears, allPorts, allSpecies)

logbookMessageTyped = LogbookMessageTyped(
logbookMessageAndValue = LogbookMessageAndValue(
logbookMessageWithRawMessage,
PNO::class.java,
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package fr.gouv.cnsp.monitorfish.domain.repositories

import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookMessage
import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookMessageTyped
import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookMessageAndValue
import fr.gouv.cnsp.monitorfish.domain.entities.logbook.VoyageDatesAndTripNumber
import fr.gouv.cnsp.monitorfish.domain.entities.logbook.messages.PNO
import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.PriorNotification
Expand Down Expand Up @@ -64,7 +64,7 @@ interface LogbookReportRepository {
// Only used in tests
fun save(message: LogbookMessage)

fun savePriorNotification(logbookMessageTyped: LogbookMessageTyped<PNO>): PriorNotification
fun savePriorNotification(logbookMessageAndValue: LogbookMessageAndValue<PNO>): PriorNotification

fun updatePriorNotificationState(
reportId: String,
Expand All @@ -77,6 +77,8 @@ interface LogbookReportRepository {

fun updatePriorNotificationNote(reportId: String, operationDate: ZonedDateTime, note: String?)

fun invalidate(reportId: String, operationDate: ZonedDateTime)

// For test purpose
fun deleteAll()
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,7 @@ interface ManualPriorNotificationRepository {

fun updateState(reportId: String, isBeingSent: Boolean, isVerified: Boolean)

fun invalidate(reportId: String)

fun findAllToVerify(): List<PriorNotification>
}
Original file line number Diff line number Diff line change
Expand Up @@ -111,14 +111,14 @@ class CreateOrUpdateManualPriorNotification(
tripGears = tripGears,
tripSegments = tripSegments,
)
val logbookMessageTyped = LogbookMessageTyped(pnoLogbookMessage, PNO::class.java)
val logbookMessageAndValue = LogbookMessageAndValue(pnoLogbookMessage, PNO::class.java)

val newOrNextPriorNotification = PriorNotification(
reportId = reportId,
authorTrigram = authorTrigram,
didNotFishAfterZeroNotice = didNotFishAfterZeroNotice,
isManuallyCreated = true,
logbookMessageTyped = logbookMessageTyped,
logbookMessageAndValue = logbookMessageAndValue,
sentAt = sentAt,

// All these props are useless for the save operation.
Expand All @@ -143,7 +143,7 @@ class CreateOrUpdateManualPriorNotification(
manualPriorNotificationRepository.save(newOrNextPriorNotification)
val createdOrUpdatedPriorNotification = getPriorNotification.execute(
createdOrUpdatedIncompletePriorNotification.reportId!!,
createdOrUpdatedIncompletePriorNotification.logbookMessageTyped.logbookMessage.operationDateTime,
createdOrUpdatedIncompletePriorNotification.logbookMessageAndValue.logbookMessage.operationDateTime,
true,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class GetNumberToVerify(
val incompletePriorNotifications = automaticPriorNotifications + manualPriorNotifications

val undeletedPriorNotifications = incompletePriorNotifications
.filter { !it.logbookMessageTyped.logbookMessage.isDeleted }
.filter { !it.logbookMessageAndValue.logbookMessage.isDeleted }

val priorNotifications = undeletedPriorNotifications
.map { priorNotification ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,12 @@ class GetPriorNotifications(
val incompletePriorNotifications = automaticPriorNotifications + manualPriorNotifications

val undeletedPriorNotifications = incompletePriorNotifications
.filter { !it.logbookMessageTyped.logbookMessage.isDeleted }
.filter { !it.logbookMessageAndValue.logbookMessage.isDeleted }

val priorNotifications = undeletedPriorNotifications
.map { priorNotification ->
priorNotification.enrich(allPorts, allRiskFactors, allVessels, priorNotification.isManuallyCreated)
priorNotification.logbookMessageTyped.logbookMessage
priorNotification.logbookMessageAndValue.logbookMessage
.enrichGearPortAndSpecyNames(allGears, allPorts, allSpecies)

priorNotification
Expand All @@ -57,14 +57,14 @@ class GetPriorNotifications(
Sort.Direction.ASC -> priorNotifications.sortedWith(
compareBy(
{ getSortKey(it, sortColumn) },
{ it.logbookMessageTyped.logbookMessage.id }, // Tie-breaker
{ it.logbookMessageAndValue.logbookMessage.id }, // Tie-breaker
),
)

Sort.Direction.DESC -> priorNotifications.sortedWith(
// Only solution found to fix typing issues
compareByDescending<PriorNotification> { getSortKey(it, sortColumn) }
.thenByDescending { it.logbookMessageTyped.logbookMessage.id }, // Tie-breaker
.thenByDescending { it.logbookMessageAndValue.logbookMessage.id }, // Tie-breaker
)
}.filter { seafrontGroup.hasSeafront(it.seafront) && (states.isNullOrEmpty() || states.contains(it.state)) }

Expand Down Expand Up @@ -99,11 +99,11 @@ class GetPriorNotifications(
sortColumn: PriorNotificationsSortColumn,
): Comparable<*>? {
return when (sortColumn) {
PriorNotificationsSortColumn.EXPECTED_ARRIVAL_DATE -> priorNotification.logbookMessageTyped.typedMessage.predictedArrivalDatetimeUtc
PriorNotificationsSortColumn.EXPECTED_LANDING_DATE -> priorNotification.logbookMessageTyped.typedMessage.predictedLandingDatetimeUtc
PriorNotificationsSortColumn.EXPECTED_ARRIVAL_DATE -> priorNotification.logbookMessageAndValue.value.predictedArrivalDatetimeUtc
PriorNotificationsSortColumn.EXPECTED_LANDING_DATE -> priorNotification.logbookMessageAndValue.value.predictedLandingDatetimeUtc
PriorNotificationsSortColumn.PORT_NAME -> priorNotification.port?.name
PriorNotificationsSortColumn.VESSEL_NAME -> priorNotification.logbookMessageTyped.logbookMessage.vesselName
PriorNotificationsSortColumn.VESSEL_RISK_FACTOR -> priorNotification.logbookMessageTyped.typedMessage.riskFactor
PriorNotificationsSortColumn.VESSEL_NAME -> priorNotification.logbookMessageAndValue.logbookMessage.vesselName
PriorNotificationsSortColumn.VESSEL_RISK_FACTOR -> priorNotification.logbookMessageAndValue.value.riskFactor
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package fr.gouv.cnsp.monitorfish.domain.use_cases.prior_notification

import fr.gouv.cnsp.monitorfish.config.UseCase
import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.PriorNotification
import fr.gouv.cnsp.monitorfish.domain.repositories.LogbookReportRepository
import fr.gouv.cnsp.monitorfish.domain.repositories.ManualPriorNotificationRepository
import java.time.ZonedDateTime

@UseCase
class InvalidatePriorNotification(
private val logbookReportRepository: LogbookReportRepository,
private val manualPriorNotificationRepository: ManualPriorNotificationRepository,
private val getPriorNotification: GetPriorNotification,
) {
fun execute(reportId: String, operationDate: ZonedDateTime, isManuallyCreated: Boolean): PriorNotification {
if (isManuallyCreated) {
manualPriorNotificationRepository.invalidate(reportId)
} else {
logbookReportRepository.invalidate(reportId, operationDate)
}

return getPriorNotification.execute(reportId, operationDate, isManuallyCreated)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class PriorNotificationController(
private val getPriorNotificationTypes: GetPriorNotificationTypes,
private val updatePriorNotificationNote: UpdatePriorNotificationNote,
private val verifyAndSendPriorNotification: VerifyAndSendPriorNotification,
private val invalidatePriorNotification: InvalidatePriorNotification,
) {
@GetMapping("")
@Operation(summary = "Get all prior notifications")
Expand Down Expand Up @@ -290,4 +291,26 @@ class PriorNotificationController(

return PriorNotificationDetailDataOutput.fromPriorNotification(updatedPriorNotification)
}

@PutMapping("/{reportId}/invalidate")
@Operation(summary = "Invalidate a prior notification by its `reportId`")
fun invalidate(
@PathParam("Logbook message `reportId`")
@PathVariable(name = "reportId")
reportId: String,
@Parameter(description = "Operation date (to optimize SQL query via Timescale).")
@RequestParam(name = "operationDate")
operationDate: ZonedDateTime,
@Parameter(description = "Is the prior notification manually created?")
@RequestParam(name = "isManuallyCreated")
isManuallyCreated: Boolean,
): PriorNotificationDetailDataOutput {
val updatedPriorNotification = invalidatePriorNotification.execute(
reportId = reportId,
operationDate = operationDate,
isManuallyCreated = isManuallyCreated,
)

return PriorNotificationDetailDataOutput.fromPriorNotification(updatedPriorNotification)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ data class ManualPriorNotificationDataOutput(
) {
companion object {
fun fromPriorNotification(priorNotification: PriorNotification): ManualPriorNotificationDataOutput {
val logbookMessage = priorNotification.logbookMessageTyped.logbookMessage
val pnoMessage = priorNotification.logbookMessageTyped.typedMessage
val logbookMessage = priorNotification.logbookMessageAndValue.logbookMessage
val pnoMessage = priorNotification.logbookMessageAndValue.value

val authorTrigram = requireNotNull(priorNotification.authorTrigram) {
"`priorNotification.authorTrigram` is null."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class PriorNotificationDetailDataOutput(
val isVesselUnderCharter = requireNotNull(priorNotification.vessel) {
"`priorNotification.vessel` is null."
}.underCharter
val logbookMessage = priorNotification.logbookMessageTyped.logbookMessage
val logbookMessage = priorNotification.logbookMessageAndValue.logbookMessage
val referenceReportId = requireNotNull(logbookMessage.getReferenceReportId()) {
"`logbookMessage.getReferenceReportId()` returned null."
}
Expand All @@ -36,7 +36,7 @@ class PriorNotificationDetailDataOutput(
isVesselUnderCharter,
logbookMessage = logbookMessageDataOutput,
state = priorNotification.state,
riskFactor = priorNotification.logbookMessageTyped.typedMessage.riskFactor,
riskFactor = priorNotification.logbookMessageAndValue.value.riskFactor,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ data class PriorNotificationListItemDataOutput(
val isCorrection: Boolean,
val isManuallyCreated: Boolean = false,
val isVesselUnderCharter: Boolean?,
val isInvalidated: Boolean? = false,
val onBoardCatches: List<LogbookMessageFishingCatchDataOutput>,
// Used to optimize SQL query via Timescale when fetching a single prior notification from the list
val operationDate: ZonedDateTime,
Expand Down Expand Up @@ -50,14 +51,14 @@ data class PriorNotificationListItemDataOutput(
val logger: Logger = LoggerFactory.getLogger(PriorNotificationListItemDataOutput::class.java)

fun fromPriorNotification(priorNotification: PriorNotification): PriorNotificationListItemDataOutput? {
val logbookMessage = priorNotification.logbookMessageTyped.logbookMessage
val logbookMessage = priorNotification.logbookMessageAndValue.logbookMessage
val referenceReportId = logbookMessage.getReferenceReportId()
if (referenceReportId == null) {
logger.warn("Prior notification has neither `reportId` nor `referencedReportId`: $priorNotification.")

return null
}
val message = priorNotification.logbookMessageTyped.typedMessage
val message = priorNotification.logbookMessageAndValue.value

val acknowledgment = logbookMessage.acknowledgment?.let { AcknowledgmentDataOutput.fromAcknowledgment(it) }
val onBoardCatches = message.catchOnboard
Expand All @@ -80,6 +81,7 @@ data class PriorNotificationListItemDataOutput(
fingerprint = priorNotification.fingerprint,
isCorrection = logbookMessage.operationType === LogbookOperationType.COR,
isManuallyCreated = priorNotification.isManuallyCreated,
isInvalidated = message.isInvalidated,
isVesselUnderCharter = vessel.underCharter,
onBoardCatches = onBoardCatches,
operationDate = logbookMessage.operationDateTime,
Expand All @@ -103,7 +105,7 @@ data class PriorNotificationListItemDataOutput(
vesselLength = vessel.length,
vesselMmsi = vessel.mmsi,
vesselName = vessel.vesselName,
riskFactor = priorNotification.logbookMessageTyped.typedMessage.riskFactor,
riskFactor = priorNotification.logbookMessageAndValue.value.riskFactor,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class SentryController(val sentryConfig: SentryConfig) {
@GetMapping("/trigger_sentry_error")
fun triggerError(): Map<String, Boolean?> {
try {
throw Exception("Sentry test error triggered from get request.")
throw IllegalArgumentException("Sentry test error triggered from get request.")
} catch (e: Exception) {
logger.error(e.message, e)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,8 @@ data class LogbookReportEntity(
val relatedLogbookMessages = relatedModels
.map { it.toLogbookMessage(mapper) }
.sortedBy { it.operationDateTime }
val consolidatedLogbookMessageTyped = referenceLogbookMessage
.toConsolidatedLogbookMessageTyped(relatedLogbookMessages, PNO::class.java)
val consolidatedLogbookMessageAndValue = referenceLogbookMessage
.toConsolidatedLogbookMessageAndValue(relatedLogbookMessages, PNO::class.java)
val updatedAt = relatedLogbookMessages.lastOrNull()?.operationDateTime ?: operationDateTime.atZone(UTC)
// For practical reasons `vessel` can't be `null`, so we temporarily set it to "Navire inconnu"
val vessel = UNKNOWN_VESSEL
Expand All @@ -154,8 +154,8 @@ data class LogbookReportEntity(
createdAt = operationDateTime.atZone(UTC),
didNotFishAfterZeroNotice = false,
isManuallyCreated = false,
logbookMessageTyped = consolidatedLogbookMessageTyped,
sentAt = consolidatedLogbookMessageTyped.logbookMessage.reportDateTime,
logbookMessageAndValue = consolidatedLogbookMessageAndValue,
sentAt = consolidatedLogbookMessageAndValue.logbookMessage.reportDateTime,
updatedAt = updatedAt,

// These props need to be calculated in the use case
Expand Down
Loading

0 comments on commit 63aeee1

Please sign in to comment.