httpRequest function

Future httpRequest(
  1. String path, {
  2. dynamic data,
  3. String method = 'POST',
})

Envía una petición HTTP a un endpoint de la API.

  • path: ruta relativa (ej: '/services/api/...')
  • data: body; puede ser Map<String,dynamic>, FormData, MultipartFile, List, etc.
  • method: 'GET'|'POST'|'PUT'|'DELETE' (default POST)

Retorna response.data del backend o lanza ApacuanaAPIError.

Implementation

Future<dynamic> httpRequest(
  String path, {
  dynamic data,
  String method = 'POST',
}) async {
  final dio = _dio;
  if (dio == null) {
    throw StateError(
      'Apacuana SDK: Cliente HTTP no inicializado. Llama a apacuana.init() primero.',
    );
  }

  // Si el caller ya pasó un FormData, lo clonamos para evitar reutilizar streams finalizados
  dynamic bodyToSend = data is FormData ? _cloneFormData(data) : data;

  if (data is! FormData) {
    // Si es Map y contiene MultipartFile u otras estructuras, convertimos a FormData
    if (data is Map<String, dynamic>) {
      final containsMultipart = data.values.any((v) =>
          v is MultipartFile ||
          (v is List && v.any((e) => e is MultipartFile)) ||
          (v is Map && v.values.any((e) => e is MultipartFile)));
      if (containsMultipart) {
        final formData = FormData();
        data.forEach((key, value) {
          if (value is MultipartFile) {
            // Clonar para evitar usar el mismo stream en múltiples requests
            formData.files.add(MapEntry(key, value.clone()));
          } else if (value is List) {
            for (final item in value) {
              if (item is MultipartFile) {
                formData.files.add(MapEntry(key, item.clone()));
              } else {
                formData.fields.add(MapEntry(key, item?.toString() ?? ''));
              }
            }
          } else if (value is Map) {
            // Serializamos mapas anidados simples a JSON string
            formData.fields.add(MapEntry(key, value.toString()));
          } else {
            formData.fields.add(MapEntry(key, value?.toString() ?? ''));
          }
        });
        bodyToSend = formData;
      } else {
        // keep as Map
        bodyToSend = data;
      }
    } else {
      // data es otro tipo (String, List, etc) -> se pasa tal cual
      bodyToSend = data;
    }
  } // else: data is FormData -> bodyToSend already cloned arriba

  try {
    Response res;
    switch (method.toUpperCase()) {
      case 'GET':
        // Para GET, si bodyToSend es Map lo pasamos como queryParameters.
        if (bodyToSend is Map<String, dynamic>) {
          res = await dio.get(path, queryParameters: bodyToSend);
        } else {
          res = await dio.get(path);
        }
        break;
      case 'POST':
        res = await dio.post(path, data: bodyToSend);
        break;
      case 'PUT':
        res = await dio.put(path, data: bodyToSend);
        break;
      case 'DELETE':
        res = await dio.delete(path, data: bodyToSend);
        break;
      default:
        throw ApacuanaAPIError(
          'Método HTTP no soportado: $method',
          statusCode: 405,
          errorCode: 'UNSUPPORTED_HTTP_METHOD',
        );
    }
    return res.data;
  } on DioException catch (e) {
    // Ya mapeamos en interceptores a ApacuanaAPIError cuando es posible.
    final underlying = e.error;
    if (underlying is ApacuanaAPIError) {
      // ignore: avoid_print
      print('[HTTP Client] Fallo en la petición a $path: $underlying');
      throw underlying;
    }

    // Fallback si por alguna razón no pasó por el interceptor.
    if (e.response != null) {
      final status = e.response!.statusCode ?? 0;
      final data = e.response!.data;
      String message = 'Error desconocido desde la API.';
      String code = 'API_ERROR';
      if (data is Map) {
        message = (data['msg'] ?? data['message'] ?? message).toString();
        code = (data['code'] ?? code).toString();
      }
      final apiErr =
          ApacuanaAPIError(message, statusCode: status, errorCode: code);
      // ignore: avoid_print
      print('[HTTP Client] Fallo en la petición a $path: $apiErr');
      throw apiErr;
    }

    if (e.type == DioExceptionType.connectionTimeout ||
        e.type == DioExceptionType.sendTimeout ||
        e.type == DioExceptionType.receiveTimeout ||
        e.type == DioExceptionType.connectionError) {
      final apiErr = ApacuanaAPIError(
        'Error de conexión a la red, timeout o CORS.',
        statusCode: 0,
        errorCode: 'NETWORK_ERROR',
      );
      // ignore: avoid_print
      print('[HTTP Client] Fallo en la petición a $path: $apiErr');
      throw apiErr;
    }

    final apiErr = ApacuanaAPIError(
      'Error desconocido en la petición: ${e.message}',
      statusCode: 0,
      errorCode: 'UNKNOWN_REQUEST_ERROR',
    );
    // ignore: avoid_print
    print('[HTTP Client] Fallo en la petición a $path: $apiErr');
    throw apiErr;
  }
}