-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
7e2a3fe
commit fedc024
Showing
10 changed files
with
245 additions
and
6 deletions.
There are no files selected for viewing
8 changes: 8 additions & 0 deletions
8
backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/config/Patchable.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package fr.gouv.cnsp.monitorfish.config | ||
|
||
/** | ||
* Warning: All properties patched MUST be declared as `var`, and not `val` | ||
*/ | ||
@Target(AnnotationTarget.PROPERTY) | ||
@Retention(AnnotationRetention.RUNTIME) | ||
annotation class Patchable |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
9 changes: 9 additions & 0 deletions
9
...r/gouv/cnsp/monitorfish/domain/entities/mission/mission_actions/PatchableMissionAction.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package fr.gouv.cnsp.monitorfish.domain.entities.mission.mission_actions | ||
|
||
import java.time.ZonedDateTime | ||
import java.util.* | ||
|
||
data class PatchableMissionAction( | ||
val actionEndDatetimeUtc: Optional<ZonedDateTime>?, | ||
val observationsByUnit: Optional<String>?, | ||
) |
54 changes: 54 additions & 0 deletions
54
backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/mappers/PatchEntity.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package fr.gouv.cnsp.monitorfish.domain.mappers | ||
|
||
import fr.gouv.cnsp.monitorfish.config.Patchable | ||
import fr.gouv.cnsp.monitorfish.config.UseCase | ||
import java.util.* | ||
import kotlin.reflect.KMutableProperty | ||
import kotlin.reflect.full.hasAnnotation | ||
import kotlin.reflect.full.memberProperties | ||
|
||
@UseCase | ||
class PatchEntity<T : Any, S : Any> { | ||
|
||
/** | ||
* Patches the target entity with values from the source entity. | ||
* | ||
* This function updates the target entity with values from the source entity for properties | ||
* annotated with @Patchable. If a property in the source entity is null, the existing value | ||
* in the target entity is retained. If a property in the source entity is an Optional, it | ||
* is handled accordingly. | ||
* | ||
* @param target The target entity to be patched. | ||
* @param source The source entity providing the patch values. | ||
*/ | ||
fun execute(target: T, source: S): T { | ||
val sourceProperties = source::class.memberProperties | ||
val targetProperties = target::class.memberProperties | ||
|
||
for (sourceProp in sourceProperties) { | ||
val targetProp = | ||
targetProperties.filter { it.hasAnnotation<Patchable>() }.find { it.name == sourceProp.name } | ||
if (targetProp != null && targetProp is KMutableProperty<*>) { | ||
val sourceValue = sourceProp.getter.call(source) | ||
val existingValue = targetProp.getter.call(target) | ||
val finalValue = if (sourceValue is Optional<*>) { | ||
getValueFromOptional(existingValue, sourceValue) | ||
} else { | ||
sourceValue ?: existingValue | ||
} | ||
|
||
targetProp.setter.call(target, finalValue) | ||
} | ||
} | ||
|
||
return target | ||
} | ||
|
||
private fun getValueFromOptional(existingValue: Any?, optional: Optional<*>?): Any? { | ||
return when { | ||
optional == null -> existingValue | ||
optional.isPresent -> optional.get() | ||
else -> null | ||
} | ||
} | ||
} |
28 changes: 28 additions & 0 deletions
28
...n/fr/gouv/cnsp/monitorfish/domain/use_cases/mission/mission_actions/PatchMissionAction.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package fr.gouv.cnsp.monitorfish.domain.use_cases.mission.mission_actions | ||
|
||
import fr.gouv.cnsp.monitorfish.config.UseCase | ||
import fr.gouv.cnsp.monitorfish.domain.entities.mission.mission_actions.MissionAction | ||
import fr.gouv.cnsp.monitorfish.domain.entities.mission.mission_actions.PatchableMissionAction | ||
import fr.gouv.cnsp.monitorfish.domain.exceptions.BackendUsageErrorCode | ||
import fr.gouv.cnsp.monitorfish.domain.exceptions.BackendUsageException | ||
import fr.gouv.cnsp.monitorfish.domain.mappers.PatchEntity | ||
import fr.gouv.cnsp.monitorfish.domain.repositories.MissionActionsRepository | ||
|
||
@UseCase | ||
class PatchMissionAction( | ||
private val missionActionsRepository: MissionActionsRepository, | ||
private val patchMissionAction: PatchEntity<MissionAction, PatchableMissionAction>, | ||
) { | ||
|
||
fun execute(id: Int, patchableEnvActionEntity: PatchableMissionAction): MissionAction { | ||
return try { | ||
val previousMissionAction = missionActionsRepository.findById(id) | ||
|
||
val updatedMissionAction = patchMissionAction.execute(previousMissionAction, patchableEnvActionEntity) | ||
|
||
missionActionsRepository.save(updatedMissionAction) | ||
} catch (e: Exception) { | ||
throw BackendUsageException(BackendUsageErrorCode.NOT_FOUND, "Action $id not found", e) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
17 changes: 17 additions & 0 deletions
17
...v/cnsp/monitorfish/infrastructure/api/public_api/input/PatchableMissionActionDataInput.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package fr.gouv.cnsp.monitorfish.infrastructure.api.public_api.input | ||
|
||
import fr.gouv.cnsp.monitorfish.domain.entities.mission.mission_actions.PatchableMissionAction | ||
import java.time.ZonedDateTime | ||
import java.util.* | ||
|
||
data class PatchableMissionActionDataInput( | ||
val actionEndDatetimeUtc: Optional<ZonedDateTime>?, | ||
val observationsByUnit: Optional<String>?, | ||
) { | ||
fun toPatchableMissionAction(): PatchableMissionAction { | ||
return PatchableMissionAction( | ||
actionEndDatetimeUtc = actionEndDatetimeUtc, | ||
observationsByUnit = observationsByUnit, | ||
) | ||
} | ||
} |
71 changes: 71 additions & 0 deletions
71
...ouv/cnsp/monitorfish/domain/use_cases/mission/mission_actions/PatchMissionActionUTests.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package fr.gouv.cnsp.monitorfish.domain.use_cases.mission.mission_actions | ||
|
||
import com.neovisionaries.i18n.CountryCode | ||
import com.nhaarman.mockitokotlin2.any | ||
import com.nhaarman.mockitokotlin2.argumentCaptor | ||
import com.nhaarman.mockitokotlin2.given | ||
import com.nhaarman.mockitokotlin2.verify | ||
import fr.gouv.cnsp.monitorfish.domain.entities.mission.mission_actions.Completion | ||
import fr.gouv.cnsp.monitorfish.domain.entities.mission.mission_actions.MissionAction | ||
import fr.gouv.cnsp.monitorfish.domain.entities.mission.mission_actions.MissionActionType | ||
import fr.gouv.cnsp.monitorfish.domain.entities.mission.mission_actions.PatchableMissionAction | ||
import fr.gouv.cnsp.monitorfish.domain.mappers.PatchEntity | ||
import fr.gouv.cnsp.monitorfish.domain.repositories.MissionActionsRepository | ||
import org.assertj.core.api.Assertions.assertThat | ||
import org.junit.jupiter.api.Test | ||
import org.junit.jupiter.api.extension.ExtendWith | ||
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 PatchMissionActionUTests { | ||
|
||
@MockBean | ||
private lateinit var missionActionsRepository: MissionActionsRepository | ||
|
||
private val patchMissionAction: PatchEntity<MissionAction, PatchableMissionAction> = PatchEntity() | ||
|
||
@Test | ||
fun `execute Should patch an existing action`() { | ||
// Given | ||
val action = MissionAction( | ||
id = null, | ||
vesselId = null, | ||
missionId = 1, | ||
actionDatetimeUtc = ZonedDateTime.now(), | ||
portLocode = "AEFAT", | ||
actionType = MissionActionType.LAND_CONTROL, | ||
gearOnboard = listOf(), | ||
seizureAndDiversion = true, | ||
isDeleted = false, | ||
hasSomeGearsSeized = false, | ||
hasSomeSpeciesSeized = false, | ||
completedBy = "XYZ", | ||
isFromPoseidon = false, | ||
flagState = CountryCode.FR, | ||
userTrigram = "LTH", | ||
completion = Completion.TO_COMPLETE, | ||
) | ||
given(missionActionsRepository.findById(any())).willReturn(action) | ||
val expectedDateTime = ZonedDateTime.now() | ||
val patch = PatchableMissionAction( | ||
actionEndDatetimeUtc = Optional.of(expectedDateTime), | ||
observationsByUnit = Optional.of("An observation"), | ||
) | ||
|
||
// When | ||
PatchMissionAction(missionActionsRepository, patchMissionAction).execute(123, patch) | ||
|
||
// Then | ||
argumentCaptor<MissionAction>().apply { | ||
verify(missionActionsRepository).save(capture()) | ||
|
||
assertThat(allValues.first().observationsByUnit).isEqualTo("An observation") | ||
assertThat(allValues.first().actionEndDatetimeUtc).isEqualTo(expectedDateTime) | ||
assertThat(allValues.first().userTrigram).isEqualTo("LTH") | ||
assertThat(allValues.first().actionType).isEqualTo(MissionActionType.LAND_CONTROL) | ||
} | ||
} | ||
} |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters