Initial commit.

This commit is contained in:
Florian THIERRY
2025-11-14 14:22:56 +01:00
commit af253dcbe3
68 changed files with 1757 additions and 0 deletions

View File

@@ -0,0 +1,15 @@
plugins {
kotlin("jvm")
id("io.spring.dependency-management") version "1.1.7"
kotlin("plugin.jpa") version "2.2.21"
}
dependencies {
implementation(kotlin("stdlib"))
implementation(project(":imagora-domain"))
implementation(project(":imagora-application"))
implementation("org.springframework:spring-context")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.jetbrains.kotlin:kotlin-noarg:2.2.21")
implementation("org.postgresql:postgresql:42.7.8")
}

View File

@@ -0,0 +1,15 @@
services:
imagora_database:
image: postgres:17
container_name: imagora_database
environment:
POSTGRES_USER: imagora_admin
POSTGRES_PASSWORD: P@ssword!
POSTGRES_DB: imagora_database
ports:
- "11979:5432"
volumes:
- "./postgres_data:/var/lib/postgresql/data"
volumes:
postgres_data:

View File

@@ -0,0 +1,10 @@
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE USER imagora_user
WITH PASSWORD 'P@ssword!'
NOCREATEDB;
GRANT SELECT, INSERT, UPDATE, DELETE
ON ALL TABLES
IN SCHEMA public
TO imagora_user;

View File

@@ -0,0 +1,16 @@
CREATE TABLE IF NOT EXISTS "user" (
id UUID NOT NULL,
pseudo VARCHAR(256) NOT NULL,
email VARCHAR(256) NOT NULL,
encrypted_password VARCHAR(512) NOT NULL,
CONSTRAINT user_pk PRIMARY KEY (id)
);
CREATE TABLE IF NOT EXISTS user_roles (
user_id UUID NOT NULL,
role SMALLINT NOT NULL,
CONSTRAINT user_roles_pk PRIMARY KEY (user_id, role),
CONSTRAINT user_roles_user_id_fk FOREIGN KEY (user_id) REFERENCES "user" (id)
);
CREATE INDEX IF NOT EXISTS user_roles_user_id_idx ON user_roles (user_id);

View File

@@ -0,0 +1,10 @@
package org.takiguchi.imagora.infrastructure.core.configuration
import org.springframework.boot.autoconfigure.domain.EntityScan
import org.springframework.context.annotation.Configuration
import org.springframework.data.jpa.repository.config.EnableJpaRepositories
@Configuration
@EnableJpaRepositories("org.takiguchi.imagora.infrastructure")
@EntityScan("org.takiguchi.imagora.infrastructure")
open class JpaConfiguration

View File

@@ -0,0 +1,65 @@
package org.takiguchi.imagora.infrastructure.user
import com.github.michaelbull.result.Result
import com.github.michaelbull.result.mapError
import com.github.michaelbull.result.runCatching
import org.springframework.stereotype.Component
import org.takiguchi.imagora.domain.core.error.TechnicalError
import org.takiguchi.imagora.domain.user.model.User
import org.takiguchi.imagora.domain.user.outputport.UserOutputPort
import org.takiguchi.imagora.infrastructure.user.model.UserEntity
import org.takiguchi.imagora.infrastructure.user.repository.UserJpaRepository
import java.util.*
@Component
class UserJpaAdapter(
private val userJpaRepository: UserJpaRepository
) : UserOutputPort {
override fun getAll(): Result<List<User>, TechnicalError> = runCatching {
userJpaRepository.findAll()
.map(UserEntity::toDomain)
}.mapError { throwable ->
TechnicalError(throwable.message ?: "Unknown error while retrieving all users.")
}
override fun getById(userId: UUID): Result<User?, TechnicalError> = runCatching {
userJpaRepository.findById(userId)
.map(UserEntity::toDomain)
.orElse(null)
}.mapError { throwable ->
TechnicalError(throwable.message ?: "Unknown error while retrieving user by its id.")
}
override fun save(user: User): Result<Unit, TechnicalError> = runCatching {
UserEntity(user).also(userJpaRepository::save)
Unit
}.mapError { throwable ->
TechnicalError(throwable.message ?: "Unknown error while saving user.")
}
override fun deleteById(user: User): Result<Unit, TechnicalError> = runCatching {
userJpaRepository.deleteById(user.id)
}.mapError { throwable ->
TechnicalError(throwable.message ?: "Unknown error while deleting user.")
}
override fun existsByEmail(email: String): Result<Boolean, TechnicalError> = runCatching {
userJpaRepository.existsByEmail(email)
}.mapError { throwable ->
TechnicalError(throwable.message ?: "Unknown error while checking user existence by its email.")
}
override fun existsByPseudo(pseudo: String): Result<Boolean, TechnicalError> = runCatching {
userJpaRepository.existsByEmail(pseudo)
}.mapError { throwable ->
TechnicalError(throwable.message ?: "Unknown error while checking user existence by its pseudo.")
}
override fun getByPseudoOrEmail(pseudoOrEmail: String): Result<User?, TechnicalError> = runCatching {
userJpaRepository.getByPseudoOrEmail(pseudoOrEmail)
?.toDomain()
}.mapError { throwable ->
TechnicalError(throwable.message ?: "Unknown error while retrieving user by its pseudo or email.")
}
}

View File

@@ -0,0 +1,42 @@
package org.takiguchi.imagora.infrastructure.user.model
import jakarta.persistence.*
import org.takiguchi.imagora.domain.user.model.User
import org.takiguchi.imagora.domain.user.model.UserRole
import java.util.*
@Entity
@Table(name = "`user`")
data class UserEntity(
@Id
val id: UUID,
@Column(nullable = false)
val pseudo: String,
@Column(nullable = false)
val email: String,
@Column(nullable = false)
val encryptedPassword: String,
@ElementCollection(targetClass = UserRole::class)
@CollectionTable(
name = "user_roles",
joinColumns = [JoinColumn(name = "user_id")]
)
@Column(name = "role")
val roles: List<UserRole>
) {
constructor(user: User) : this (
id = user.id,
pseudo = user.pseudo,
email = user.email,
encryptedPassword = user.encryptedPassword,
roles = user.roles,
)
fun toDomain() = User(
id = id,
pseudo = pseudo,
email = email,
encryptedPassword = encryptedPassword,
roles = roles
)
}

View File

@@ -0,0 +1,22 @@
package org.takiguchi.imagora.infrastructure.user.repository
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.Query
import org.springframework.data.repository.query.Param
import org.springframework.stereotype.Repository
import org.takiguchi.imagora.infrastructure.user.model.UserEntity
import java.util.*
@Repository
interface UserJpaRepository : JpaRepository<UserEntity, UUID> {
fun existsByEmail(email: String): Boolean
@Query("SELECT u FROM UserEntity u JOIN FETCH u.roles WHERE u.pseudo = :pseudoOrEmail OR u.email = :pseudoOrEmail")
fun getByPseudoOrEmail(@Param("pseudoOrEmail") pseudoOrEmail: String): UserEntity?
@Query("SELECT u FROM UserEntity u JOIN FETCH u.roles")
override fun findAll(): List<UserEntity>
@Query("SELECT u FROM UserEntity u JOIN FETCH u.roles WHERE u.id = :userId")
override fun findById(@Param("userId") userId: UUID): Optional<UserEntity>
}