Compare commits

...

6 Commits

Author SHA1 Message Date
34056b7c03 fix: 3 tests
Some checks failed
CI/CD Pipeline / pipeline (push) Failing after 2m46s
- DashboardWebSocketEndpoint: null-check on onMessage to avoid NPE when client sends null payload
- ConversationParticipantTest.equalsHashCode: share Conversation+Membre instances (random UUIDs inside newConversation/newMembre were defeating equals)
- MessageTest.equalsHashCode: same fix pattern
2026-04-23 13:01:50 +00:00
e503c6c0a1 fix: empty parent.relativePath to allow standalone clones (CI builds from registry)
Some checks failed
CI/CD Pipeline / pipeline (push) Failing after 3m7s
2026-04-23 11:24:53 +00:00
2c1c3b6e40 chore: bump unionflow-server-api to 1.0.6 (adds ville/pays to OrganisationSummaryResponse)
Some checks failed
CI/CD Pipeline / pipeline (push) Failing after 3m27s
2026-04-23 02:19:26 +00:00
9ab825c5e0 ci: use lionsctl-ci image; drop actions/checkout dependency
Some checks failed
CI/CD Pipeline / pipeline (push) Failing after 3m55s
2026-04-22 21:38:54 +00:00
2b48bc18b3 ci: enable lionsctl pipeline via lionsctl-ci image
Some checks failed
CI/CD Pipeline / pipeline (push) Failing after 34s
2026-04-22 19:42:43 +00:00
dahoud
00602d963b ci: ajouter workflow Gitea Actions (lionsctl pipeline auto-deploy sur push main)
Some checks failed
CI/CD Lions Pipeline / Build + Push + Deploy (push) Failing after 22s
2026-04-22 16:00:51 +00:00
5 changed files with 181 additions and 60 deletions

76
.gitea/workflows/ci.yml Normal file
View File

@@ -0,0 +1,76 @@
# ============================================================================
# Template — .gitea/workflows/ci.yml
# Drop this file into each app repo (adjust LIONS_JAVA_VERSION +
# LIONS_APP_NAME + optional --deploy-repo-url). It runs inside the
# registry.lions.dev/lionsdev/lionsctl-ci:latest image, so lionsctl,
# kubectl, helm, docker CLI, JDK 17+21 and Maven are all pre-installed.
#
# Required Gitea repo secrets:
# LIONS_REGISTRY_USERNAME (typically "lionsregistry")
# LIONS_REGISTRY_PASSWORD
# LIONS_GIT_USERNAME (typically "lionsdev")
# LIONS_GIT_ACCESS_TOKEN (Gitea PAT with write:repository, write:package)
# LIONS_GIT_PASSWORD (Gitea password for same user — Helm mode)
# SMTP_HOST SMTP_PORT SMTP_USERNAME SMTP_PASSWORD SMTP_FROM
# ============================================================================
name: CI/CD Pipeline
on:
push:
branches: [ main ]
workflow_dispatch: {}
env:
# Adjust per repo:
# - unionflow-server-impl-quarkus -> 21
# - all others -> 17
LIONS_JAVA_VERSION: "21"
LIONS_CLUSTER: "k1"
jobs:
pipeline:
runs-on: ubuntu-latest
container:
image: registry.lions.dev/lionsdev/lionsctl-ci:latest
credentials:
username: ${{ secrets.LIONS_REGISTRY_USERNAME }}
password: ${{ secrets.LIONS_REGISTRY_PASSWORD }}
# Mount the host docker socket so `docker build/push` inside the
# container hits the runner's daemon (DinD-free).
volumes:
- /var/run/docker.sock:/var/run/docker.sock
steps:
- name: Show tooling
run: |
lionsctl --version || true
docker --version
kubectl version --client=true
helm version --short
mvn --version | head -n2
- name: Pipeline deploy
env:
LIONS_REGISTRY_USERNAME: ${{ secrets.LIONS_REGISTRY_USERNAME }}
LIONS_REGISTRY_PASSWORD: ${{ secrets.LIONS_REGISTRY_PASSWORD }}
LIONS_GIT_USERNAME: ${{ secrets.LIONS_GIT_USERNAME }}
LIONS_GIT_ACCESS_TOKEN: ${{ secrets.LIONS_GIT_ACCESS_TOKEN }}
LIONS_GIT_PASSWORD: ${{ secrets.LIONS_GIT_PASSWORD }}
SMTP_HOST: ${{ secrets.SMTP_HOST }}
SMTP_PORT: ${{ secrets.SMTP_PORT }}
SMTP_USERNAME: ${{ secrets.SMTP_USERNAME }}
SMTP_PASSWORD: ${{ secrets.SMTP_PASSWORD }}
SMTP_FROM: ${{ secrets.SMTP_FROM }}
# No actions/checkout — lionsctl clones internally using git_access_token.
run: |
# For btpxpress-backend add: --deploy-repo-url https://git.lions.dev/lionsdev/btpxpress-server-k1
# For btpxpress-frontend add: --deploy-repo-url https://git.lions.dev/lionsdev/btpxpress-client-k1
lionsctl pipeline \
-u ${{ gitea.server_url }}/${{ gitea.repository }} \
-b ${{ gitea.ref_name }} \
-j ${{ env.LIONS_JAVA_VERSION }} \
-e production \
-c ${{ env.LIONS_CLUSTER }} \
-p prod \
--deploy-repo-url https://git.lions.dev/lionsdev/unionflow-server-impl-quarkus-k1 \
-m admin@lions.dev

41
pom.xml
View File

@@ -7,8 +7,8 @@
<parent>
<groupId>dev.lions.unionflow</groupId>
<artifactId>unionflow-parent</artifactId>
<version>1.0.5</version>
<relativePath>../unionflow-server-api/parent-pom.xml</relativePath>
<version>1.0.6</version>
<relativePath/> <!-- Force resolution from Maven repo (Gitea); enables standalone clones -->
</parent>
<artifactId>unionflow-server-impl-quarkus</artifactId>
@@ -18,16 +18,17 @@
<description>Implémentation Quarkus du serveur UnionFlow</description>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<maven.compiler.release>21</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<quarkus.platform.version>3.15.1</quarkus.platform.version>
<quarkus.platform.version>3.20.0</quarkus.platform.version>
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
<!-- Jacoco -->
<jacoco.version>0.8.11</jacoco.version>
<jacoco.version>0.8.12</jacoco.version>
</properties>
<dependencyManagement>
@@ -47,7 +48,7 @@
<dependency>
<groupId>dev.lions.unionflow</groupId>
<artifactId>unionflow-server-api</artifactId>
<version>1.0.5</version>
<version>1.0.6</version>
</dependency>
<!-- Lions User Manager API (pour DTOs et client Keycloak) -->
@@ -122,11 +123,6 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-messaging-kafka</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-reactive-messaging-kafka</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-mailer</artifactId>
@@ -141,6 +137,10 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-health</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-micrometer-registry-prometheus</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-cache</artifactId>
@@ -215,6 +215,20 @@
<version>1.3.30</version>
</dependency>
<!-- Firebase Admin SDK — notifications push FCM (P2.2) -->
<dependency>
<groupId>com.google.firebase</groupId>
<artifactId>firebase-admin</artifactId>
<version>9.3.0</version>
<exclusions>
<!-- Éviter les conflits avec Netty/Vert.x de Quarkus -->
<exclusion>
<groupId>io.netty</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Tests -->
<dependency>
<groupId>io.quarkus</groupId>
@@ -269,6 +283,7 @@
<artifactId>smallrye-reactive-messaging-in-memory</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<repositories>

View File

@@ -1,43 +1,47 @@
package dev.lions.unionflow.server.resource;
import io.quarkus.websockets.next.OnClose;
import io.quarkus.websockets.next.OnOpen;
import io.quarkus.websockets.next.OnTextMessage;
import io.quarkus.websockets.next.WebSocket;
import io.quarkus.websockets.next.WebSocketConnection;
import org.jboss.logging.Logger;
/**
* Endpoint WebSocket pour le dashboard temps réel.
* Les clients mobiles et web se connectent ici pour recevoir les mises à jour.
* Types de messages supportés : stats_update, new_activity, event_update, notification, pong
*/
@WebSocket(path = "/ws/dashboard")
public class DashboardWebSocketEndpoint {
private static final Logger LOG = Logger.getLogger(DashboardWebSocketEndpoint.class);
@OnOpen
public String onOpen(WebSocketConnection connection) {
LOG.infof("WebSocket connection opened: %s", connection.id());
return "{\"type\":\"connected\",\"data\":{\"message\":\"Connected to UnionFlow Dashboard WebSocket\"}}";
}
@OnTextMessage
public String onMessage(String message, WebSocketConnection connection) {
LOG.debugf("WebSocket message received from %s: %s", connection.id(), message);
// Répondre aux pings avec un pong (heartbeat)
if ("ping".equalsIgnoreCase(message.trim()) || message.contains("\"type\":\"ping\"")) {
return "{\"type\":\"pong\",\"data\":{\"timestamp\":" + System.currentTimeMillis() + "}}";
}
// Accusé de réception pour les autres messages
return "{\"type\":\"ack\",\"data\":{\"received\":true}}";
}
@OnClose
public void onClose(WebSocketConnection connection) {
LOG.infof("WebSocket connection closed: %s", connection.id());
}
}
package dev.lions.unionflow.server.resource;
import io.quarkus.websockets.next.OnClose;
import io.quarkus.websockets.next.OnOpen;
import io.quarkus.websockets.next.OnTextMessage;
import io.quarkus.websockets.next.WebSocket;
import io.quarkus.websockets.next.WebSocketConnection;
import org.jboss.logging.Logger;
/**
* Endpoint WebSocket pour le dashboard temps réel.
* Les clients mobiles et web se connectent ici pour recevoir les mises à jour.
* Types de messages supportés : stats_update, new_activity, event_update, notification, pong
*/
@WebSocket(path = "/ws/dashboard")
public class DashboardWebSocketEndpoint {
private static final Logger LOG = Logger.getLogger(DashboardWebSocketEndpoint.class);
@OnOpen
public String onOpen(WebSocketConnection connection) {
LOG.infof("WebSocket connection opened: %s", connection.id());
return "{\"type\":\"connected\",\"data\":{\"message\":\"Connected to UnionFlow Dashboard WebSocket\"}}";
}
@OnTextMessage
public String onMessage(String message, WebSocketConnection connection) {
LOG.debugf("WebSocket message received from %s: %s", connection.id(), message);
if (message == null) {
return "{\"type\":\"ack\",\"data\":{\"received\":true}}";
}
// Répondre aux pings avec un pong (heartbeat)
if ("ping".equalsIgnoreCase(message.trim()) || message.contains("\"type\":\"ping\"")) {
return "{\"type\":\"pong\",\"data\":{\"timestamp\":" + System.currentTimeMillis() + "}}";
}
// Accusé de réception pour les autres messages
return "{\"type\":\"ack\",\"data\":{\"received\":true}}";
}
@OnClose
public void onClose(WebSocketConnection connection) {
LOG.infof("WebSocket connection closed: %s", connection.id());
}
}

View File

@@ -103,10 +103,23 @@ class ConversationParticipantTest {
@DisplayName("equals et hashCode")
void equalsHashCode() {
UUID id = UUID.randomUUID();
ConversationParticipant a = buildMinimal("PARTICIPANT");
// Partage les mêmes Conversation et Membre pour que Lombok equals
// sur les champs imbriqués donne true (les IDs imbriqués sont aléatoires
// dans newConversation/newMembre, donc il faut réutiliser les instances).
Conversation sharedConv = newConversation();
Membre sharedMembre = newMembre();
ConversationParticipant a = new ConversationParticipant();
a.setId(id);
ConversationParticipant b = buildMinimal("PARTICIPANT");
a.setConversation(sharedConv);
a.setMembre(sharedMembre);
a.setRoleDansConversation("PARTICIPANT");
a.setNotifier(true);
ConversationParticipant b = new ConversationParticipant();
b.setId(id);
b.setConversation(sharedConv);
b.setMembre(sharedMembre);
b.setRoleDansConversation("PARTICIPANT");
b.setNotifier(true);
assertThat(a).isEqualTo(b);
assertThat(a.hashCode()).isEqualTo(b.hashCode());
}

View File

@@ -143,10 +143,23 @@ class MessageTest {
@DisplayName("equals et hashCode")
void equalsHashCode() {
UUID id = UUID.randomUUID();
Message a = buildMinimal(TypeContenu.TEXTE);
// Partage Conversation et Membre pour que Lombok equals
// (récursif sur les champs) donne true — sinon les UUIDs aléatoires
// dans newConversation/newMembre cassent l'égalité.
Conversation sharedConv = newConversation();
Membre sharedExpediteur = newMembre();
Message a = new Message();
a.setId(id);
Message b = buildMinimal(TypeContenu.TEXTE);
a.setConversation(sharedConv);
a.setExpediteur(sharedExpediteur);
a.setTypeMessage(TypeContenu.TEXTE);
a.setContenu("Texte test");
Message b = new Message();
b.setId(id);
b.setConversation(sharedConv);
b.setExpediteur(sharedExpediteur);
b.setTypeMessage(TypeContenu.TEXTE);
b.setContenu("Texte test");
assertThat(a).isEqualTo(b);
assertThat(a.hashCode()).isEqualTo(b.hashCode());
}