Skip to main content

SDK Python officiel de l'écosystème SAHGES

Project description

SAHGES SDK

SDK Python officiel pour l'écosystème SAHGES. Facilite l'intégration avec les services SAHGES via une interface Python simple et robuste avec types stricts.

Python Version License

📋 Prérequis

  • Python 3.10 ou supérieur
  • pip pour l'installation des dépendances

🚀 Installation

pip install sahges-sdk

Ou depuis les sources :

git clone https://gitlab.com/florianogomez/sahges-sdk.git
cd sahges-sdk
pip install -e .

🎯 Démarrage rapide

Authentification

from sahges_sdk.auth import SahgesAuthClient

# Initialiser le client
client = SahgesAuthClient(
    client_id="votre_client_id",
    client_secret="votre_client_secret"
)

# Se connecter - retourne un objet SahgesLoginResponse
response = client.login(payload={
    "credential": "user@example.com",
    "password": "mot_de_passe"
})

# ✅ Accès par attribut (dataclass)
print(f"Token: {response.access_token}")
print(f"Email: {response.user.email}")
print(f"Nom: {response.user.first_name} {response.user.last_name}")
print(f"Organisation: {response.user.organization.name}")
print(f"Rôle: {response.user.role}")

Gestion de documents

from sahges_sdk.docs import SahgesDocsClient
from pathlib import Path

# Initialiser le client
docs = SahgesDocsClient(
    client_id="votre_client_id",
    client_secret="votre_client_secret"
)

# Uploader un document - retourne un objet SahgesDocument
document = docs.create(
    title="Mon document",
    file_path=Path("document.pdf"),
    visibility="ORGANIZATION",
    status="DRAFT"
)

# ✅ Accès par attribut
print(f"ID: {document.id}")
print(f"Titre: {document.title}")
print(f"Taille: {document.file_size} bytes")
print(f"Format: {document.file_format}")
print(f"Créé le: {document.created_at}")

# Lister les documents - retourne List[SahgesDocumentListItem]
documents = docs.list(payload={"status": "VALIDATED"})

for doc in documents:
    print(f"- {doc.title} ({doc.file_format})")

⚙️ Configuration

Variables d'environnement

Créez un fichier .env :

# Credentials API (obligatoire)
SAHGES_CLIENT_ID=votre_client_id
SAHGES_CLIENT_SECRET=votre_client_secret

# URLs des services (optionnel)
SAHGES_AUTHENTICATION_BASE_URL=https://api.sahges.com
SAHGES_DOCS_BASE_URL=https://docs.sahges.com

Utilisation avec .env

import os
from dotenv import load_dotenv
from sahges_sdk.auth import SahgesAuthClient

load_dotenv()

client = SahgesAuthClient(
    client_id=os.getenv('SAHGES_CLIENT_ID'),
    client_secret=os.getenv('SAHGES_CLIENT_SECRET')
)

📖 Guide d'utilisation

Module Auth - Authentification

Le module Auth fournit toutes les fonctionnalités d'authentification et de gestion de session.

1. Connexion (Login)

from sahges_sdk.auth import SahgesAuthClient

client = SahgesAuthClient(
    client_id="votre_client_id",
    client_secret="votre_client_secret"
)

# Se connecter
response = client.login(payload={
    "credential": "user@example.com",  # Email ou téléphone
    "password": "votre_mot_de_passe"
})

# SahgesLoginResponse est une dataclass avec:
# - access_token: str
# - refresh_token: Optional[str]
# - user: AuthUser

print(f"Token d'accès: {response.access_token}")
print(f"Token de rafraîchissement: {response.refresh_token}")
print(f"Utilisateur: {response.user.email}")

2. Rafraîchissement de token (Refresh)

# Rafraîchir le token sans redemander le mot de passe
new_tokens = client.refresh(payload={
    "refresh_token": response.refresh_token
})

# SahgesRefreshResponse est identique à SahgesLoginResponse
print(f"Nouveau token: {new_tokens.access_token}")
print(f"Utilisateur: {new_tokens.user.email}")  # Inclus dans la réponse

3. Introspection

# Obtenir les informations de l'utilisateur connecté
user = client.introspect(access_token=response.access_token)

# AuthUser est une dataclass avec:
# - id: UUID
# - email: str
# - first_name: str
# - last_name: str
# - phone: Optional[str]
# - is_active: bool
# - is_using_default_password: bool
# - role: SahgesAuthUserRoleEnum (USER, ADMIN, SUPERADMIN)
# - organization: SahgesAuthOrganization

print(f"ID: {user.id}")
print(f"Email: {user.email}")
print(f"Nom complet: {user.first_name} {user.last_name}")
print(f"Téléphone: {user.phone}")
print(f"Actif: {user.is_active}")
print(f"Rôle: {user.role}")
print(f"Organisation: {user.organization.name}")
print(f"Type org: {user.organization.type}")

4. Déconnexion (Logout)

# Se déconnecter - retourne True en cas de succès
result = client.logout(access_token=response.access_token)

print(result)  # True

# En cas d'erreur, lève SahgesAuthenticationError

5. Mot de passe oublié (Forgot Password)

# Demander un lien de réinitialisation par email
response = client.forgot_password(payload={
    "credential": "user@example.com"  # Email ou téléphone
})

# SahgesForgotPasswordResponse contient:
# - message: str
print(response.message)  # "Email de réinitialisation envoyé"

6. Valider le token de réinitialisation

# Vérifier que le token reçu par email est valide
token = "token_reçu_par_email"

challenge = client.reset_password_challenge(token=token)

# SahgesResetPasswordChallengeResponse contient:
# - valid: bool
# - message: Optional[str]

if challenge.valid:
    print("Token valide, vous pouvez réinitialiser")
else:
    print(f"Token invalide: {challenge.message}")

7. Réinitialiser le mot de passe

# Changer le mot de passe avec le token valide
result = client.reset_password(payload={
    "token": token,
    "new_password": "nouveau_mot_de_passe_securise",
    "new_password_confirmation": "nouveau_mot_de_passe_securise"
})

# SahgesResetPasswordResponse contient:
# - user: SahgesResetPasswordUser (version simplifiée)
# - redirect_url: Optional[str]

print(f"Mot de passe changé pour: {result.user.id}")
if result.redirect_url:
    print(f"Redirection vers: {result.redirect_url}")

Cycle de vie complet d'une session

from sahges_sdk.auth import SahgesAuthClient

client = SahgesAuthClient(client_id="...", client_secret="...")

# 1. Connexion
login = client.login({"credential": "user@example.com", "password": "pass"})
access_token = login.access_token
refresh_token = login.refresh_token

# 2. Utiliser le token pour les requêtes...
user = client.introspect(access_token=access_token)
print(f"Connecté: {user.email}")

# 3. Rafraîchir avant expiration
refreshed = client.refresh({"refresh_token": refresh_token})
access_token = refreshed.access_token
refresh_token = refreshed.refresh_token

# 4. Déconnexion
client.logout(access_token=access_token)

Module Docs - Gestion documentaire

Le module Docs gère l'upload, le stockage et le partage de documents avec traitement IA.

1. Créer un document (Upload)

from sahges_sdk.docs import SahgesDocsClient
from pathlib import Path

docs = SahgesDocsClient(client_id="...", client_secret="...")

# Créer avec upload de fichier
document = docs.create(
    title="Contrat client XYZ",
    file_path=Path("/path/to/contrat.pdf"),
    description="Contrat commercial 2025",
    visibility="ORGANIZATION",  # PRIVATE, ORGANIZATION, PUBLIC
    status="DRAFT",              # DRAFT, PENDING, VALIDATED, ARCHIVED
    category="legal",
    tags=["contrat", "2025", "client-xyz"]
)

# SahgesDocument est une dataclass complète avec:
# - id: UUID
# - title: str
# - visibility: SahgesDocumentVisibilityEnum
# - status: SahgesDocumentStatusEnum
# - file_name: str
# - file_size: int
# - file_mime_type: str
# - file_format: SahgesDocumentFileFormatEnum
# - sha256_hash: str
# - preview_generated: bool
# - created_at: datetime
# - updated_at: datetime
# + 15+ champs optionnels (description, tags, ocr_text, ai_summary, etc.)

print(f"SahgesDocument créé: {document.id}")
print(f"Fichier: {document.file_name} ({document.file_size} bytes)")
print(f"Format: {document.file_format}")
print(f"Hash SHA-256: {document.sha256_hash}")

# Vérifier les résultats du traitement IA
if document.ocr_text:
    print(f"Texte OCR disponible: {len(document.ocr_text)} caractères")
if document.ai_summary:
    print(f"Résumé IA: {document.ai_summary}")
if document.ai_metadata:
    print(f"Métadonnées IA: {document.ai_metadata}")

2. Lister les documents

# Liste simple - retourne List[SahgesDocumentListItem]
documents = docs.list(payload={})

for doc in documents:
    # SahgesDocumentListItem est une version simplifiée
    print(f"- {doc.title}")
    print(f"  ID: {doc.id}")
    print(f"  Format: {doc.file_format}")
    print(f"  Taille: {doc.file_size} bytes")
    print(f"  Créé: {doc.created_at}")

# Avec filtres
documents = docs.list(payload={
    "page": 1,
    "search": "contrat",           # Recherche textuelle
    "visibility": "ORGANIZATION",   # Filtre par visibilité
    "status": "VALIDATED",          # Filtre par statut
    "category": "legal",            # Filtre par catégorie
    "owner_only": True,             # Mes documents uniquement
    "shared_with_me": False         # Exclure documents partagés
})

print(f"Trouvé {len(documents)} document(s)")

3. Récupérer un document complet

# Obtenir tous les détails d'un document
document = docs.find(payload={
    "document_id": "550e8400-e29b-41d4-a716-446655440000"
})

# SahgesDocument complet avec tous les champs
print(f"Titre: {document.title}")
print(f"Description: {document.description}")
print(f"Catégorie: {document.category}")
print(f"Tags: {document.tags}")
print(f"Propriétaire: {document.owner_auth_user_id}")
print(f"Organisation: {document.organization_id}")
print(f"Métadonnées: {document.document_metadata}")

# Traitement IA
if document.ocr_text:
    print(f"OCR: {document.ocr_text[:200]}...")
if document.audio_transcription:
    print(f"Transcription: {document.audio_transcription[:200]}...")

4. Mettre à jour un document

# Modifier les métadonnées d'un document
updated = docs.update(payload={
    "document_id": document.id,
    "title": "Nouveau titre",
    "description": "Nouvelle description",
    "status": "VALIDATED",
    "category": "finance",
    "tags": ["important", "2025"]
})

print(f"Mis à jour: {updated.title}")

# Changer uniquement la visibilité
updated = docs.update_visibility(payload={
    "document_id": document.id,
    "visibility": "PUBLIC"
})

print(f"Nouvelle visibilité: {updated.visibility}")

5. Télécharger un document

# Télécharger vers un fichier
docs.download(
    document_id=document.id,
    output_path="/path/to/save/document.pdf"
)

# Ou obtenir le contenu en bytes
file_bytes = docs.download(document_id=document.id)
# Traiter les bytes directement...
print(f"Téléchargé {len(file_bytes)} bytes")

6. Supprimer un document

# Supprimer définitivement
result = docs.delete(document_id=document.id)

print(result)  # True

# En cas d'erreur, lève SahgesRequestError

7. Partager un document

from datetime import datetime, timedelta

# Créer un partage avec permissions
share = docs.share_create(payload={
    "document_id": document.id,
    "shared_with_auth_user_id": "user-uuid-du-collegue",
    "permission": "VIEW",  # VIEW, EDIT, MANAGE
    "expires_at": datetime.now() + timedelta(days=30)
})

# SahgesDocumentShareCreateResponse contient:
# - id: UUID
# - document_id: UUID
# - shared_with_auth_user_id: UUID
# - shared_by_auth_user_id: UUID
# - permission: SahgesDocumentSharePermissionEnum
# - created_at: datetime
# - expires_at: Optional[datetime]

print(f"Partage créé: {share.id}")
print(f"Permission: {share.permission}")
print(f"Expire le: {share.expires_at}")

8. Gérer les partages

# Lister tous les partages d'un document
shares = docs.share_list(payload={
    "document_id": document.id
})

# List[SahgesDocumentShare]
for share in shares:
    print(f"Partagé avec: {share.shared_with.email}")
    print(f"  Permission: {share.permission}")
    print(f"  Par: {share.shared_by.email}")
    print(f"  Le: {share.created_at}")

# Révoquer un partage
if shares:
    result = docs.share_delete(payload={
        "document_id": document.id,
        "share_id": shares[0].id
    })
    print(result)  # True

9. Endpoints clients (accès restreint)

Pour les applications tierces avec permissions limitées :

# Liste (ORGANIZATION + PUBLIC uniquement)
documents = docs.clients_list(payload={
    "search": "facture",
    "category": "comptabilite"
})

# Créer (visibilité forcée à ORGANIZATION)
document = docs.clients_create(
    title="Facture janvier",
    file_path=Path("facture.pdf"),
    status="VALIDATED"
)

# Récupérer
doc = docs.clients_find(payload={"document_id": document.id})

# Télécharger
docs.clients_download(
    document_id=document.id,
    output_path="facture_downloaded.pdf"
)

Module Auth - Gestion des Clients API

Le module Auth permet également de gérer les clients API (applications tierces).

1. Lister les clients

from sahges_sdk.auth import SahgesAuthClient

client = SahgesAuthClient(client_id="...", client_secret="...")

# Liste avec filtres
response = client.list_clients(payload={
    "page": 1,
    "per_page": 20,
    "is_active": True,
    "organization_id": "uuid-org"
})

# SahgesClientListResponse contient:
# - total: int
# - page: int
# - per_page: int
# - data: List[SahgesClient]

print(f"Total: {response.total} clients")
for api_client in response.data:
    print(f"- {api_client.name} ({api_client.client_id})")
    print(f"  Actif: {api_client.is_active}")
    print(f"  Redirect URI: {api_client.redirect_uri}")

2. Créer un client

# Créer un nouveau client API
new_client = client.create_client(payload={
    "name": "Mon Application Mobile",
    "redirect_uri": "https://mon-app.com/callback",
    "organization_id": "uuid-org"  # optionnel
})

# SahgesClient contient:
# - client_id: str (UUID)
# - secret: str (généré automatiquement)
# - name: str
# - redirect_uri: Optional[str]
# - is_active: bool
# - organization_id: Optional[UUID]
# - created_at: datetime

print(f"Client créé: {new_client.client_id}")
print(f"Secret: {new_client.secret}")  # À sauvegarder immédiatement !

3. Récupérer et mettre à jour

# Récupérer un client
api_client = client.find_client(client_id="uuid-here")

# Mettre à jour
updated = client.update_client(
    client_id=api_client.client_id,
    payload={
        "name": "Nouveau nom",
        "redirect_uri": "https://new-url.com/callback"
    }
)

# Activer/Désactiver
activated = client.activate_client(client_id=api_client.client_id)
deactivated = client.deactivate_client(client_id=api_client.client_id)

# Réinitialiser le secret
new_secret = client.reset_client_secret(client_id=api_client.client_id)
print(f"Nouveau secret: {new_secret.secret}")

# Supprimer
result = client.delete_client(client_id=api_client.client_id)
print(result)  # True

Module Auth - Gestion des Utilisateurs

Gestion complète des utilisateurs avec CRUD et permissions.

1. Lister les utilisateurs

# Liste avec filtres
response = client.list_users(payload={
    "page": 1,
    "per_page": 50,
    "is_active": True,
    "role": "USER",  # USER, ADMIN, SUPERADMIN
    "organization_id": "uuid-org"
})

# SahgesUserListResponse contient:
# - total: int
# - data: List[SahgesUser]

for user in response.data:
    print(f"{user.first_name} {user.last_name} ({user.email})")
    print(f"  Rôle: {user.role}, Organisation: {user.organization.name}")

2. Créer un utilisateur

# Créer un nouvel utilisateur
new_user = client.create_user(payload={
    "email": "john.doe@example.com",
    "first_name": "John",
    "last_name": "Doe",
    "phone": "+22912345678",
    "role": "USER",
    "organization_id": "uuid-org"
})

# SahgesUser contient tous les champs (id, email, first_name, etc.)
print(f"Utilisateur créé: {new_user.id}")
print(f"Mot de passe par défaut: utilisé={new_user.is_using_default_password}")

3. Gérer les utilisateurs

# Récupérer un utilisateur
user = client.find_user(user_id="uuid-here")

# Récupérer l'utilisateur actuellement connecté
current = client.get_current_user(access_token="token")

# Mettre à jour
updated = client.update_user(
    user_id=user.id,
    payload={
        "first_name": "Jane",
        "phone": "+22987654321"
    }
)

# Activer/Désactiver
activated = client.activate_user(user_id=user.id)
deactivated = client.deactivate_user(user_id=user.id)

# Réinitialiser le mot de passe (envoyer email avec nouveau mdp)
reset = client.reset_user(user_id=user.id)

# Supprimer
result = client.delete_user(user_id=user.id)

4. Routes client (accès restreint)

Les clients API peuvent gérer les utilisateurs selon leurs permissions :

# Liste pour le client (limité à son organisation)
users = client.client_list_users(payload={"is_active": True})

# Créer (permissions configurables)
new_user = client.client_create_user(payload={
    "email": "user@example.com",
    "first_name": "John",
    "last_name": "Doe"
})

# Récupérer et mettre à jour
user = client.client_find_user(user_id="uuid")
updated = client.client_update_user(
    user_id=user.id,
    payload={"first_name": "Jane"}
)

# Réinitialiser
reset = client.client_reset_user(user_id=user.id)

Module Auth - Gestion des Organisations

Gestion hiérarchique des organisations (INTERNAL, EXTERNAL, PARTNER).

1. Lister et créer

# Lister avec filtres
response = client.list_organizations(payload={
    "page": 1,
    "type": "PARTNER",  # INTERNAL, EXTERNAL, PARTNER
    "is_active": True
})

# SahgesOrganizationListResponse contient:
# - total: int
# - data: List[SahgesOrganization]

for org in response.data:
    print(f"{org.name} ({org.type})")
    if org.parent_organization_id:
        print(f"  Parent: {org.parent_organization_id}")

# Créer une organisation
new_org = client.create_organization(payload={
    "name": "ACME Corporation",
    "type": "PARTNER",
    "description": "Organisation partenaire",
    "parent_organization_id": "uuid-parent"  # optionnel
})

print(f"Organisation créée: {new_org.id}")

2. Gérer les organisations

# Récupérer
org = client.find_organization(organization_id="uuid-here")

# Mettre à jour
updated = client.update_organization(
    organization_id=org.id,
    payload={
        "name": "Nouveau nom",
        "description": "Nouvelle description"
    }
)

# Activer/Désactiver
activated = client.activate_organization(organization_id=org.id)
deactivated = client.deactivate_organization(organization_id=org.id)

# Supprimer
result = client.delete_organization(organization_id=org.id)

Module Auth - Gestion des Notifications

Gestion complète des notifications avec envoi email/SMS.

1. Envoyer une notification

# Envoyer une notification à un utilisateur
notification = client.send_notification(payload={
    "to_user_id": "uuid-user",
    "summary": "Vous avez un nouveau message",
    "category": "INFO",  # INFO, SUCCESS, WARNING, ERROR
    "send_email": True,
    "send_sms": False,
    "details": "Message détaillé optionnel"
})

# SahgesNotification contient:
# - id: UUID
# - to_user: SahgesNotificationUser
# - summary: str
# - category: str
# - is_read: bool
# - send_email: bool
# - created_at: datetime

print(f"Notification envoyée: {notification.id}")

2. Lister et consulter

# Lister les notifications (requiert access_token utilisateur)
response = client.list_notifications(
    access_token="user_token",
    payload={
        "page": 1,
        "is_read": False,  # Seulement les non lues
        "category": "INFO"
    }
)

# SahgesNotificationListResponse contient:
# - total: int
# - data: List[SahgesNotification]

print(f"{response.total} notification(s)")
for notif in response.data:
    print(f"- {notif.summary} ({notif.category})")
    print(f"  Lu: {notif.is_read}, Le: {notif.created_at}")

# Récupérer une notification
notification = client.find_notification(
    access_token="user_token",
    notification_id="uuid-here"
)

3. Marquer comme lu

# Marquer une notification comme lue
read_notif = client.read_notification(
    access_token="user_token",
    notification_id="uuid-here"
)

print(f"Lue: {read_notif.is_read}")  # True

# Marquer toutes comme lues
result = client.read_all_notifications(access_token="user_token")
print(result)  # True

# Compter les non lues
count = client.count_unread_notifications(access_token="user_token")
print(f"Non lues: {count.count}")

🏗️ Architecture du SDK

Structure des modules

src/sahges_sdk/
├── auth/              # Module d'authentification
│   ├── auth_client.py # Client avec 40+ méthodes
│   ├── types.py       # Types dataclass (SahgesLoginResponse, SahgesClient, etc.)
│   ├── routes.py      # Définition des routes API
│   ├── enums.py       # Énumérations (rôles, types org...)
│   └── [login|logout|clients|users|organizations|notifications]/
├── docs/              # Module de gestion documentaire
│   ├── docs_client.py # Client avec 10+ méthodes
│   ├── types.py       # Types dataclass (SahgesDocument, etc.)
│   ├── enums.py       # Énumérations (visibilité, statut...)
│   ├── routes.py      # Définition des routes API
│   └── [documents|shares|clients]/
├── base/              # Classes de base partagées
│   ├── client.py      # BaseSahgesApiClient (HTTP)
│   ├── endpoint.py    # Définition des endpoints
│   ├── decorators.py  # @sahges_endpoint pour validation
│   ├── enums.py       # SahgesAuthUserRoleEnum, etc.
│   ├── error.py       # Exceptions personnalisées
│   └── logger.py      # Configuration logging
└── config/            # Configuration centralisée

Types et dataclasses

Le SDK utilise marshmallow-dataclass pour générer automatiquement :

  • Des dataclasses Python avec types stricts
  • Des schémas Marshmallow pour validation
  • Autocomplétion IDE complète
  • Type checking statique (mypy)
# Exemple de type généré automatiquement
@dataclass
class SahgesLoginResponse:
    """Type pour la réponse de login"""
    access_token: str
    user: AuthUser
    refresh_token: Optional[str] = None
    
    class Meta:
        unknown = EXCLUDE  # Ignore les champs inconnus

# Utilisation
response = client.login(...)
print(response.access_token)  # ✅ Accès par attribut
print(response.user.email)    # ✅ Navigation type-safe

Énumérations

from sahges_sdk.base.enums import SahgesAuthUserRoleEnum, SahgesAuthOrganizationTypeEnum
from sahges_sdk.docs.enums import (
    SahgesDocumentVisibilityEnum,
    SahgesDocumentStatusEnum,
    SahgesDocumentSharePermissionEnum,
    SahgesDocumentFileFormatEnum
)

# Valeurs possibles
SahgesAuthUserRoleEnum          # USER, ADMIN, SUPERADMIN
SahgesAuthOrganizationTypeEnum  # INTERNAL, EXTERNAL, PARTNER

SahgesDocumentVisibilityEnum       # PRIVATE, ORGANIZATION, PUBLIC, SHARED
SahgesDocumentStatusEnum           # DRAFT, ACTIVE, ARCHIVED, DELETED
SahgesDocumentSharePermissionEnum  # VIEW, COMMENT, EDIT, MANAGE
SahgesDocumentFileFormatEnum       # PDF, DOCX, XLSX, PNG, JPG, MP4, etc.

Types disponibles

Tous les types suivants sont disponibles à l'import depuis sahges_sdk.auth et sahges_sdk.docs :

Types Auth:

from sahges_sdk.auth import (
    # Authentification
    SahgesLoginResponse,
    SahgesRefreshResponse,
    SahgesAuthUser,
    SahgesForgotPasswordResponse,
    SahgesResetPasswordResponse,
    SahgesResetPasswordChallengeResponse,
    
    # Clients API
    SahgesClient,
    SahgesClientListResponse,
    SahgesClientSecretResponse,
    
    # Utilisateurs
    SahgesUser,
    SahgesUserListResponse,
    
    # Organisations
    SahgesOrganization,
    SahgesOrganizationListResponse,
    
    # Notifications
    SahgesNotification,
    SahgesNotificationListResponse,
    SahgesNotificationCountResponse,
    SahgesNotificationUser,
    
    # Authentification Client
    SahgesClientAuthResponse,
    SahgesClientIntrospectResponse
)

Types Docs:

from sahges_sdk.docs import (
    SahgesDocument,
    SahgesDocumentListItem,
    SahgesDocumentListResponse,
    SahgesDocumentShare,
    SahgesDocumentShareCreateResponse
)

🔒 Gestion des erreurs

Le SDK fournit des exceptions spécifiques héritant de SahgesError :

from sahges_sdk.base.error import (
    SahgesError,                 # Erreur de base
    SahgesClientConfigError,     # Configuration invalide
    SahgesRequestError,          # Erreur HTTP générale
    SahgesAuthenticationError,   # Erreur d'authentification (401/403)
    SahgesValidationError        # Erreur de validation de données
)

# Gestion complète des erreurs
try:
    response = client.login(payload={
        "credential": "user@example.com",
        "password": "wrong_password"
    })
    print(f"Connecté: {response.user.email}")
    
except SahgesAuthenticationError as e:
    # Erreur 401/403
    print(f"Authentification échouée: {e}")
    print(f"Status: {e.status_code}")
    print(f"Réponse: {e.response_data}")
    
except SahgesValidationError as e:
    # Données invalides
    print(f"Erreur de validation: {e}")
    
except SahgesRequestError as e:
    # Autre erreur HTTP (400, 404, 500...)
    print(f"Erreur HTTP: {e}")
    print(f"Status: {e.status_code}")
    
except SahgesClientConfigError as e:
    # client_id ou client_secret manquant
    print(f"Configuration incorrecte: {e}")
    
except SahgesError as e:
    # Erreur SAHGES générique
    print(f"Erreur SAHGES: {e}")

Codes HTTP et exceptions

Code HTTP Exception Description
400 SahgesValidationError Données invalides
401 SahgesAuthenticationError Non authentifié
403 SahgesAuthenticationError Permissions insuffisantes
404 SahgesRequestError Ressource introuvable
429 SahgesRequestError Rate limit dépassé
500 SahgesRequestError Erreur serveur

🧪 Tests

Le projet comprend 33 tests unitaires + 16 tests manuels.

Lancer les tests

# Tous les tests
pytest tests/ -v

# Tests avec couverture
pytest --cov=src tests/
pytest --cov=src --cov-report=html tests/

# Tests spécifiques
pytest tests/test_auth_login.py -v
pytest tests/test_auth_complete.py -v
pytest tests/test_docs_complete.py -v

# Tests manuels (nécessitent vraie API)
python tests/test_manual_login.py
python tests/test_manual_docs.py

Utilisation du script manage.sh

# Lancer les tests
./manage.sh test

# Statistiques du projet
./manage.sh stats

# Publier sur PyPI
./manage.sh publish pypi

Configuration pour tests

Créez un fichier .env.test :

SAHGES_CLIENT_ID=test_client_id
SAHGES_CLIENT_SECRET=test_client_secret
SAHGES_TEST_EMAIL=test@example.com
SAHGES_TEST_PASSWORD=test_password

📝 Développement

Installation développeur

git clone https://gitlab.com/florianogomez/sahges-sdk.git
cd sahges-sdk
python -m venv venv
source venv/bin/activate  # ou venv\Scripts\activate sur Windows
pip install -e ".[dev]"

Dépendances

Production:

  • httpx>=0.27 - Client HTTP moderne
  • marshmallow>=3.20 - Validation et sérialisation
  • marshmallow-dataclass>=8.6 - Génération auto dataclass + schéma

Développement:

  • pytest - Framework de tests
  • pytest-cov - Couverture de code
  • python-dotenv - Variables d'environnement

Conventions de code

  • Python 3.10+ : Type hints modernes (str | None au lieu de Union[str, None])
  • Dataclasses : Pour tous les types de réponse
  • Marshmallow : Pour validation des schémas
  • Docstrings : Format Google pour toutes les fonctions publiques
  • Type hints : Obligatoires partout
  • Exceptions : Utiliser les exceptions spécifiques du SDK

Ajouter une nouvelle fonctionnalité

Exemple pour le module Auth :

# 1. Créer la fonction (src/sahges_sdk/auth/new_feature/feature.py)
from sahges_sdk.base.decorators import sahges_endpoint
from sahges_sdk.auth.routes import SahgesAuthenticationRoutes
from sahges_sdk.auth.types import NewFeatureResponse

@sahges_endpoint(
    request_schema=NewFeatureRequestSchema,
    response_schema=NewFeatureResponseSchema
)
def sahges_auth_new_feature(self, payload: dict) -> NewFeatureResponse:
    """Description de la fonctionnalité."""
    endpoint = SahgesAuthenticationRoutes.new_feature.value
    response = self.request(
        method=endpoint.method,
        path=endpoint.path,
        json=payload
    )
    return response

# 2. Définir le type de réponse (src/sahges_sdk/auth/types.py)
from marshmallow_dataclass import dataclass
from marshmallow import EXCLUDE

@dataclass
class NewFeatureResponse:
    """Type pour la réponse de new_feature"""
    result: str
    success: bool
    
    class Meta:
        unknown = EXCLUDE

# 3. Ajouter la route (src/sahges_sdk/auth/routes.py)
class SahgesAuthenticationRoutes(Enum):
    new_feature = Endpoint(
        path=f"/{BASE}/new-feature",
        method=HTTPMethod.POST
    )

# 4. Attacher au client (src/sahges_sdk/auth/auth_client.py)
from types import MethodType
from sahges_sdk.auth.types import NewFeatureResponse

class SahgesAuthClient(BaseSahgesApiClient):
    def __init__(self, client_id, client_secret):
        super().__init__(...)
        from sahges_sdk.auth.new_feature.feature import sahges_auth_new_feature
        self.new_feature = MethodType(sahges_auth_new_feature, self)
    
    def new_feature(self, payload: Dict[str, Any]) -> NewFeatureResponse:
        """
        Description publique de la fonctionnalité
        
        Args:
            payload: Paramètres de la requête
            
        Returns:
            NewFeatureResponse: Résultat de l'opération
        """
        # Implémentation liée dans __init__
        pass

# 5. Écrire les tests (tests/test_new_feature.py)
def test_new_feature_success():
    client = SahgesAuthClient("test_id", "test_secret")
    # Mock et assertions...

🤝 Contribution

Les contributions sont les bienvenues ! Processus :

  1. Fork le projet
  2. Créer une branche (git checkout -b feature/AmazingFeature)
  3. Committer les changements (git commit -m 'Add AmazingFeature')
  4. Pusher vers la branche (git push origin feature/AmazingFeature)
  5. Ouvrir une Pull Request

Checklist avant PR

  • Tests passent (./manage.sh test)
  • Type hints ajoutés partout
  • Docstrings présentes (format Google)
  • Types dataclass pour les réponses
  • Gestion d'erreurs appropriée
  • CHANGELOG.md mis à jour

📄 Licence

Ce projet est sous licence MIT. Voir LICENSE pour détails.

📧 Contact

SAHGES - floriano.gomez@bj.sanlamallianz.com

Repository : https://gitlab.com/florianogomez/sahges-sdk

🔗 Liens utiles

❓ FAQ

Comment obtenir des credentials API ?

Contactez floriano.gomez@bj.sanlamallianz.com pour obtenir client_id et client_secret.

Quelle est la durée de vie des tokens ?

  • Access token : 15 minutes
  • Refresh token : 7 jours

Pourquoi marshmallow-dataclass au lieu de TypedDict ?

  • Accès par attribut : response.access_token au lieu de response["access_token"]
  • Validation automatique : Les données invalides lèvent des erreurs
  • Autocomplétion IDE : L'IDE connaît tous les attributs
  • Type checking : mypy peut vérifier les types
  • Schémas réutilisables : Partage avec le backend SAHGES

Comment convertir un objet en dict ?

from marshmallow_dataclass import class_schema

# Obtenir le schéma
schema = class_schema(type(response))()

# Convertir en dict
response_dict = schema.dump(response)

Quels formats de fichiers sont supportés ?

Tous les formats. Traitement spécial pour :

  • PDF : OCR et extraction de texte
  • Images : OCR et analyse IA
  • Audio/Vidéo : Transcription automatique
  • Office : Conversion et prévisualisation

Comment gérer les fichiers volumineux ?

Le système gère automatiquement les uploads en streaming. Vérifiez les limites avec votre administrateur.

Puis-je utiliser le SDK en production ?

Oui, le SDK est conçu pour la production avec :

  • Gestion d'erreurs robuste
  • Logging approprié
  • Validation stricte des données
  • Tests complets (33 tests unitaires)
  • Support Python 3.10+

📚 Exemples complets

Workflow Auth + Docs combiné

from sahges_sdk.auth import SahgesAuthClient
from sahges_sdk.docs import SahgesDocsClient
from pathlib import Path

# 1. Authentification
auth = SahgesAuthClient(client_id="...", client_secret="...")

login = auth.login({
    "credential": "user@example.com",
    "password": "password"
})

print(f"Connecté: {login.user.first_name} {login.user.last_name}")
print(f"Organisation: {login.user.organization.name}")
print(f"Rôle: {login.user.role}")

# 2. Gestion documentaire
docs = SahgesDocsClient(client_id="...", client_secret="...")

# Créer un document
doc = docs.create(
    title="Rapport mensuel",
    file_path=Path("rapport.pdf"),
    visibility="ORGANIZATION",
    status="VALIDATED",
    category="reporting",
    tags=["mensuel", "2025", "janvier"]
)

print(f"SahgesDocument créé: {doc.id}")
print(f"Fichier: {doc.file_name} ({doc.file_size} bytes)")

# Lister mes documents
my_docs = docs.list({"owner_only": True})
print(f"Vous avez {len(my_docs)} document(s)")

# Partager avec un collègue
from datetime import datetime, timedelta

share = docs.share_create({
    "document_id": doc.id,
    "shared_with_auth_user_id": "colleague-uuid",
    "permission": "VIEW",
    "expires_at": datetime.now() + timedelta(days=7)
})

print(f"Partagé avec permission {share.permission}")

# 3. Déconnexion
auth.logout(access_token=login.access_token)
print("Session terminée")

Recherche et téléchargement

from sahges_sdk.docs import SahgesDocsClient

docs = SahgesDocsClient(client_id="...", client_secret="...")

# Recherche avancée
results = docs.list({
    "search": "contrat client",
    "category": "legal",
    "status": "VALIDATED",
    "visibility": "ORGANIZATION"
})

print(f"Trouvé {len(results)} document(s)")

# Télécharger le premier résultat
if results:
    doc = results[0]
    print(f"Téléchargement de: {doc.title}")
    
    docs.download(
        document_id=doc.id,
        output_path=f"/tmp/{doc.file_name}"
    )
    
    print(f"Téléchargé dans /tmp/{doc.file_name}")

Gestion des erreurs avancée

from sahges_sdk.auth import SahgesAuthClient
from sahges_sdk.base.error import (
    SahgesAuthenticationError,
    SahgesValidationError,
    SahgesRequestError
)

client = SahgesAuthClient(client_id="...", client_secret="...")

try:
    response = client.login({
        "credential": "user@example.com",
        "password": "wrong_password"
    })
    
except SahgesAuthenticationError as e:
    if e.status_code == 401:
        print("Identifiants incorrects")
    elif e.status_code == 403:
        print("Compte désactivé ou accès refusé")
    else:
        print(f"Erreur d'authentification: {e}")
        
except SahgesValidationError as e:
    print(f"Données invalides: {e}")
    # Exemple : email mal formaté, mot de passe trop court
    
except SahgesRequestError as e:
    if e.status_code == 429:
        print("Trop de tentatives, réessayez plus tard")
    elif e.status_code >= 500:
        print("Erreur serveur, réessayez plus tard")
    else:
        print(f"Erreur HTTP {e.status_code}: {e}")
        
except Exception as e:
    print(f"Erreur inattendue: {e}")

Version actuelle : 0.1.9

Dernière mise à jour : Janvier 2026

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sahges_sdk-0.3.1.tar.gz (92.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

sahges_sdk-0.3.1-py3-none-any.whl (93.6 kB view details)

Uploaded Python 3

File details

Details for the file sahges_sdk-0.3.1.tar.gz.

File metadata

  • Download URL: sahges_sdk-0.3.1.tar.gz
  • Upload date:
  • Size: 92.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.1

File hashes

Hashes for sahges_sdk-0.3.1.tar.gz
Algorithm Hash digest
SHA256 fdd5ecf139c43719b124a5ece011c6ca88eab329e174fc40ce85147b1b78a628
MD5 9ea37196716b6b88386952d76351fa44
BLAKE2b-256 0bb02516bfe80dc6e563595da876fb9fd5fe19934c7535545f617b575ec07f31

See more details on using hashes here.

File details

Details for the file sahges_sdk-0.3.1-py3-none-any.whl.

File metadata

  • Download URL: sahges_sdk-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 93.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.1

File hashes

Hashes for sahges_sdk-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 36355416e45ce3156e5fd0ae26ab6077a9626a1efa3774d4cd94d4b090be663d
MD5 31c2a24c47aa254073d2747caf5cc32e
BLAKE2b-256 079ec21a45ae01d0f76bbb1e8d7d62b83a9377bce58f82e51193f47661dd27a6

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page