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.