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.
📋 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 modernemarshmallow>=3.20- Validation et sérialisationmarshmallow-dataclass>=8.6- Génération auto dataclass + schéma
Développement:
pytest- Framework de testspytest-cov- Couverture de codepython-dotenv- Variables d'environnement
Conventions de code
- Python 3.10+ : Type hints modernes (
str | Noneau lieu deUnion[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 :
- Fork le projet
- Créer une branche (
git checkout -b feature/AmazingFeature) - Committer les changements (
git commit -m 'Add AmazingFeature') - Pusher vers la branche (
git push origin feature/AmazingFeature) - 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
- SahgesDocumentation Auth complète
- SahgesDocumentation Docs complète
- Exemple d'utilisation
- Guide de migration
- CHANGELOG
❓ 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_tokenau lieu deresponse["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}")
⚡ Clients Asynchrones (Async/Await)
Le SDK fournit des clients asynchrones pour les applications FastAPI, Starlette, et autres frameworks async :
import asyncio
from sahges_sdk import SahgesAsyncAuthClient
async def main():
async with SahgesAsyncAuthClient(client_id, client_secret) as client:
response = await client.login({
"credential": "user@example.com",
"password": "password"
})
print(f"Connecté: {response.user.email}")
asyncio.run(main())
Clients asynchrones disponibles :
SahgesAsyncAuthClient- Authentification asyncSahgesAsyncDocsClient- Gestion documents asyncSahgesAsyncNotificationClient- Notifications async
Pour un guide complet des clients async, patterns FastAPI, concurrence, et performance: voir ASYNC_GUIDE.md
📊 Gestion des Erreurs
Le SDK fourni une hiérarchie d'exceptions pour faciliter le handling d'erreurs :
from sahges_sdk.base.error import (
SahgesError, # Exception de base
SahgesAuthenticationError, # 401
SahgesForbiddenError, # 403
SahgesNotFoundError, # 404
SahgesRateLimitError, # 429
SahgesServerError, # 5xx
)
Pour un guide détaillé des exceptions et stratégies de recovery: voir ERROR_HANDLING.md
Version actuelle : 0.4.0
Dernière mise à jour : Juin 2026
Metadata
Release files for sahges-sdk 0.6.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| sahges_sdk-0.6.0.tar.gz | 122.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| sahges_sdk-0.6.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 267.3 kB
Release files / sahges_sdk-0.6.0.tar.gz
| Download URL | sahges_sdk-0.6.0.tar.gz |
|---|---|
| Size | 122.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
641f7aff9d4a6ed43448f03dceff93a778214d4cc81623175a5e96dadd59b94c
|
|
BLAKE2b-256 checksum How to use checksums |
36923dc88a361ac940fbacb824eefec7d14289535527c5f9c5b18cc14a84ecda
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.1
|
Release files / sahges_sdk-0.6.0-py3-none-any.whl
| Download URL | sahges_sdk-0.6.0-py3-none-any.whl |
|---|---|
| Size | 144.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8b7b8415515808259c1f0b5207e861b12af2e932710ab134433ca6fec1aa243a
|
|
BLAKE2b-256 checksum How to use checksums |
0157856059d43ca12fb137b5b7857ab334210aa299e4a5749fed8bba61fc96f9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.1
|