Formats de Réponse
Comprendre les structures des réponses API
Découvrez les formats de réponse mindzieAPI, les codes d'état, les modèles de gestion des erreurs et les structures de données pour créer des intégrations robustes.
Format de Réponse Standard
Toutes les réponses mindzieAPI suivent un format JSON cohérent avec des structures prévisibles :
Réponse réussie
{
"data": {
// Données principales de la réponse
},
"metadata": {
"timestamp": "2024-01-15T10:30:00Z",
"requestId": "req_12345",
"version": "1.0.0"
},
"pagination": {
// Présent pour les réponses paginées
"currentPage": 1,
"totalPages": 5,
"totalItems": 100,
"itemsPerPage": 20,
"hasNext": true,
"hasPrevious": false
}
}
Réponse d'erreur
{
"error": {
"code": "validation_failed",
"message": "Échec de la validation de la requête",
"details": {
"field": "datasetId",
"reason": "Format GUID invalide"
},
"timestamp": "2024-01-15T10:30:00Z",
"requestId": "req_12345"
}
}
Types de Réponses
Réponses de succès
Codes d'état HTTP 2xx avec des données JSON structurées et des métadonnées.
Réponses d'erreur
Codes d'état HTTP 4xx/5xx avec des informations détaillées sur l'erreur.
Pagination
Format de pagination cohérent pour les réponses à grands ensembles de données.
Codes d'État HTTP
Codes de succès (2xx)
| Code | Statut | Description | Usage |
|---|---|---|---|
200 |
OK | Requête réussie, données retournées | Requêtes GET, opérations réussies |
201 |
Created | Ressource créée avec succès | Requêtes POST créant de nouvelles ressources |
202 |
Accepted | Requête acceptée pour traitement asynchrone | Opérations longues, tâches en file d'attente |
204 |
No Content | Requête réussie, aucune donnée retournée | Requêtes DELETE, mises à jour sans données retournées |
Codes d'erreur client (4xx)
| Code | Statut | Description | Causes courantes |
|---|---|---|---|
400 |
Bad Request | Format de requête ou paramètres invalides | En-têtes manquants, JSON invalide, données malformées |
401 |
Unauthorized | Authentification requise ou échouée | Jeton manquant/invalide, identifiants expirés |
403 |
Forbidden | Authentification valide mais permissions insuffisantes | Accès utilisateur limité, mauvais locataire/projet |
404 |
Not Found | Ressource demandée inexistante | Point d'accès invalide, ID de ressource inexistant |
422 |
Unprocessable Entity | Format valide mais validation logique métier échouée | Règles métier invalides, violations de contraintes |
429 |
Too Many Requests | Limite de débit dépassée | Trop d'appels API dans la fenêtre temporelle |
Codes d'erreur serveur (5xx)
| Code | Statut | Description | Action |
|---|---|---|---|
500 |
Internal Server Error | Erreur serveur inattendue | Réessayer avec attente exponentielle |
502 |
Bad Gateway | Erreur du service en amont | Vérifier le statut du service, réessayer plus tard |
503 |
Service Unavailable | Service temporairement indisponible | Réessayer après délai, vérifier la maintenance |
504 |
Gateway Timeout | Délai d'attente dépassé | Augmenter le timeout, optimiser la requête |
Modèles de Réponse Courants
Réponse d'une ressource unique
{
"actionId": "87654321-4321-4321-4321-210987654321",
"actionType": "analyze",
"status": "completed",
"startTime": "2024-01-15T10:30:00Z",
"endTime": "2024-01-15T10:32:15Z",
"duration": 135,
"result": {
"outputId": "98765432-8765-4321-4321-987654321098",
"recordsProcessed": 10000
}
}
Réponse de collection avec pagination
{
"actions": [
{
"actionId": "87654321-4321-4321-4321-210987654321",
"actionType": "analyze",
"status": "completed"
},
{
"actionId": "11111111-2222-3333-4444-555555555555",
"actionType": "export",
"status": "processing"
}
],
"pagination": {
"currentPage": 1,
"totalPages": 5,
"totalItems": 100,
"itemsPerPage": 20,
"hasNext": true,
"hasPrevious": false,
"links": {
"first": "/api/Action/history?page=1&limit=20",
"next": "/api/Action/history?page=2&limit=20",
"last": "/api/Action/history?page=5&limit=20"
}
}
}
Réponse d'opération asynchrone
{
"operationId": "op_12345678-1234-1234-1234-123456789012",
"status": "processing",
"progress": {
"percentage": 45,
"currentStep": "data_analysis",
"totalSteps": 5,
"estimatedCompletion": "2024-01-15T10:35:00Z"
},
"trackingUrl": "/api/Execution/status/op_12345678-1234-1234-1234-123456789012",
"message": "Traitement de l'analyse du jeu de données en cours..."
}
Détails de la réponse d'erreur
Erreur de validation
{
"error": {
"code": "validation_failed",
"message": "Échec de la validation de la requête",
"details": {
"errors": [
{
"field": "datasetId",
"code": "invalid_format",
"message": "Doit être un GUID valide"
},
{
"field": "parameters.timeout",
"code": "out_of_range",
"message": "Doit être compris entre 1 et 3600 secondes"
}
]
},
"timestamp": "2024-01-15T10:30:00Z",
"requestId": "req_12345"
}
}
Erreur d'authentification
{
"error": {
"code": "invalid_token",
"message": "Le jeton d'accès fourni est invalide ou expiré",
"details": {
"tokenType": "bearer",
"expiresAt": "2024-01-15T09:00:00Z",
"suggestion": "Veuillez rafraîchir votre jeton d'accès"
},
"timestamp": "2024-01-15T10:30:00Z",
"requestId": "req_12345"
}
}
Erreur de limitation de débit
{
"error": {
"code": "rate_limit_exceeded",
"message": "Limite de débit dépassée pour ce point d'accès",
"details": {
"limit": 100,
"remaining": 0,
"resetTime": "2024-01-15T11:00:00Z",
"retryAfter": 1800
},
"timestamp": "2024-01-15T10:30:00Z",
"requestId": "req_12345"
}
}
En-têtes de Réponse
En-têtes standards
| En-tête | Description | Exemple |
|---|---|---|
Content-Type |
Format de la réponse | application/json; charset=utf-8 |
X-Request-Id |
Identifiant unique de la requête | req_12345678 |
X-Response-Time |
Temps de traitement serveur | 145ms |
X-API-Version |
Version de l'API utilisée | 1.0.0 |
En-têtes de limitation de débit
| En-tête | Description | Exemple |
|---|---|---|
X-RateLimit-Limit |
Nombre maximum de requêtes par fenêtre | 100 |
X-RateLimit-Remaining |
Requêtes restantes dans la fenêtre | 95 |
X-RateLimit-Reset |
Timestamp de réinitialisation de la fenêtre | 1642251600 |
Retry-After |
Secondes à attendre avant réessai | 3600 |
Bonnes pratiques pour la gestion des erreurs
Exemple JavaScript
async function handleAPIResponse(response) {
// Vérifier si la réponse est correcte
if (!response.ok) {
const errorData = await response.json();
switch (response.status) {
case 400:
throw new ValidationError(errorData.error.message, errorData.error.details);
case 401:
throw new AuthenticationError('Échec de l’authentification');
case 403:
throw new AuthorizationError('Permissions insuffisantes');
case 404:
throw new NotFoundError('Ressource non trouvée');
case 429:
const retryAfter = response.headers.get('Retry-After');
throw new RateLimitError(`Limite de débit atteinte. Réessayer après ${retryAfter} secondes`);
case 500:
case 502:
case 503:
case 504:
throw new ServerError('Erreur serveur survenue. Veuillez réessayer.');
default:
throw new APIError(`Erreur inattendue : ${response.status}`);
}
}
return await response.json();
}
// Utilisation avec logique de réessai
async function apiCallWithRetry(url, options, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await fetch(url, options);
return await handleAPIResponse(response);
} catch (error) {
if (error instanceof RateLimitError) {
const retryAfter = parseInt(error.retryAfter) || Math.pow(2, attempt);
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
continue;
}
if (error instanceof ServerError && attempt < maxRetries) {
const delay = Math.pow(2, attempt) * 1000; // Backoff exponentiel
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
throw error;
}
}
}
Exemple Python
import requests
import time
from typing import Dict, Any
class APIError(Exception):
def __init__(self, message: str, status_code: int = None, details: Dict = None):
super().__init__(message)
self.status_code = status_code
self.details = details
def handle_api_response(response: requests.Response) -> Dict[str, Any]:
"""Gérer la réponse API avec gestion appropriée des erreurs"""
if response.ok:
return response.json()
try:
error_data = response.json()
except ValueError:
error_data = {"error": {"message": response.text}}
error_info = error_data.get("error", {})
message = error_info.get("message", f"HTTP {response.status_code}")
details = error_info.get("details", {})
if response.status_code == 429:
retry_after = response.headers.get('Retry-After', '60')
raise APIError(f"Limite de débit atteinte. Réessayer après {retry_after} secondes",
response.status_code, details)
elif response.status_code >= 500:
raise APIError(f"Erreur serveur : {message}", response.status_code, details)
elif response.status_code >= 400:
raise APIError(f"Erreur client : {message}", response.status_code, details)
raise APIError(f"Erreur inattendue : {message}", response.status_code, details)
def api_call_with_retry(url: str, method: str = 'GET', max_retries: int = 3, **kwargs) -> Dict[str, Any]:
"""Effectuer un appel API avec logique de réessai automatique"""
for attempt in range(1, max_retries + 1):
try:
response = requests.request(method, url, **kwargs)
return handle_api_response(response)
except APIError as e:
if e.status_code == 429:
retry_after = int(e.details.get('retryAfter', 60))
time.sleep(retry_after)
continue
elif e.status_code >= 500 and attempt < max_retries:
delay = 2 ** attempt # Backoff exponentiel
time.sleep(delay)
continue
raise
raise APIError(f"Nombre maximal de réessais ({max_retries}) dépassé")
Objets de transfert de données (DTO) API
Voici les principaux DTO utilisés dans les points d'accès mindzieAPI.
DTO Tenant
TenantListItemDto
{
"tenantId": "12345678-1234-1234-1234-123456789012",
"name": "acme-corp",
"displayName": "Acme Corporation",
"description": "Locataire principal",
"caseCount": 50000,
"maxUserCount": 100,
"maxAnalystCount": 20,
"analystCount": 12,
"userCount": 45,
"preRelease": false,
"isAcademic": false,
"autoload": true,
"dateCreated": "2024-01-15T10:30:00Z",
"isDisabled": false
}
TenantDetailDto
{
"tenantId": "12345678-1234-1234-1234-123456789012",
"name": "acme-corp",
"displayName": "Acme Corporation",
"description": "Locataire principal",
"isAcademic": false,
"preRelease": false,
"maxUserCount": 100,
"maxAnalystCount": 20,
"maxCases": 100000,
"dateCreated": "2024-01-15T10:30:00Z",
"isDisabled": false
}
TenantUpdatedDto
{
"tenantId": "12345678-1234-1234-1234-123456789012",
"name": "acme-corp",
"displayName": "Acme Corporation Mise à jour",
"message": "Locataire 'acme-corp' mis à jour avec succès",
"isDisabled": false
}
DTO Utilisateur
UserListItemDto
{
"userId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"email": "john.smith@example.com",
"displayName": "John Smith",
"firstName": "John",
"lastName": "Smith",
"roleName": "Analyste",
"disabled": false,
"isServiceAccount": false,
"homeTenantId": null,
"homeTenantName": null,
"lastLogin": "2024-01-15T10:30:00Z",
"tenantCount": 2,
"tenantNames": "acme-corp, globex-inc",
"dateCreated": "2024-01-01T00:00:00Z"
}
UserCreatedDto
{
"userId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"email": "john.smith@example.com",
"displayName": "John Smith",
"message": "Utilisateur créé avec succès"
}
DTO Projet
ProjectReturn
{
"projectId": "87654321-4321-4321-4321-210987654321",
"tenantId": "12345678-1234-1234-1234-123456789012",
"projectName": "Analyse des bons de commande",
"projectDescription": "Analyse de process mining du workflow P2P",
"dateCreated": "2024-01-15T10:30:00Z",
"dateModified": "2024-01-20T14:45:00Z",
"createdBy": "user-guid",
"modifiedBy": "user-guid",
"isActive": true,
"datasetCount": 3,
"investigationCount": 5,
"dashboardCount": 2,
"userCount": 8
}
ProjectSummaryReturn
{
"projectId": "87654321-4321-4321-4321-210987654321",
"projectName": "Analyse des bons de commande",
"projectDescription": "Analyse de process mining",
"dateCreated": "2024-01-15T10:30:00Z",
"dateModified": "2024-01-20T14:45:00Z",
"statistics": {
"totalDatasets": 3,
"totalInvestigations": 5,
"totalDashboards": 2,
"totalNotebooks": 12,
"totalUsers": 8
}
}
ProjectUserReturn
{
"permissionId": "11111111-1111-1111-1111-111111111111",
"userId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"email": "john.smith@example.com",
"displayName": "John Smith",
"isOwner": true,
"dateAssigned": "2024-01-15T10:30:00Z"
}
ProjectThumbnailReturn
{
"projectId": "87654321-4321-4321-4321-210987654321",
"hasThumbnail": true,
"base64Image": "data:image/jpeg;base64,/9j/4AAQ..."
}
ProjectImportReturn
{
"success": true,
"projectId": "99999999-9999-9999-9999-999999999999",
"projectName": "Projet importé",
"datasetsImported": 2,
"investigationsImported": 3,
"dashboardsImported": 1,
"errorMessage": null,
"message": "Projet importé avec succès"
}
DTO Investigation
InvestigationReturn
{
"investigationId": "11111111-2222-3333-4444-555555555555",
"projectId": "87654321-4321-4321-4321-210987654321",
"investigationName": "Analyse de commande",
"investigationDescription": "Analyse de process mining du workflow de commande",
"datasetId": "12345678-1234-1234-1234-123456789012",
"dateCreated": "2024-01-15T10:30:00Z",
"dateModified": "2024-01-20T14:45:00Z",
"createdBy": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"modifiedBy": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"investigationOrder": 1.0,
"isUsedForOperationCenter": false,
"investigationFolderId": null,
"notebookCount": 3
}
InvestigationListReturn
{
"investigations": [
{
"investigationId": "11111111-2222-3333-4444-555555555555",
"projectId": "87654321-4321-4321-4321-210987654321",
"investigationName": "Analyse de commande",
"datasetId": "12345678-1234-1234-1234-123456789012",
"notebookCount": 3
}
],
"totalCount": 5,
"page": 1,
"pageSize": 50
}
CreateInvestigationRequest
{
"investigationName": "Analyse de commande",
"investigationDescription": "Analyse de process mining",
"datasetId": "12345678-1234-1234-1234-123456789012",
"isUsedForOperationCenter": false
}
UpdateInvestigationRequest
{
"investigationName": "Nom d'analyse mis à jour",
"investigationDescription": "Description mise à jour",
"isUsedForOperationCenter": true
}
DTO Notebook
NotebookReturn
{
"notebookId": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
"investigationId": "11111111-2222-3333-4444-555555555555",
"name": "Principal",
"description": "Bloc-notes d'analyse principal",
"dateCreated": "2024-01-15T10:30:00Z",
"dateModified": "2024-01-20T14:45:00Z",
"createdBy": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"modifiedBy": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"notebookType": 0,
"notebookOrder": 1.0,
"lastExecutionDuration": 2.5,
"blockCount": 12
}
NotebookListReturn
{
"notebooks": [
{
"notebookId": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
"investigationId": "11111111-2222-3333-4444-555555555555",
"name": "Principal",
"notebookOrder": 1.0,
"blockCount": 12
}
],
"totalCount": 3
}
Étapes Suivantes
Maintenant que vous comprenez les formats de réponse, explorez des sections spécifiques de l'API comme Actions, Blocs, ou Jeux de données pour voir ces modèles en action.