fix: kafka dev standalone, OIDC realm LUM, Flyway out-of-order, shutdown guard, TypeRef categorie/modules
- docker-compose.dev.yml: retire le service kafka (standalone existant sur :9092), kafka-ui pointe host.docker.internal:9092 - application-dev.properties: OIDC admin-service realm corrigé lions-user-manager (fix AUTH changement mdp) - application-prod.properties: nouvelle var KEYCLOAK_LUM_AUTH_SERVER_URL + fallback KEYCLOAK_CLIENT_SECRET - application.properties: quarkus.flyway.out-of-order=true (évite échec si migration hors-séquence) - V10 renommé V10_1 (évite conflit avec historique Flyway existant) - AlertMonitoringService: guard Arc.container().isRunning() pour éviter NPE au shutdown - TypeOrganisationReferenceResource: forward categorie + modulesRequis au service - Tests: coverage TypeOrganisationReferenceResource + TypeReferenceService
This commit is contained in:
@@ -34,61 +34,31 @@ services:
|
||||
- postgres-dev
|
||||
restart: unless-stopped
|
||||
|
||||
kafka:
|
||||
image: apache/kafka:4.0.0
|
||||
container_name: unionflow-kafka-dev
|
||||
environment:
|
||||
KAFKA_NODE_ID: 1
|
||||
KAFKA_PROCESS_ROLES: broker,controller
|
||||
KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka:9093
|
||||
KAFKA_LISTENERS: PLAINTEXT://:9092,CONTROLLER://:9093
|
||||
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092
|
||||
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,CONTROLLER:PLAINTEXT
|
||||
KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
|
||||
KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
|
||||
KAFKA_LOG_DIRS: /var/lib/kafka/data
|
||||
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
|
||||
KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
|
||||
KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
|
||||
KAFKA_DEFAULT_REPLICATION_FACTOR: 1
|
||||
KAFKA_NUM_PARTITIONS: 3
|
||||
KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true"
|
||||
CLUSTER_ID: MkU3OEVBNTcwNTJENDM2Qk
|
||||
ports:
|
||||
- "9092:9092"
|
||||
volumes:
|
||||
- kafka_dev_data:/var/lib/kafka/data
|
||||
networks:
|
||||
- unionflow-dev
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "/opt/kafka/bin/kafka-topics.sh --list --bootstrap-server localhost:9092 || exit 1"]
|
||||
interval: 15s
|
||||
timeout: 10s
|
||||
retries: 10
|
||||
start_period: 30s
|
||||
restart: unless-stopped
|
||||
# Kafka est géré comme conteneur standalone (apache/kafka:4.0.0) sur port 9092.
|
||||
# Ne pas le redéfinir ici pour éviter le conflit de port.
|
||||
# Démarrer manuellement si nécessaire :
|
||||
# docker run -d --name kafka --network unionflow-dev \
|
||||
# -p 9092:9092 apache/kafka:4.0.0
|
||||
# Puis recréer les topics :
|
||||
# docker exec kafka /opt/kafka/bin/kafka-topics.sh --create \
|
||||
# --bootstrap-server localhost:9092 --topic unionflow.finance.approvals --partitions 3 --replication-factor 1
|
||||
|
||||
kafka-ui:
|
||||
image: ghcr.io/kafbat/kafka-ui:v1.4.2
|
||||
container_name: unionflow-kafka-ui-dev
|
||||
environment:
|
||||
KAFKA_CLUSTERS_0_NAME: unionflow-dev
|
||||
KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: kafka:9092
|
||||
KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: host.docker.internal:9092
|
||||
DYNAMIC_CONFIG_ENABLED: "true"
|
||||
ports:
|
||||
- "8082:8080"
|
||||
networks:
|
||||
- unionflow-dev
|
||||
depends_on:
|
||||
kafka:
|
||||
condition: service_healthy
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
postgres_dev_data:
|
||||
driver: local
|
||||
kafka_dev_data:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
unionflow-dev:
|
||||
|
||||
@@ -70,6 +70,8 @@ public class TypeOrganisationReferenceResource {
|
||||
.estDefaut(request.estDefaut())
|
||||
.estSysteme(request.estSysteme())
|
||||
.organisationId(request.organisationId())
|
||||
.categorie(request.categorie())
|
||||
.modulesRequis(request.modulesRequis())
|
||||
.build();
|
||||
try {
|
||||
TypeReferenceResponse created = typeReferenceService.creer(withDomaine);
|
||||
|
||||
@@ -5,6 +5,7 @@ import dev.lions.unionflow.server.entity.SystemAlert;
|
||||
import dev.lions.unionflow.server.repository.AlertConfigurationRepository;
|
||||
import dev.lions.unionflow.server.repository.SystemAlertRepository;
|
||||
import dev.lions.unionflow.server.repository.SystemLogRepository;
|
||||
import io.quarkus.arc.Arc;
|
||||
import io.quarkus.scheduler.Scheduled;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
@@ -47,6 +48,10 @@ public class AlertMonitoringService {
|
||||
@Scheduled(cron = "0 * * * * ?") // Toutes les minutes à la seconde 0
|
||||
@Transactional
|
||||
public void monitorSystemMetrics() {
|
||||
// Guard contre l'exécution pendant le shutdown Quarkus (Arc.container() null → NPE)
|
||||
if (!Arc.container().isRunning()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
log.debug("Running scheduled system metrics monitoring...");
|
||||
|
||||
|
||||
@@ -50,10 +50,12 @@ quarkus.log.category."io.quarkus.security".level=INFO
|
||||
wave.mock.enabled=true
|
||||
wave.redirect.base.url=http://localhost:8085
|
||||
|
||||
# OIDC client "admin-service" — service account pour appels admin vers lions-user-manager
|
||||
quarkus.oidc-client.admin-service.auth-server-url=http://localhost:8180/realms/unionflow
|
||||
# OIDC client "admin-service" — service account pour appels vers lions-user-manager
|
||||
# Utilise le realm lions-user-manager (cohérent avec le serveur LUM qui valide ce realm)
|
||||
# Le client unionflow-server existe dans lions-user-manager realm avec ce secret
|
||||
quarkus.oidc-client.admin-service.auth-server-url=http://localhost:8180/realms/lions-user-manager
|
||||
quarkus.oidc-client.admin-service.client-id=unionflow-server
|
||||
quarkus.oidc-client.admin-service.credentials.secret=unionflow-secret-2025
|
||||
quarkus.oidc-client.admin-service.credentials.secret=Esj0DzyRt7wSPtcePDae1dQQdqmQxlJm
|
||||
quarkus.oidc-client.admin-service.grant.type=client
|
||||
quarkus.oidc-client.admin-service.tls.verification=none
|
||||
quarkus.oidc-client.admin-service.early-tokens-acquisition=true
|
||||
|
||||
@@ -58,9 +58,10 @@ quarkus.log.category."org.jboss.resteasy".level=WARN
|
||||
quarkus.rest-client.lions-user-manager-api.url=${LIONS_USER_MANAGER_URL:http://lions-user-manager:8081}
|
||||
|
||||
# OIDC client "admin-service" — service account pour appels admin vers lions-user-manager
|
||||
quarkus.oidc-client.admin-service.auth-server-url=${KEYCLOAK_AUTH_SERVER_URL:https://security.lions.dev/realms/unionflow}
|
||||
# Utilise le realm lions-user-manager (cohérent avec le serveur LUM qui valide ce realm)
|
||||
quarkus.oidc-client.admin-service.auth-server-url=${KEYCLOAK_LUM_AUTH_SERVER_URL:https://security.lions.dev/realms/lions-user-manager}
|
||||
quarkus.oidc-client.admin-service.client-id=unionflow-server
|
||||
quarkus.oidc-client.admin-service.credentials.secret=${KEYCLOAK_CLIENT_SECRET}
|
||||
quarkus.oidc-client.admin-service.credentials.secret=${KEYCLOAK_ADMIN_SERVICE_SECRET:${KEYCLOAK_CLIENT_SECRET}}
|
||||
quarkus.oidc-client.admin-service.grant.type=client
|
||||
|
||||
# Wave Money — Production
|
||||
|
||||
@@ -45,6 +45,7 @@ quarkus.hibernate-orm.metrics.enabled=false
|
||||
quarkus.flyway.migrate-at-start=true
|
||||
quarkus.flyway.baseline-on-migrate=true
|
||||
quarkus.flyway.baseline-version=0
|
||||
quarkus.flyway.out-of-order=true
|
||||
|
||||
# Configuration Keycloak OIDC — base commune
|
||||
quarkus.oidc.application-type=service
|
||||
|
||||
@@ -2,9 +2,13 @@ package dev.lions.unionflow.server.resource;
|
||||
|
||||
import static io.restassured.RestAssured.given;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.reference.request.CreateTypeReferenceRequest;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.reference.response.TypeReferenceResponse;
|
||||
import dev.lions.unionflow.server.service.TypeReferenceService;
|
||||
import io.quarkus.security.identity.SecurityIdentity;
|
||||
@@ -83,6 +87,40 @@ class TypeOrganisationReferenceResourceTest {
|
||||
.contentType(ContentType.JSON);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "superadmin@test.com", roles = {"SUPER_ADMIN"})
|
||||
void creerType_withCategorieAndModules_forwardsFields() {
|
||||
TypeReferenceResponse response = TypeReferenceResponse.builder()
|
||||
.domaine("TYPE_ORGANISATION")
|
||||
.code("TONTINE")
|
||||
.libelle("Tontine")
|
||||
.categorie("FINANCIER_SOLIDAIRE")
|
||||
.modulesRequis("TONTINE,COTISATIONS")
|
||||
.build();
|
||||
response.setId(UUID.randomUUID());
|
||||
ArgumentCaptor<CreateTypeReferenceRequest> captor =
|
||||
ArgumentCaptor.forClass(CreateTypeReferenceRequest.class);
|
||||
when(typeReferenceService.creer(captor.capture())).thenReturn(response);
|
||||
|
||||
String body = """
|
||||
{"code":"TONTINE","libelle":"Tontine","domaine":"TYPE_ORGANISATION",
|
||||
"categorie":"FINANCIER_SOLIDAIRE","modulesRequis":"TONTINE,COTISATIONS"}
|
||||
""";
|
||||
|
||||
given()
|
||||
.contentType(ContentType.JSON)
|
||||
.body(body)
|
||||
.when()
|
||||
.post(BASE_PATH)
|
||||
.then()
|
||||
.statusCode(201);
|
||||
|
||||
CreateTypeReferenceRequest captured = captor.getValue();
|
||||
assertEquals("FINANCIER_SOLIDAIRE", captured.categorie());
|
||||
assertEquals("TONTINE,COTISATIONS", captured.modulesRequis());
|
||||
assertEquals("TYPE_ORGANISATION", captured.domaine());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "superadmin@test.com", roles = {"SUPER_ADMIN"})
|
||||
void creerType_illegalArg_returns400() {
|
||||
|
||||
@@ -201,7 +201,7 @@ class TypeReferenceServiceTest {
|
||||
UpdateTypeReferenceRequest updateRequest = new UpdateTypeReferenceRequest(
|
||||
null, // code (pas de changement)
|
||||
"Nouveau libellé",
|
||||
null, null, null, null, null, null, null);
|
||||
null, null, null, null, null, null, null, null, null);
|
||||
|
||||
TypeReferenceResponse updated = typeReferenceService.modifier(created.getId(), updateRequest);
|
||||
assertThat(updated.getLibelle()).isEqualTo("Nouveau libellé");
|
||||
@@ -212,7 +212,7 @@ class TypeReferenceServiceTest {
|
||||
@DisplayName("modifier avec UUID inexistant lance IllegalArgumentException")
|
||||
void modifier_inexistant_throws() {
|
||||
UpdateTypeReferenceRequest updateRequest = new UpdateTypeReferenceRequest(
|
||||
null, "Libellé", null, null, null, null, null, null, null);
|
||||
null, "Libellé", null, null, null, null, null, null, null, null, null);
|
||||
|
||||
assertThatThrownBy(() -> typeReferenceService.modifier(UUID.randomUUID(), updateRequest))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
@@ -317,7 +317,7 @@ class TypeReferenceServiceTest {
|
||||
.build());
|
||||
|
||||
UpdateTypeReferenceRequest updateRequest = new UpdateTypeReferenceRequest(
|
||||
newCode, null, null, null, null, null, null, null, null);
|
||||
newCode, null, null, null, null, null, null, null, null, null, null);
|
||||
|
||||
TypeReferenceResponse updated = typeReferenceService.modifier(created.getId(), updateRequest);
|
||||
assertThat(updated.getCode()).isEqualTo(newCode.toUpperCase());
|
||||
@@ -340,7 +340,7 @@ class TypeReferenceServiceTest {
|
||||
.build());
|
||||
|
||||
UpdateTypeReferenceRequest updateRequest = new UpdateTypeReferenceRequest(
|
||||
"CHANGED_CODE", null, null, null, null, null, null, null, null);
|
||||
"CHANGED_CODE", null, null, null, null, null, null, null, null, null, null);
|
||||
|
||||
assertThatThrownBy(() -> typeReferenceService.modifier(created.getId(), updateRequest))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
@@ -365,7 +365,7 @@ class TypeReferenceServiceTest {
|
||||
|
||||
// Même code (case-insensitive) → !equalsIgnoreCase = false → if body skipped
|
||||
UpdateTypeReferenceRequest updateRequest = new UpdateTypeReferenceRequest(
|
||||
code.toLowerCase(), "Nouveau libelle", null, null, null, null, null, null, null);
|
||||
code.toLowerCase(), "Nouveau libelle", null, null, null, null, null, null, null, null, null);
|
||||
|
||||
TypeReferenceResponse updated = typeReferenceService.modifier(created.getId(), updateRequest);
|
||||
assertThat(updated).isNotNull();
|
||||
@@ -457,7 +457,7 @@ class TypeReferenceServiceTest {
|
||||
|
||||
// Modifier le code avec l'organisation non-null → L195-197 : orgId = entity.getOrganisation().getId()
|
||||
UpdateTypeReferenceRequest updateRequest = new UpdateTypeReferenceRequest(
|
||||
newCode, null, null, null, null, null, null, null, null);
|
||||
newCode, null, null, null, null, null, null, null, null, null, null);
|
||||
|
||||
TypeReferenceResponse updated = typeReferenceService.modifier(created.getId(), updateRequest);
|
||||
|
||||
@@ -491,7 +491,9 @@ class TypeReferenceServiceTest {
|
||||
"warning", // severity
|
||||
10, // ordreAffichage
|
||||
true, // estDefaut
|
||||
false // actif
|
||||
false, // actif
|
||||
null, // categorie
|
||||
null // modulesRequis
|
||||
);
|
||||
|
||||
TypeReferenceResponse updated = typeReferenceService.modifier(created.getId(), updateRequest);
|
||||
|
||||
Reference in New Issue
Block a user