Skip to content

Commit

Permalink
Préavis - Ajouter dans le formulaire -12 m la possibilité de joindre …
Browse files Browse the repository at this point in the history
…des fichiers (#3384)

## Linked issues

- Resolve #3320

----

- [ ] Tests E2E (Cypress)
  • Loading branch information
ivangabriele committed Sep 6, 2024
2 parents bef94e6 + e471896 commit a21dd8e
Show file tree
Hide file tree
Showing 30 changed files with 912 additions and 44 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package fr.gouv.cnsp.monitorfish.domain.entities.prior_notification

import fr.gouv.cnsp.monitorfish.utils.CustomZonedDateTime

data class PriorNotificationDocument(
val id: String?,
val content: ByteArray,
val createdAt: CustomZonedDateTime,
val fileName: String,
val isManualPriorNotification: Boolean,
val mimeType: String,
val reportId: String,
val updatedAt: CustomZonedDateTime,
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false

other as PriorNotificationDocument

if (id != other.id) return false
if (!content.contentEquals(other.content)) return false
if (createdAt != other.createdAt) return false
if (fileName != other.fileName) return false
if (isManualPriorNotification != other.isManualPriorNotification) return false
if (mimeType != other.mimeType) return false
if (reportId != other.reportId) return false
if (updatedAt != other.updatedAt) return false

return true
}

override fun hashCode(): Int {
var result = id?.hashCode() ?: 0
result = 31 * result + content.contentHashCode()
result = 31 * result + createdAt.hashCode()
result = 31 * result + fileName.hashCode()
result = 31 * result + isManualPriorNotification.hashCode()
result = 31 * result + mimeType.hashCode()
result = 31 * result + reportId.hashCode()
result = 31 * result + updatedAt.hashCode()
return result
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package fr.gouv.cnsp.monitorfish.domain.entities.prior_notification

import java.time.ZonedDateTime

data class PriorNotificationIdentifier(
val reportId: String,
val operationDate: ZonedDateTime,
val isManualPriorNotification: Boolean,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package fr.gouv.cnsp.monitorfish.domain.repositories

import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.PriorNotificationDocument

interface PriorNotificationUploadRepository {
fun findAllByReportId(reportId: String): List<PriorNotificationDocument>

fun findById(id: String): PriorNotificationDocument

fun save(newPriorNotificationDocument: PriorNotificationDocument): PriorNotificationDocument

fun deleteById(id: String)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
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.PriorNotificationDocument
import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.PriorNotificationIdentifier
import fr.gouv.cnsp.monitorfish.domain.repositories.LogbookReportRepository
import fr.gouv.cnsp.monitorfish.domain.repositories.ManualPriorNotificationRepository
import fr.gouv.cnsp.monitorfish.domain.repositories.PriorNotificationUploadRepository
import fr.gouv.cnsp.monitorfish.utils.CustomZonedDateTime

@UseCase
class CreatePriorNotificationUpload(
private val logbookReportRepository: LogbookReportRepository,
private val manualPriorNotificationRepository: ManualPriorNotificationRepository,
private val priorNotificationUploadRepository: PriorNotificationUploadRepository,
) {
fun execute(identifier: PriorNotificationIdentifier, content: ByteArray, fileName: String, mimeType: String) {
val createdAt = CustomZonedDateTime.now()

val newPriorNotificationDocument = PriorNotificationDocument(
id = null,
content = content,
createdAt = createdAt,
fileName = fileName,
isManualPriorNotification = identifier.isManualPriorNotification,
mimeType = mimeType,
reportId = identifier.reportId,
updatedAt = createdAt,
)

priorNotificationUploadRepository.save(newPriorNotificationDocument)

if (identifier.isManualPriorNotification) {
manualPriorNotificationRepository.updateState(
reportId = identifier.reportId,
isBeingSent = false,
isSent = false,
isVerified = false,
)
} else {
logbookReportRepository.updatePriorNotificationState(
reportId = identifier.reportId,
operationDate = identifier.operationDate,
isBeingSent = false,
isSent = false,
isVerified = false,
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
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.PriorNotificationIdentifier
import fr.gouv.cnsp.monitorfish.domain.repositories.LogbookReportRepository
import fr.gouv.cnsp.monitorfish.domain.repositories.ManualPriorNotificationRepository
import fr.gouv.cnsp.monitorfish.domain.repositories.PriorNotificationUploadRepository

@UseCase
class DeletePriorNotificationUpload(
private val logbookReportRepository: LogbookReportRepository,
private val manualPriorNotificationRepository: ManualPriorNotificationRepository,
private val priorNotificationUploadRepository: PriorNotificationUploadRepository,
) {
fun execute(identifier: PriorNotificationIdentifier, priorNotificationUploadId: String) {
priorNotificationUploadRepository.deleteById(priorNotificationUploadId)

if (identifier.isManualPriorNotification) {
manualPriorNotificationRepository.updateState(
reportId = identifier.reportId,
isBeingSent = false,
isSent = false,
isVerified = false,
)
} else {
logbookReportRepository.updatePriorNotificationState(
reportId = identifier.reportId,
operationDate = identifier.operationDate,
isBeingSent = false,
isSent = false,
isVerified = false,
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
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.PriorNotificationDocument
import fr.gouv.cnsp.monitorfish.domain.repositories.PriorNotificationUploadRepository

@UseCase
class GetPriorNotificationUpload(
private val priorNotificationUploadRepository: PriorNotificationUploadRepository,
) {
fun execute(priorNotificationUploadId: String): PriorNotificationDocument {
return priorNotificationUploadRepository.findById(priorNotificationUploadId)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
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.PriorNotificationDocument
import fr.gouv.cnsp.monitorfish.domain.repositories.PriorNotificationUploadRepository

@UseCase
class GetPriorNotificationUploads(
private val priorNotificationUploadRepository: PriorNotificationUploadRepository,
) {
fun execute(reportId: String): List<PriorNotificationDocument> {
return priorNotificationUploadRepository.findAllByReportId(reportId)
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package fr.gouv.cnsp.monitorfish.infrastructure.api.bff

import fr.gouv.cnsp.monitorfish.domain.entities.facade.SeafrontGroup
import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.PriorNotificationIdentifier
import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.PriorNotificationState
import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.filters.PriorNotificationsFilter
import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.sorters.PriorNotificationsSortColumn
Expand All @@ -9,6 +10,8 @@ import fr.gouv.cnsp.monitorfish.infrastructure.api.input.LogbookPriorNotificatio
import fr.gouv.cnsp.monitorfish.infrastructure.api.input.ManualPriorNotificationComputeDataInput
import fr.gouv.cnsp.monitorfish.infrastructure.api.input.ManualPriorNotificationFormDataInput
import fr.gouv.cnsp.monitorfish.infrastructure.api.outputs.*
import fr.gouv.cnsp.monitorfish.infrastructure.exceptions.BackendRequestErrorCode
import fr.gouv.cnsp.monitorfish.infrastructure.exceptions.BackendRequestException
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.Parameter
import io.swagger.v3.oas.annotations.tags.Tag
Expand All @@ -19,6 +22,7 @@ import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*
import org.springframework.web.multipart.MultipartFile
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter.ISO_DATE_TIME

Expand All @@ -28,14 +32,18 @@ import java.time.format.DateTimeFormatter.ISO_DATE_TIME
class PriorNotificationController(
private val computeManualPriorNotification: ComputeManualPriorNotification,
private val createOrUpdateManualPriorNotification: CreateOrUpdateManualPriorNotification,
private val createPriorNotificationUpload: CreatePriorNotificationUpload,
private val deletePriorNotificationUpload: DeletePriorNotificationUpload,
private val getPriorNotification: GetPriorNotification,
private val getPriorNotificationPdfDocument: GetPriorNotificationPdfDocument,
private val getPriorNotificationUpload: GetPriorNotificationUpload,
private val getPriorNotificationUploads: GetPriorNotificationUploads,
private val getPriorNotifications: GetPriorNotifications,
private val getNumberToVerify: GetNumberToVerify,
private val getPriorNotificationTypes: GetPriorNotificationTypes,
private val invalidatePriorNotification: InvalidatePriorNotification,
private val updateLogbookPriorNotification: UpdateLogbookPriorNotification,
private val verifyAndSendPriorNotification: VerifyAndSendPriorNotification,
private val invalidatePriorNotification: InvalidatePriorNotification,
) {
data class Status(val status: String)

Expand Down Expand Up @@ -282,7 +290,7 @@ class PriorNotificationController(
}

@GetMapping("/{reportId}/pdf")
@Operation(summary = "Get the PDF document")
@Operation(summary = "Get the PNO PDF document")
fun getPdfDocument(
@PathParam("Logbook message `reportId`")
@PathVariable(name = "reportId")
Expand Down Expand Up @@ -357,4 +365,87 @@ class PriorNotificationController(

return PriorNotificationDataOutput.fromPriorNotification(updatedPriorNotification)
}

@GetMapping("/{reportId}/uploads/{priorNotificationUploadId}")
@Operation(summary = "Download a prior notification attachment")
fun getUploads(
@PathParam("Logbook message `reportId`")
@PathVariable(name = "reportId")
reportId: String,
@PathParam("Prior notification upload ID`")
@PathVariable(name = "priorNotificationUploadId")
priorNotificationUploadId: String,
): ResponseEntity<ByteArray> {
val document = getPriorNotificationUpload.execute(priorNotificationUploadId)

val headers = HttpHeaders().apply {
contentType = MediaType.parseMediaType(document.mimeType)
setContentDispositionFormData("attachment", document.fileName)
}

return ResponseEntity(document.content, headers, HttpStatus.OK)
}

@GetMapping("/{reportId}/uploads")
@Operation(summary = "Get all the attachment documents for a given prior notification")
fun getUploads(
@PathParam("Logbook message `reportId`")
@PathVariable(name = "reportId")
reportId: String,
): List<PriorNotificationUploadDataOutput> {
return getPriorNotificationUploads.execute(reportId)
.map { PriorNotificationUploadDataOutput.fromPriorNotificationDocument(it) }
}

@PostMapping("/{reportId}/uploads")
@Operation(summary = "Attach a document to a prior notification")
fun createUpload(
@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 = "isManualPriorNotification")
isManualPriorNotification: Boolean,
@RequestParam("file")
file: MultipartFile,
): ResponseEntity<*> {
val content = file.bytes
?: throw BackendRequestException(BackendRequestErrorCode.EMPTY_UPLOADED_FILE)
val fileName = file.originalFilename
?: throw BackendRequestException(BackendRequestErrorCode.MISSING_UPLOADED_FILE_NAME)
val mimeType = file.contentType
?: throw BackendRequestException(BackendRequestErrorCode.MISSING_UPLOADED_FILE_TYPE)

val identifier = PriorNotificationIdentifier(reportId, operationDate, isManualPriorNotification)

createPriorNotificationUpload.execute(identifier, content, fileName, mimeType)

return ResponseEntity("File uploaded successfully: " + file.originalFilename, HttpStatus.OK)
}

@DeleteMapping("/{reportId}/uploads/{priorNotificationUploadId}")
@Operation(summary = "Delete a prior notification attachment")
fun deleteUpload(
@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 = "isManualPriorNotification")
isManualPriorNotification: Boolean,
@PathParam("Prior notification upload ID")
@PathVariable(name = "priorNotificationUploadId")
priorNotificationUploadId: String,
): ResponseEntity<Unit> {
val identifier = PriorNotificationIdentifier(reportId, operationDate, isManualPriorNotification)

deletePriorNotificationUpload.execute(identifier, priorNotificationUploadId)

return ResponseEntity(HttpStatus.NO_CONTENT)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package fr.gouv.cnsp.monitorfish.infrastructure.api.outputs

import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.PriorNotificationDocument
import fr.gouv.cnsp.monitorfish.utils.CustomZonedDateTime

class PriorNotificationUploadDataOutput(
val id: String,
val createdAt: CustomZonedDateTime,
val fileName: String,
val isManualPriorNotification: Boolean,
val mimeType: String,
val reportId: String,
val updatedAt: CustomZonedDateTime,
) {
companion object {
fun fromPriorNotificationDocument(priorNotificationDocument: PriorNotificationDocument): PriorNotificationUploadDataOutput {
val id = requireNotNull(priorNotificationDocument.id) {
"`id` is null."
}

return PriorNotificationUploadDataOutput(
id = id,
createdAt = priorNotificationDocument.createdAt,
fileName = priorNotificationDocument.fileName,
isManualPriorNotification = priorNotificationDocument.isManualPriorNotification,
mimeType = priorNotificationDocument.mimeType,
reportId = priorNotificationDocument.reportId,
updatedAt = priorNotificationDocument.updatedAt,
)
}
}
}
Loading

0 comments on commit a21dd8e

Please sign in to comment.