diff --git a/.env.dev.defaults b/.env.dev.defaults index 01c2bfcd40..ddbc3c0c08 100644 --- a/.env.dev.defaults +++ b/.env.dev.defaults @@ -9,8 +9,13 @@ MONITORENV_API_KEY=DUMMY-API-KEY MONITORENV_IMAGE= # Spring -ENV_DB_URL=jdbc:postgresql://localhost:5432/monitorenvdb?user=postgres&password=postgres +ENV_DB_URL=jdbc:postgresql://localhost:5432/monitorenvdb +ENV_DB_USER=postgres +ENV_DB_PASSWORD=postgres FLYWAY_MIGRATION_PATH=classpath:/db/migration,classpath:/db/testdata +CACEM_DB_URL=jdbc:postgresql://localhost:5432/cacem +CACEM_DB_USER=cacem +CACEM_DB_PASSWORD=cacem # Sentry MONITORENV_SENTRY_ENABLED=false diff --git a/.env.infra.example b/.env.infra.example index d80446b0f2..d3005d83da 100644 --- a/.env.infra.example +++ b/.env.infra.example @@ -8,7 +8,12 @@ MONITORENV_API_KEY= # Spring ENV_DB_URL= +ENV_DB_USER= +ENV_DB_PASSWORD= FLYWAY_MIGRATION_PATH= +CACEM_DB_URL= +CACEM_DB_USER= +CACEM_DB_PASSWORD= # Sentry MONITORENV_SENTRY_ENABLED= diff --git a/.env.test.defaults b/.env.test.defaults index bd1948fa6c..11f330e930 100644 --- a/.env.test.defaults +++ b/.env.test.defaults @@ -8,8 +8,13 @@ MONITORENV_VERSION=0.0.0 MONITORENV_API_KEY=DUMMY-API-KEY # Spring -ENV_DB_URL=jdbc:postgresql://localhost:5432/monitorenvdb?user=postgres&password=postgres +ENV_DB_URL=jdbc:postgresql://localhost:5432/monitorenvdb +ENV_DB_USER=postgres +ENV_DB_PASSWORD=postgres FLYWAY_MIGRATION_PATH=classpath:/db/migration,classpath:/db/testdata +CACEM_DB_URL=jdbc:postgresql://localhost:5432/cacem +CACEM_DB_USER=cacem +CACEM_DB_PASSWORD=cacem # Sentry MONITORENV_SENTRY_ENABLED=false diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/config/CacemJpaConfig.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/config/CacemJpaConfig.kt new file mode 100644 index 0000000000..7ddc9ea112 --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/config/CacemJpaConfig.kt @@ -0,0 +1,64 @@ +package fr.gouv.cacem.monitorenv.config + +import com.zaxxer.hikari.HikariDataSource +import jakarta.persistence.EntityManagerFactory +import org.springframework.beans.factory.annotation.Qualifier +import org.springframework.beans.factory.annotation.Value +import org.springframework.boot.ApplicationRunner +import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.data.jpa.repository.config.EnableJpaRepositories +import org.springframework.orm.jpa.JpaTransactionManager +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean +import org.springframework.transaction.PlatformTransactionManager +import javax.sql.DataSource + +@Configuration +@EnableJpaRepositories( + basePackages = [ + "fr.gouv.cacem.monitorenv.infrastructure.cacem.repositories", + ], + entityManagerFactoryRef = "cacemEntityManagerFactory", + transactionManagerRef = "cacemTransactionManager", +) +class CacemJpaConfig( + @Value("\${spring.datasource.cacem.url}") private val url: String, + @Value("\${spring.datasource.cacem.user}") private val username: String, + @Value("\${spring.datasource.cacem.password}") private val password: String, +) { + @Bean + fun cacemDataSource(): HikariDataSource { + val ds = HikariDataSource() + ds.jdbcUrl = url + ds.username = username + ds.password = password + ds.driverClassName = "org.postgresql.Driver" + ds.maximumPoolSize = 10 + ds.maxLifetime = 60000 + return ds + } + + @Bean + fun cacemEntityManagerFactory( + builder: EntityManagerFactoryBuilder, + @Qualifier("cacemDataSource") dataSource: DataSource, + ): LocalContainerEntityManagerFactoryBean = + builder + .dataSource(dataSource) + .packages("fr.gouv.cacem.monitorenv.infrastructure.cacem.models") + .persistenceUnit("cacem") + .build() + + @Bean + fun debugPrimaryDs( + @Qualifier("primaryDataSource") ds: DataSource, + ) = ApplicationRunner { + println(ds) + } + + @Bean + fun cacemTransactionManager( + @Qualifier("cacemEntityManagerFactory") emf: EntityManagerFactory, + ): PlatformTransactionManager = JpaTransactionManager(emf) +} diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/config/JpaConfig.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/config/JpaConfig.kt deleted file mode 100644 index c10e2eb063..0000000000 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/config/JpaConfig.kt +++ /dev/null @@ -1,14 +0,0 @@ -package fr.gouv.cacem.monitorenv.config - -import org.springframework.boot.autoconfigure.EnableAutoConfiguration -import org.springframework.context.annotation.ComponentScan -import org.springframework.context.annotation.Configuration -import org.springframework.data.jpa.repository.config.EnableJpaRepositories -import org.springframework.transaction.annotation.EnableTransactionManagement - -@Configuration -@EnableJpaRepositories(basePackages = ["fr.gouv.cacem.monitorenv.infrastructure.database.repositories"]) -@EnableTransactionManagement -@EnableAutoConfiguration -@ComponentScan("fr.gouv.cacem.monitorenv") -class JpaConfig diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/config/PrimaryJpaConfig.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/config/PrimaryJpaConfig.kt new file mode 100644 index 0000000000..6c2e31e6da --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/config/PrimaryJpaConfig.kt @@ -0,0 +1,60 @@ +package fr.gouv.cacem.monitorenv.config + +import com.zaxxer.hikari.HikariDataSource +import jakarta.persistence.EntityManagerFactory +import org.springframework.beans.factory.annotation.Qualifier +import org.springframework.beans.factory.annotation.Value +import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.context.annotation.Primary +import org.springframework.data.jpa.repository.config.EnableJpaRepositories +import org.springframework.orm.jpa.JpaTransactionManager +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean +import org.springframework.transaction.PlatformTransactionManager +import javax.sql.DataSource + +@Configuration +@EnableJpaRepositories( + basePackages = [ + "fr.gouv.cacem.monitorenv.infrastructure.database.repositories", + ], + entityManagerFactoryRef = "primaryEntityManagerFactory", + transactionManagerRef = "primaryTransactionManager", +) +class PrimaryJpaConfig( + @Value("\${spring.datasource.primary.url}") private val url: String, + @Value("\${spring.datasource.primary.user}") private val username: String, + @Value("\${spring.datasource.primary.password}") private val password: String, +) { + @Bean + @Primary + fun primaryDataSource(): HikariDataSource { + val ds = HikariDataSource() + ds.jdbcUrl = url + ds.username = username + ds.password = password + ds.driverClassName = "org.postgresql.Driver" + ds.maximumPoolSize = 10 + ds.maxLifetime = 60000 + return ds + } + + @Bean + @Primary + fun primaryEntityManagerFactory( + builder: EntityManagerFactoryBuilder, + @Qualifier("primaryDataSource") dataSource: DataSource, + ): LocalContainerEntityManagerFactoryBean = + builder + .dataSource(dataSource) + .packages("fr.gouv.cacem.monitorenv") + .persistenceUnit("primary") + .build() + + @Bean + @Primary + fun primaryTransactionManager( + @Qualifier("primaryEntityManagerFactory") emf: EntityManagerFactory, + ): PlatformTransactionManager = JpaTransactionManager(emf) +} diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/repositories/IRegulatoryAreaRepositoryNew.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/repositories/IRegulatoryAreaRepositoryNew.kt new file mode 100644 index 0000000000..52289d3d35 --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/repositories/IRegulatoryAreaRepositoryNew.kt @@ -0,0 +1,14 @@ +package fr.gouv.cacem.monitorenv.domain.repositories + +import fr.gouv.cacem.monitorenv.domain.entities.regulatoryArea.RegulatoryAreaEntity +import org.locationtech.jts.geom.Geometry + +interface IRegulatoryAreaRepositoryNew { + fun findById(id: Int): RegulatoryAreaEntity? + + fun findAll(): List + + fun count(): Long + + fun findAllIdsByGeometry(geometry: Geometry): List +} diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/regulatoryAreas/GetAllNewRegulatoryAreas.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/regulatoryAreas/GetAllRegulatoryAreasNew.kt similarity index 74% rename from backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/regulatoryAreas/GetAllNewRegulatoryAreas.kt rename to backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/regulatoryAreas/GetAllRegulatoryAreasNew.kt index 8f15320260..a457a18eb1 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/regulatoryAreas/GetAllNewRegulatoryAreas.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/domain/use_cases/regulatoryAreas/GetAllRegulatoryAreasNew.kt @@ -2,15 +2,20 @@ package fr.gouv.cacem.monitorenv.domain.use_cases.regulatoryAreas import fr.gouv.cacem.monitorenv.config.UseCase import fr.gouv.cacem.monitorenv.domain.entities.regulatoryArea.RegulatoryAreaEntity -import fr.gouv.cacem.monitorenv.domain.repositories.IRegulatoryAreaRepository +import fr.gouv.cacem.monitorenv.domain.repositories.IRegulatoryAreaRepositoryNew import org.slf4j.LoggerFactory +import org.springframework.transaction.annotation.Transactional @UseCase -class GetAllRegulatoryAreas( - private val regulatoryAreaRepository: IRegulatoryAreaRepository, +class GetAllRegulatoryAreasNew( + private val regulatoryAreaRepository: IRegulatoryAreaRepositoryNew, ) { - private val logger = LoggerFactory.getLogger(GetAllRegulatoryAreas::class.java) + private val logger = LoggerFactory.getLogger(GetAllRegulatoryAreasNew::class.java) + @Transactional( + transactionManager = "cacemTransactionManager", + readOnly = true, + ) fun execute(): List { logger.info("Attempt to GET all regulatory areas") val regulatoryAreas = regulatoryAreaRepository.findAll() diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/bff/v1/RegulatoryAreas.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/bff/v1/RegulatoryAreas.kt index 6aeb02f87c..719a7999bc 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/bff/v1/RegulatoryAreas.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/api/endpoints/bff/v1/RegulatoryAreas.kt @@ -35,4 +35,11 @@ class RegulatoryAreas( val regulatoryAreas = getAllRegulatoryAreas.execute() return regulatoryAreas.map { RegulatoryAreaWithMetadataDataOutput.fromRegulatoryAreaEntity(it) } } + + @GetMapping("/new") + @Operation(summary = "Get new regulatory Areas") + fun getAllNew(): List { + val regulatoryAreas = getAllRegulatoryAreas.execute() + return regulatoryAreas.map { RegulatoryAreaWithMetadataDataOutput.fromRegulatoryAreaEntity(it) } + } } diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/RegulatoryAreaNewModel.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/cacem/model/RegulatoryAreaNewModel.kt similarity index 86% rename from backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/RegulatoryAreaNewModel.kt rename to backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/cacem/model/RegulatoryAreaNewModel.kt index f7e54dd26a..8e80d463fe 100644 --- a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/model/RegulatoryAreaNewModel.kt +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/cacem/model/RegulatoryAreaNewModel.kt @@ -1,10 +1,10 @@ -package fr.gouv.cacem.monitorenv.infrastructure.database.model +package fr.gouv.cacem.monitorenv.infrastructure.cacem.model import com.fasterxml.jackson.databind.annotation.JsonDeserialize import com.fasterxml.jackson.databind.annotation.JsonSerialize import fr.gouv.cacem.monitorenv.domain.entities.regulatoryArea.RegulatoryAreaEntity -import fr.gouv.cacem.monitorenv.infrastructure.database.model.TagRegulatoryAreaModel.Companion.toTagEntities -import fr.gouv.cacem.monitorenv.infrastructure.database.model.ThemeRegulatoryAreaModel.Companion.toThemeEntities +import fr.gouv.cacem.monitorenv.infrastructure.cacem.model.TagRegulatoryAreaNewModel.Companion.toTagEntities +import fr.gouv.cacem.monitorenv.infrastructure.cacem.model.ThemeRegulatoryAreaNewModel.Companion.toThemeEntities import jakarta.persistence.Column import jakarta.persistence.Entity import jakarta.persistence.FetchType @@ -16,8 +16,8 @@ import org.n52.jackson.datatype.jts.GeometryDeserializer import org.n52.jackson.datatype.jts.GeometrySerializer @Entity -@Table(name = "regulations_cacem") -data class RegulatoryAreaModel( +@Table(name = "reg_cacem", schema = "prod") +data class RegulatoryAreaNewModel( @Id @Column(name = "id") val id: Int, @Column(name = "plan") val plan: String?, @Column(name = "date") val date: String?, @@ -40,12 +40,12 @@ data class RegulatoryAreaModel( mappedBy = "regulatoryArea", fetch = FetchType.LAZY, ) - var tags: List, + var tags: List, @OneToMany( mappedBy = "regulatoryArea", fetch = FetchType.LAZY, ) - var themes: List, + var themes: List, @Column(name = "resume") val resume: String?, @Column(name = "type") val type: String?, @Column(name = "url") val url: String?, diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/cacem/model/TagNewModel.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/cacem/model/TagNewModel.kt new file mode 100644 index 0000000000..5089e3e26a --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/cacem/model/TagNewModel.kt @@ -0,0 +1,79 @@ +package fr.gouv.cacem.monitorenv.infrastructure.cacem.model + +import fr.gouv.cacem.monitorenv.domain.entities.tags.TagEntity +import jakarta.persistence.* +import java.time.ZonedDateTime + +@Entity +@Table(name = "tags") +data class TagNewModel( + @Id + @Column(name = "id", nullable = false, unique = true) + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Int, + val name: String, + val startedAt: ZonedDateTime?, + val endedAt: ZonedDateTime?, + @ManyToOne + @JoinColumn(name = "parent_id") + val parent: TagNewModel?, + @OneToMany( + mappedBy = "parent", + fetch = FetchType.LAZY, + cascade = [CascadeType.ALL], + ) + var subTags: List, +) { + fun toTagEntity(): TagEntity = + TagEntity( + id = id, + name = name, + startedAt = startedAt, + endedAt = endedAt, + subTags = subTags.map { it.toTagEntity() }, + ) + + companion object { + fun fromTagEntity( + tagEntity: TagEntity, + parent: TagNewModel? = null, + ): TagNewModel { + val tagModel = + TagNewModel( + id = tagEntity.id, + name = tagEntity.name, + parent = parent, + startedAt = tagEntity.startedAt, + endedAt = tagEntity.endedAt, + subTags = listOf(), + ) + tagModel.subTags = tagEntity.subTags.map { fromTagEntity(it, tagModel) } + return tagModel + } + } + + override fun toString(): String = "TagModel(id=$id, name='$name', startedAt=$startedAt, endedAt=$endedAt)" + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as TagNewModel + + if (id != other.id) return false + if (name != other.name) return false + if (startedAt != other.startedAt) return false + if (endedAt != other.endedAt) return false + if (parent != other.parent) return false + + return true + } + + override fun hashCode(): Int { + var result = id + result = 31 * result + name.hashCode() + result = 31 * result + (startedAt?.hashCode() ?: 0) + result = 31 * result + (endedAt?.hashCode() ?: 0) + return result + } +} diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/cacem/model/TagRegulatoryAreaNewModel.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/cacem/model/TagRegulatoryAreaNewModel.kt new file mode 100644 index 0000000000..61b0d88d0a --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/cacem/model/TagRegulatoryAreaNewModel.kt @@ -0,0 +1,45 @@ +package fr.gouv.cacem.monitorenv.infrastructure.cacem.model + +import fr.gouv.cacem.monitorenv.domain.entities.tags.TagEntity +import jakarta.persistence.Embeddable +import jakarta.persistence.EmbeddedId +import jakarta.persistence.Entity +import jakarta.persistence.FetchType +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne +import jakarta.persistence.MapsId +import jakarta.persistence.Table +import java.io.Serializable + +@Entity +@Table(name = "tags_regulatory_areas") +data class TagRegulatoryAreaNewModel( + @EmbeddedId + val id: TagRegulatoryAreaPk, + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "tags_id") + @MapsId("tagId") + val tag: TagNewModel, + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "regulatory_areas_id") + @MapsId("regulatoryAreaId") + val regulatoryArea: RegulatoryAreaNewModel, +) { + companion object { + fun toTagEntities(tags: List): List { + val parents = tags.map { it.tag }.filter { it.parent === null } + + return parents.map { parent -> + val subTags = tags.filter { it.tag.parent?.id == parent.id }.map { it.tag } + parent.subTags = subTags + return@map parent.toTagEntity() + } + } + } +} + +@Embeddable +data class TagRegulatoryAreaPk( + val tagId: Int, + val regulatoryAreaId: Int, +) : Serializable diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/cacem/model/ThemeNewModel.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/cacem/model/ThemeNewModel.kt new file mode 100644 index 0000000000..3c61ac4b15 --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/cacem/model/ThemeNewModel.kt @@ -0,0 +1,89 @@ +package fr.gouv.cacem.monitorenv.infrastructure.cacem.model + +import fr.gouv.cacem.monitorenv.domain.entities.themes.ThemeEntity +import jakarta.persistence.CascadeType +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.FetchType +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne +import jakarta.persistence.OneToMany +import jakarta.persistence.Table +import java.time.ZonedDateTime + +@Entity +@Table(name = "themes") +data class ThemeNewModel( + @Id + @Column(name = "id", nullable = false, unique = true) + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Int, + val name: String, + val startedAt: ZonedDateTime?, + val endedAt: ZonedDateTime?, + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "parent_id") + val parent: ThemeNewModel?, + @OneToMany( + mappedBy = "parent", + fetch = FetchType.LAZY, + cascade = [CascadeType.ALL], + ) + var subThemes: List, +) { + fun toThemeEntity(): ThemeEntity = + ThemeEntity( + id = id, + name = name, + startedAt = startedAt, + endedAt = endedAt, + subThemes = subThemes.map { it.toThemeEntity() }, + ) + + companion object { + fun fromThemeEntity( + themeEntity: ThemeEntity, + parent: ThemeNewModel? = null, + ): ThemeNewModel { + val tagModel = + ThemeNewModel( + id = themeEntity.id, + name = themeEntity.name, + parent = parent, + startedAt = themeEntity.startedAt, + endedAt = themeEntity.endedAt, + subThemes = listOf(), + ) + tagModel.subThemes = themeEntity.subThemes.map { fromThemeEntity(it, tagModel) } + return tagModel + } + } + + override fun toString(): String = "ThemeModel(id=$id, name='$name', startedAt=$startedAt, endedAt=$endedAt)" + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as ThemeNewModel + + if (id != other.id) return false + if (name != other.name) return false + if (startedAt != other.startedAt) return false + if (endedAt != other.endedAt) return false + if (parent != other.parent) return false + + return true + } + + override fun hashCode(): Int { + var result = id + result = 31 * result + name.hashCode() + result = 31 * result + (startedAt?.hashCode() ?: 0) + result = 31 * result + (endedAt?.hashCode() ?: 0) + return result + } +} diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/cacem/model/ThemeRegulatoryAreaNewModel.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/cacem/model/ThemeRegulatoryAreaNewModel.kt new file mode 100644 index 0000000000..6757bd3742 --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/cacem/model/ThemeRegulatoryAreaNewModel.kt @@ -0,0 +1,45 @@ +package fr.gouv.cacem.monitorenv.infrastructure.cacem.model + +import fr.gouv.cacem.monitorenv.domain.entities.themes.ThemeEntity +import jakarta.persistence.Embeddable +import jakarta.persistence.EmbeddedId +import jakarta.persistence.Entity +import jakarta.persistence.FetchType +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne +import jakarta.persistence.MapsId +import jakarta.persistence.Table +import java.io.Serializable + +@Entity +@Table(name = "themes_regulatory_areas") +data class ThemeRegulatoryAreaNewModel( + @EmbeddedId + val id: ThemeRegulatoryAreaPk, + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "themes_id") + @MapsId("themeId") + val theme: ThemeNewModel, + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "regulatory_areas_id") + @MapsId("regulatoryAreaId") + val regulatoryArea: RegulatoryAreaNewModel, +) { + companion object { + fun toThemeEntities(themes: List): List { + val parents = themes.map { it.theme }.filter { it.parent === null } + + return parents.map { parent -> + val subThemes = themes.filter { it.theme.parent?.id == parent.id }.map { it.theme } + parent.subThemes = subThemes + return@map parent.toThemeEntity() + } + } + } + + @Embeddable + data class ThemeRegulatoryAreaPk( + val themeId: Int, + val regulatoryAreaId: Int, + ) : Serializable +} diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/cacem/repositories/JpaRegulatoryAreaNewRepository.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/cacem/repositories/JpaRegulatoryAreaNewRepository.kt new file mode 100644 index 0000000000..7c469f8f31 --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/cacem/repositories/JpaRegulatoryAreaNewRepository.kt @@ -0,0 +1,24 @@ +package fr.gouv.cacem.monitorenv.infrastructure.cacem.repositories + +import fr.gouv.cacem.monitorenv.domain.entities.regulatoryArea.RegulatoryAreaEntity +import fr.gouv.cacem.monitorenv.domain.repositories.IRegulatoryAreaRepository +import fr.gouv.cacem.monitorenv.infrastructure.cacem.repositories.interfaces.IDBRegulatoryAreaNewRepository +import org.locationtech.jts.geom.Geometry +import org.springframework.data.repository.findByIdOrNull +import org.springframework.stereotype.Repository + +@Repository +class JpaRegulatoryAreaNewRepository( + private val dbRegulatoryAreaRepository: IDBRegulatoryAreaNewRepository, +) : IRegulatoryAreaRepository { + override fun findAll(): List = + dbRegulatoryAreaRepository.findAllByOrderByLayerName().map { it.toRegulatoryArea() } + + override fun findById(id: Int): RegulatoryAreaEntity? = + dbRegulatoryAreaRepository.findByIdOrNull(id)?.toRegulatoryArea() + + override fun count(): Long = dbRegulatoryAreaRepository.count() + + override fun findAllIdsByGeometry(geometry: Geometry): List = + dbRegulatoryAreaRepository.findAllIdsByGeom(geometry) +} diff --git a/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/cacem/repositories/interfaces/IDBRegulatoryAreaNewRepository.kt b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/cacem/repositories/interfaces/IDBRegulatoryAreaNewRepository.kt new file mode 100644 index 0000000000..3630c50042 --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/cacem/monitorenv/infrastructure/cacem/repositories/interfaces/IDBRegulatoryAreaNewRepository.kt @@ -0,0 +1,19 @@ +package fr.gouv.cacem.monitorenv.infrastructure.cacem.repositories.interfaces + +import fr.gouv.cacem.monitorenv.infrastructure.cacem.model.RegulatoryAreaNewModel +import org.locationtech.jts.geom.Geometry +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.data.jpa.repository.Query + +interface IDBRegulatoryAreaNewRepository : JpaRepository { + @Query( + value = + """ + SELECT r.id FROM RegulatoryAreaModel r + WHERE ST_INTERSECTS(st_setsrid(r.geom, 4326), ST_Buffer(st_setsrid(:geometry, 4326), 0)) + """, + ) + fun findAllIdsByGeom(geometry: Geometry): List + + fun findAllByOrderByLayerName(): List +} diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml index 9241aabcba..efffd85fb9 100644 --- a/backend/src/main/resources/application.yml +++ b/backend/src/main/resources/application.yml @@ -1,125 +1,136 @@ server: - port: 8880 - forward-headers-strategy: framework - compression: - enabled: true - excluded-user-agents: "" - mime-types: text/html,text/xml,text/plain,text/css,text/javascript,application/javascript - min-response-size: 2048 + port: 8880 + forward-headers-strategy: framework + compression: + enabled: true + excluded-user-agents: "" + mime-types: text/html,text/xml,text/plain,text/css,text/javascript,application/javascript + min-response-size: 2048 host: - ip: ${MONITORENV_URL} + ip: ${MONITORENV_URL} monitorenv: - kafka: - ais: - batch-size: ${MONITORENV_KAFKA_AIS_BATCH_SIZE} - topic: ${MONITORENV_KAFKA_AIS_TOPIC} - timeout: ${MONITORENV_KAFKA_AIS_TIMEOUT} - enabled: ${MONITORENV_KAFKA_AIS_ENABLED} - producer: - enabled: ${MONITORENV_KAFKA_AIS_PRODUCER_ENABLED:false} + kafka: + ais: + batch-size: ${MONITORENV_KAFKA_AIS_BATCH_SIZE} + topic: ${MONITORENV_KAFKA_AIS_TOPIC} + timeout: ${MONITORENV_KAFKA_AIS_TIMEOUT} + enabled: ${MONITORENV_KAFKA_AIS_ENABLED} + producer: + enabled: ${MONITORENV_KAFKA_AIS_PRODUCER_ENABLED:false} - ajp: - port: 8000 - version: ${VERSION} - sentry: - enabled: ${MONITORENV_SENTRY_ENABLED} - environment: ${ENVIRONMENT} - dsn: ${SENTRY_DSN} - oidc: - enabled: ${MONITORENV_OIDC_ENABLED} - bypassEmailDomainsFilter: ${MONITORENV_OIDC_BYPASS_DOMAINS_FILTER:false} - clientId: ${MONITORENV_OIDC_CLIENT_ID:} - clientSecret: ${MONITORENV_OIDC_CLIENT_SECRET:} - redirectUri: ${MONITORENV_OIDC_REDIRECT_URI:} - loginUrl: ${MONITORENV_OIDC_LOGIN_URL} - successUrl: ${MONITORENV_OIDC_SUCCESS_URL} - errorUrl: ${MONITORENV_OIDC_ERROR_URL} - authorizedEmailDomains: ${MONITORENV_OIDC_AUTHORIZED_EMAIL_DOMAINS:} - issuerUri: ${MONITORENV_OIDC_ISSUER_URI:} - issuerUriExternal: ${MONITORENV_OIDC_ISSUER_URI_EXTERNAL} - authorizationUri: ${MONITORENV_OIDC_AUTHORIZATION_URI:} - tokenUri: ${MONITORENV_OIDC_TOKEN_URI:} - userInfoUri: ${MONITORENV_OIDC_USER_INFO_URI:} - jwkSetUri: ${MONITORENV_OIDC_JWK_SET_URI:} - proxyUrl: ${MONITORENV_OIDC_PROXY_URL:} - api: - protected: - paths: /bff/v1/* - super-user-paths: /bff/v1/missions/*,/bff/v1/reportings/*,/bff/v1/semaphores/*,/bff/v1/stations/* - public-paths: /api/v1/authorization/management/* - api-key: ${MONITORENV_API_KEY} - brief: - templatePath: /template_export_brief.docx - tmpDocxPath: tmp_brief.docx - tmpOdtPath: tmp_brief.odt - ext: - id: ${MONITORENV_EXT_ID} - password: ${MONITORENV_EXT_PASSWORD} - legicem: - id: ${MONITORENV_LEGICEM_ID} - password: ${MONITORENV_LEGICEM_PASSWORD} + ajp: + port: 8000 + version: ${VERSION} + sentry: + enabled: ${MONITORENV_SENTRY_ENABLED} + environment: ${ENVIRONMENT} + dsn: ${SENTRY_DSN} + oidc: + enabled: ${MONITORENV_OIDC_ENABLED} + bypassEmailDomainsFilter: ${MONITORENV_OIDC_BYPASS_DOMAINS_FILTER:false} + clientId: ${MONITORENV_OIDC_CLIENT_ID:} + clientSecret: ${MONITORENV_OIDC_CLIENT_SECRET:} + redirectUri: ${MONITORENV_OIDC_REDIRECT_URI:} + loginUrl: ${MONITORENV_OIDC_LOGIN_URL} + successUrl: ${MONITORENV_OIDC_SUCCESS_URL} + errorUrl: ${MONITORENV_OIDC_ERROR_URL} + authorizedEmailDomains: ${MONITORENV_OIDC_AUTHORIZED_EMAIL_DOMAINS:} + issuerUri: ${MONITORENV_OIDC_ISSUER_URI:} + issuerUriExternal: ${MONITORENV_OIDC_ISSUER_URI_EXTERNAL} + authorizationUri: ${MONITORENV_OIDC_AUTHORIZATION_URI:} + tokenUri: ${MONITORENV_OIDC_TOKEN_URI:} + userInfoUri: ${MONITORENV_OIDC_USER_INFO_URI:} + jwkSetUri: ${MONITORENV_OIDC_JWK_SET_URI:} + proxyUrl: ${MONITORENV_OIDC_PROXY_URL:} + api: + protected: + paths: /bff/v1/* + super-user-paths: /bff/v1/missions/*,/bff/v1/reportings/*,/bff/v1/semaphores/*,/bff/v1/stations/* + public-paths: /api/v1/authorization/management/* + api-key: ${MONITORENV_API_KEY} + brief: + templatePath: /template_export_brief.docx + tmpDocxPath: tmp_brief.docx + tmpOdtPath: tmp_brief.odt + ext: + id: ${MONITORENV_EXT_ID} + password: ${MONITORENV_EXT_PASSWORD} + legicem: + id: ${MONITORENV_LEGICEM_ID} + password: ${MONITORENV_LEGICEM_PASSWORD} spring: - jmx: - enabled: false - mvc: - static-path-pattern: "/**" - web: - resources: - static-locations: file:${STATIC_FILES_PATH} - flyway: - locations: ${FLYWAY_MIGRATION_PATH} - jpa: - hibernate: - ddl-auto: validate - properties: - hibernate: - temp: - use_jdbc_metadata_defaults: false - dialect: org.hibernate.spatial.dialect.postgis.PostgisPG95Dialect - jdbc: - time_zone: UTC - default_batch_fetch_size: ${MONITORENV_BATCH_SIZE:50} - datasource: - url: ${ENV_DB_URL} - driver-class-name: org.postgresql.Driver - hikari: - maxLifetime: 60000 - kafka: - bootstrap-servers: ${MONITORENV_KAFKA_AIS_SERVERS} - consumer: - group-id: ${MONITORENV_KAFKA_AIS_GROUP_ID:test} - auto-offset-reset: latest + jmx: + enabled: false + mvc: + static-path-pattern: "/**" + web: + resources: + static-locations: file:${STATIC_FILES_PATH} + flyway: + url: ${ENV_DB_URL}?user=${ENV_DB_USER}&password=${ENV_DB_PASSWORD} + locations: ${FLYWAY_MIGRATION_PATH} + jpa: + hibernate: + ddl-auto: validate + properties: + hibernate: + temp: + use_jdbc_metadata_defaults: false + dialect: org.hibernate.spatial.dialect.postgis.PostgisPG95Dialect + jdbc: + time_zone: UTC + default_batch_fetch_size: ${MONITORENV_BATCH_SIZE:50} + datasource: + primary: + url: ${ENV_DB_URL} + user: ${ENV_DB_USER} + password: ${ENV_DB_PASSWORD} + driver-class-name: org.postgresql.Driver + hikari: + maxLifetime: 60000 + cacem: + url: ${CACEM_DB_URL} + user: ${CACEM_DB_USER} + password: ${CACEM_DB_PASSWORD} + driver-class-name: org.postgresql.Driver + hikari: + maxLifetime: 60000 + kafka: + bootstrap-servers: ${MONITORENV_KAFKA_AIS_SERVERS} + consumer: + group-id: ${MONITORENV_KAFKA_AIS_GROUP_ID:test} + auto-offset-reset: latest - properties: - security.protocol: SSL + properties: + security.protocol: SSL - ssl.keystore.location: ${MONITORENV_KAFKA_AIS_KEYSTORE} - ssl.keystore.password: ${MONITORENV_KAFKA_AIS_KEYSTORE_PASSWORD} - ssl.key.password: ${MONITORENV_KAFKA_AIS_KEY_PASSWORD} - ssl.keystore.type: PKCS12 + ssl.keystore.location: ${MONITORENV_KAFKA_AIS_KEYSTORE} + ssl.keystore.password: ${MONITORENV_KAFKA_AIS_KEYSTORE_PASSWORD} + ssl.key.password: ${MONITORENV_KAFKA_AIS_KEY_PASSWORD} + ssl.keystore.type: PKCS12 - ssl.truststore.location: ${MONITORENV_KAFKA_AIS_TRUSTSTORE} - ssl.truststore.password: ${MONITORENV_KAFKA_AIS_TRUSTSTORE_PASSWORD} - ssl.truststore.type: JKS + ssl.truststore.location: ${MONITORENV_KAFKA_AIS_TRUSTSTORE} + ssl.truststore.password: ${MONITORENV_KAFKA_AIS_TRUSTSTORE_PASSWORD} + ssl.truststore.type: JKS management: - endpoints: - web: - exposure: - include: health - endpoint: - health: - show-details: always - metrics: - enable: false + endpoints: + web: + exposure: + include: health + endpoint: + health: + show-details: always + metrics: + enable: false monitorfish: - url: ${MONITORFISH_URL} - xApiKey: ${MONITORFISH_API_KEY} + url: ${MONITORFISH_URL} + xApiKey: ${MONITORFISH_API_KEY} rapportnav: - url: ${RAPPORTNAV_URL} - timeout: 3000 + url: ${RAPPORTNAV_URL} + timeout: 3000 diff --git a/backend/src/main/resources/db/testdata/V666.26__insert_dummy_regulatory_areas.sql b/backend/src/main/resources/db/testdata/V666.26__insert_dummy_regulatory_areas.sql index fed0778d5c..5922b6285d 100644 --- a/backend/src/main/resources/db/testdata/V666.26__insert_dummy_regulatory_areas.sql +++ b/backend/src/main/resources/db/testdata/V666.26__insert_dummy_regulatory_areas.sql @@ -1,4 +1,4 @@ -DO +/* DO $$ DECLARE permanent character varying = 'permanent'; @@ -606,4 +606,4 @@ INSERT INTO public.regulatory_areas ( ); END; -$$ +$$ */ diff --git a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/AbstractDBTests.kt b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/AbstractDBTests.kt index eac6c31d77..d0ec692972 100644 --- a/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/AbstractDBTests.kt +++ b/backend/src/test/kotlin/fr/gouv/cacem/monitorenv/infrastructure/database/repositories/AbstractDBTests.kt @@ -40,7 +40,7 @@ abstract class AbstractDBTests { @JvmStatic @DynamicPropertySource fun properties(registry: DynamicPropertyRegistry) { - registry.add("spring.datasource.url") { getJdbcUrl() } + registry.add("spring.datasource.primary.url") { getJdbcUrl() } } private fun getJdbcUrl(): String { diff --git a/docker-compose.yml b/docker-compose.yml index 2f67a7efeb..1b657f1389 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,7 +5,12 @@ services: container_name: monitorenv_backend user: "monitorenv:${MONITORENV_LOGS_AND_BACKUPS_GID}" environment: - - ENV_DB_URL=jdbc:postgresql://db:5432/$POSTGRES_DB?user=$POSTGRES_USER&password=$POSTGRES_PASSWORD + - ENV_DB_URL=jdbc:postgresql://db:5432/$POSTGRES_DB + - ENV_DB_USER=$POSTGRES_USER + - ENV_DB_PASSWORD=$POSTGRES_PASSWORD + - CACEM_DB_URL=jdbc:postgresql://db:5432/cacem + - CACEM_DB_USER=$CACEM_DB_USER + - CACEM_DB_PASSWORD=$CACEM_DB_PASSWORD - FLYWAY_MIGRATION_PATH=${FLYWAY_MIGRATION_PATH} - MONITORENV_HTTP_PROXY_HOST=${MONITORENV_HTTP_PROXY_HOST} - MONITORENV_HTTP_PROXY_PORT=${MONITORENV_HTTP_PROXY_PORT}