Ajouter le modèle et le DAO des relations entre entités #55

Closed
opened 2026-07-20 21:47:06 +02:00 by fy59 · 0 comments
Owner

Ajouter le modèle et le DAO des relations entre entités

Contexte

Les entités OSINT peuvent être reliées entre elles afin de représenter les
liens découverts pendant une enquête.

Exemples :

  • une personne utilise une adresse e-mail ;
  • un pseudonyme appartient à un compte Instagram ;
  • un compte bancaire reçoit un virement ;
  • un domaine pointe vers une adresse IP ;
  • une organisation contrôle un site web.

Ces relations seront ensuite utilisées par le futur graphe interactif de
l'enquête.

Les données métier des relations doivent rester indépendantes de leur future
représentation graphique. Les coordonnées, dimensions et paramètres
d'affichage des nœuds ne doivent donc pas être ajoutés au modèle métier.

La table SQLite relations existe déjà dans le schéma V1.

Objectif

Ajouter un modèle métier RelationRecord et un DAO RelationDao permettant
de créer et de consulter les relations persistées entre les entités.

Modèle RelationRecord

Créer :

  • include/models/relation_record.h
  • src/models/relation_record.c

Le modèle doit être opaque et posséder les champs suivants :

  • identifier : UUID de la relation ;
  • source_entity_identifier : UUID de l'entité source ;
  • target_entity_identifier : UUID de l'entité cible ;
  • relation_type : code métier décrivant la relation ;
  • label : libellé facultatif ;
  • justification : justification facultative ;
  • confidence : niveau de confiance compris entre 0 et 100 ;
  • created_at : date UTC de création ;
  • updated_at : date UTC de dernière modification ;
  • status : statut métier de la relation.

Créer l'énumération suivante :

  • RELATION_STATUS_UNKNOWN
  • RELATION_STATUS_ACTIVE
  • RELATION_STATUS_ARCHIVED
  • RELATION_STATUS_DELETED
  • RELATION_STATUS_DISPUTED

RELATION_STATUS_UNKNOWN doit uniquement servir de valeur défensive et ne
doit jamais être persisté.

Validations du modèle

Le constructeur doit :

  • refuser un identifiant de relation qui n'est pas un UUID valide ;
  • refuser un identifiant d'entité source invalide ;
  • refuser un identifiant d'entité cible invalide ;
  • refuser une relation dont la source et la cible sont identiques ;
  • nettoyer les espaces autour des chaînes ;
  • refuser un type de relation vide ;
  • convertir un libellé vide en NULL ;
  • convertir une justification vide en NULL ;
  • refuser une confiance inférieure à 0 ou supérieure à 100 ;
  • refuser une date UTC invalide ;
  • refuser un statut non persistable ;
  • copier toutes les chaînes reçues.

La fonction de destruction doit accepter NULL.

Tous les accesseurs doivent accepter un modèle NULL et retourner une valeur
défensive cohérente.

DAO RelationDao

Créer :

  • include/dao/relation_dao.h
  • src/dao/relation_dao.c

Le DAO doit emprunter une connexion Database existante et ne jamais la
fermer.

Il doit exposer les opérations suivantes :

Construction

RelationDao *relation_dao_new(
    Database *database,
    GError **error
);

Destruction

void relation_dao_free(
    RelationDao *relation_dao
);

Insertion

gboolean relation_dao_insert(
    RelationDao *relation_dao,
    const RelationRecord *relation_record,
    GError **error
);

L'insertion doit :

  • utiliser une requête préparée ;
  • vérifier que l'entité source existe ;
  • vérifier que l'entité cible existe ;
  • refuser une relation entre une entité et elle-même ;
  • refuser un UUID de relation déjà utilisé ;
  • refuser un doublon possédant la même source, la même cible et le même type ;
  • conserver le sens source vers cible de la relation ;
  • ne jamais remplacer silencieusement une relation existante.

Recherche par identifiant

RelationRecord *relation_dao_find_by_identifier(
    RelationDao *relation_dao,
    const char *identifier,
    GError **error
);

Une relation absente doit retourner NULL sans produire d'erreur.

Liste complète

GPtrArray *relation_dao_list_all(
    RelationDao *relation_dao,
    GError **error
);

Le tableau doit :

  • contenir des RelationRecord ;
  • utiliser relation_record_free() comme fonction de destruction ;
  • appartenir à l'appelant ;
  • être trié par created_at, puis par UUID ;
  • être vide sans erreur lorsque la table ne contient aucune relation.

Comptage

gboolean relation_dao_count(
    RelationDao *relation_dao,
    guint64 *out_count,
    GError **error
);

Gestion des erreurs

Créer un domaine d'erreur propre à chaque composant :

  • RELATION_RECORD_ERROR
  • RELATION_DAO_ERROR

Le DAO doit différencier au minimum :

  • argument invalide ;
  • erreur d'allocation ;
  • erreur de préparation SQL ;
  • erreur de liaison SQL ;
  • erreur d'exécution SQL ;
  • violation de contrainte ;
  • erreur de lecture ;
  • modèle SQLite invalide ;
  • schéma absent ou incompatible ;
  • valeur numérique hors limites.

Les statements SQLite doivent être finalisés sur tous les chemins de sortie.

Tests du modèle

Créer :

tests/test_relation_record.c

Les tests doivent couvrir au minimum :

  • création complète valide ;
  • champs facultatifs absents ;
  • nettoyage des chaînes ;
  • UUID de relation invalide ;
  • UUID source invalide ;
  • UUID cible invalide ;
  • source identique à la cible ;
  • type de relation vide ;
  • confiance inférieure à 0 ;
  • confiance supérieure à 100 ;
  • date de création invalide ;
  • date de modification invalide ;
  • chaque statut valide ;
  • statut invalide ;
  • accesseurs appelés avec NULL ;
  • destruction avec NULL.

Tests du DAO

Créer :

tests/test_relation_dao.c

Les tests doivent utiliser une base SQLite temporaire initialisée avec le
schéma réel de l'application.

Ils doivent couvrir au minimum :

  • refus d'une connexion absente ;
  • création et destruction du DAO ;
  • insertion complète valide ;
  • insertion avec champs facultatifs absents ;
  • refus d'un UUID déjà utilisé ;
  • refus d'une entité source inexistante ;
  • refus d'une entité cible inexistante ;
  • refus d'une relation réflexive ;
  • refus d'un doublon source, cible et type ;
  • possibilité de créer plusieurs types de relation entre les mêmes entités ;
  • possibilité de créer les relations A vers B et B vers A ;
  • recherche d'une relation existante ;
  • recherche d'une relation absente ;
  • refus d'un identifiant de recherche invalide ;
  • liste vide ;
  • ordre stable de la liste ;
  • comptage d'une table vide ;
  • comptage de plusieurs relations ;
  • arguments invalides ;
  • contraintes de clés étrangères.

Makefile

Ajouter les deux exécutables de test :

tests/test_relation_record
tests/test_relation_dao

Ils doivent être :

  • construits par make test ;
  • exécutés par make test ;
  • supprimés par make clean.

La compilation doit conserver :

-std=c17 -Wall -Wextra -Wpedantic -Werror

Hors périmètre

Ce ticket ne couvre pas :

  • l'interface GTK ;
  • le graphe interactif ;
  • les positions graphiques des nœuds ;
  • la modification d'une relation existante ;
  • la suppression ou l'archivage depuis l'interface ;
  • l'association des relations aux preuves via relation_preuves ;
  • l'association aux éléments de chronologie ;
  • l'association aux hypothèses ;
  • une migration du schéma SQLite.

L'association entre relations et preuves fera l'objet d'un ticket séparé.

Critères d'acceptation

  • RelationRecord est opaque et valide les règles du schéma.
  • RelationDao emprunte correctement la connexion Database.
  • Une relation valide peut être insérée puis relue.
  • Les relations conservent leur orientation source vers cible.
  • Les doublons sont refusés proprement.
  • Les références vers des entités inexistantes sont refusées.
  • Les statuts SQLite sont convertis explicitement vers l'énumération C.
  • Les listes retournées possèdent leurs modèles.
  • Tous les statements sont finalisés.
  • Les tests ciblés passent.
  • make clean && make && make test réussit.
  • git diff --check ne retourne aucune erreur.
  • Aucun changement d'interface GTK n'est introduit.
  • Aucune donnée de disposition graphique n'est mélangée au modèle métier.
# Ajouter le modèle et le DAO des relations entre entités ## Contexte Les entités OSINT peuvent être reliées entre elles afin de représenter les liens découverts pendant une enquête. Exemples : - une personne utilise une adresse e-mail ; - un pseudonyme appartient à un compte Instagram ; - un compte bancaire reçoit un virement ; - un domaine pointe vers une adresse IP ; - une organisation contrôle un site web. Ces relations seront ensuite utilisées par le futur graphe interactif de l'enquête. Les données métier des relations doivent rester indépendantes de leur future représentation graphique. Les coordonnées, dimensions et paramètres d'affichage des nœuds ne doivent donc pas être ajoutés au modèle métier. La table SQLite `relations` existe déjà dans le schéma V1. ## Objectif Ajouter un modèle métier `RelationRecord` et un DAO `RelationDao` permettant de créer et de consulter les relations persistées entre les entités. ## Modèle `RelationRecord` Créer : - `include/models/relation_record.h` - `src/models/relation_record.c` Le modèle doit être opaque et posséder les champs suivants : - `identifier` : UUID de la relation ; - `source_entity_identifier` : UUID de l'entité source ; - `target_entity_identifier` : UUID de l'entité cible ; - `relation_type` : code métier décrivant la relation ; - `label` : libellé facultatif ; - `justification` : justification facultative ; - `confidence` : niveau de confiance compris entre 0 et 100 ; - `created_at` : date UTC de création ; - `updated_at` : date UTC de dernière modification ; - `status` : statut métier de la relation. Créer l'énumération suivante : - `RELATION_STATUS_UNKNOWN` - `RELATION_STATUS_ACTIVE` - `RELATION_STATUS_ARCHIVED` - `RELATION_STATUS_DELETED` - `RELATION_STATUS_DISPUTED` `RELATION_STATUS_UNKNOWN` doit uniquement servir de valeur défensive et ne doit jamais être persisté. ### Validations du modèle Le constructeur doit : - refuser un identifiant de relation qui n'est pas un UUID valide ; - refuser un identifiant d'entité source invalide ; - refuser un identifiant d'entité cible invalide ; - refuser une relation dont la source et la cible sont identiques ; - nettoyer les espaces autour des chaînes ; - refuser un type de relation vide ; - convertir un libellé vide en `NULL` ; - convertir une justification vide en `NULL` ; - refuser une confiance inférieure à 0 ou supérieure à 100 ; - refuser une date UTC invalide ; - refuser un statut non persistable ; - copier toutes les chaînes reçues. La fonction de destruction doit accepter `NULL`. Tous les accesseurs doivent accepter un modèle `NULL` et retourner une valeur défensive cohérente. ## DAO `RelationDao` Créer : - `include/dao/relation_dao.h` - `src/dao/relation_dao.c` Le DAO doit emprunter une connexion `Database` existante et ne jamais la fermer. Il doit exposer les opérations suivantes : ### Construction ```c RelationDao *relation_dao_new( Database *database, GError **error ); ``` ### Destruction ```c void relation_dao_free( RelationDao *relation_dao ); ``` ### Insertion ```c gboolean relation_dao_insert( RelationDao *relation_dao, const RelationRecord *relation_record, GError **error ); ``` L'insertion doit : - utiliser une requête préparée ; - vérifier que l'entité source existe ; - vérifier que l'entité cible existe ; - refuser une relation entre une entité et elle-même ; - refuser un UUID de relation déjà utilisé ; - refuser un doublon possédant la même source, la même cible et le même type ; - conserver le sens source vers cible de la relation ; - ne jamais remplacer silencieusement une relation existante. ### Recherche par identifiant ```c RelationRecord *relation_dao_find_by_identifier( RelationDao *relation_dao, const char *identifier, GError **error ); ``` Une relation absente doit retourner `NULL` sans produire d'erreur. ### Liste complète ```c GPtrArray *relation_dao_list_all( RelationDao *relation_dao, GError **error ); ``` Le tableau doit : - contenir des `RelationRecord` ; - utiliser `relation_record_free()` comme fonction de destruction ; - appartenir à l'appelant ; - être trié par `created_at`, puis par UUID ; - être vide sans erreur lorsque la table ne contient aucune relation. ### Comptage ```c gboolean relation_dao_count( RelationDao *relation_dao, guint64 *out_count, GError **error ); ``` ## Gestion des erreurs Créer un domaine d'erreur propre à chaque composant : - `RELATION_RECORD_ERROR` - `RELATION_DAO_ERROR` Le DAO doit différencier au minimum : - argument invalide ; - erreur d'allocation ; - erreur de préparation SQL ; - erreur de liaison SQL ; - erreur d'exécution SQL ; - violation de contrainte ; - erreur de lecture ; - modèle SQLite invalide ; - schéma absent ou incompatible ; - valeur numérique hors limites. Les statements SQLite doivent être finalisés sur tous les chemins de sortie. ## Tests du modèle Créer : ```text tests/test_relation_record.c ``` Les tests doivent couvrir au minimum : - création complète valide ; - champs facultatifs absents ; - nettoyage des chaînes ; - UUID de relation invalide ; - UUID source invalide ; - UUID cible invalide ; - source identique à la cible ; - type de relation vide ; - confiance inférieure à 0 ; - confiance supérieure à 100 ; - date de création invalide ; - date de modification invalide ; - chaque statut valide ; - statut invalide ; - accesseurs appelés avec `NULL` ; - destruction avec `NULL`. ## Tests du DAO Créer : ```text tests/test_relation_dao.c ``` Les tests doivent utiliser une base SQLite temporaire initialisée avec le schéma réel de l'application. Ils doivent couvrir au minimum : - refus d'une connexion absente ; - création et destruction du DAO ; - insertion complète valide ; - insertion avec champs facultatifs absents ; - refus d'un UUID déjà utilisé ; - refus d'une entité source inexistante ; - refus d'une entité cible inexistante ; - refus d'une relation réflexive ; - refus d'un doublon source, cible et type ; - possibilité de créer plusieurs types de relation entre les mêmes entités ; - possibilité de créer les relations `A vers B` et `B vers A` ; - recherche d'une relation existante ; - recherche d'une relation absente ; - refus d'un identifiant de recherche invalide ; - liste vide ; - ordre stable de la liste ; - comptage d'une table vide ; - comptage de plusieurs relations ; - arguments invalides ; - contraintes de clés étrangères. ## Makefile Ajouter les deux exécutables de test : ```text tests/test_relation_record tests/test_relation_dao ``` Ils doivent être : - construits par `make test` ; - exécutés par `make test` ; - supprimés par `make clean`. La compilation doit conserver : ```text -std=c17 -Wall -Wextra -Wpedantic -Werror ``` ## Hors périmètre Ce ticket ne couvre pas : - l'interface GTK ; - le graphe interactif ; - les positions graphiques des nœuds ; - la modification d'une relation existante ; - la suppression ou l'archivage depuis l'interface ; - l'association des relations aux preuves via `relation_preuves` ; - l'association aux éléments de chronologie ; - l'association aux hypothèses ; - une migration du schéma SQLite. L'association entre relations et preuves fera l'objet d'un ticket séparé. ## Critères d'acceptation - [x] `RelationRecord` est opaque et valide les règles du schéma. - [x] `RelationDao` emprunte correctement la connexion `Database`. - [x] Une relation valide peut être insérée puis relue. - [x] Les relations conservent leur orientation source vers cible. - [x] Les doublons sont refusés proprement. - [x] Les références vers des entités inexistantes sont refusées. - [x] Les statuts SQLite sont convertis explicitement vers l'énumération C. - [x] Les listes retournées possèdent leurs modèles. - [x] Tous les statements sont finalisés. - [x] Les tests ciblés passent. - [x] `make clean && make && make test` réussit. - [x] `git diff --check` ne retourne aucune erreur. - [x] Aucun changement d'interface GTK n'est introduit. - [x] Aucune donnée de disposition graphique n'est mélangée au modèle métier.
fy59 closed this issue 2026-07-20 22:18:29 +02:00
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: fy59/labfy-investigation#55
No description provided.