makeApiCallWithConfig method

Future<ApiCallResponse> makeApiCallWithConfig({
  1. required String apiUrl,
  2. required ApiCallType callType,
  3. BuildContext? context,
  4. Map<String, dynamic> headers = const {},
  5. Map<String, dynamic> params = const {},
  6. String? body,
  7. BodyType? bodyType = BodyType.JSON,
  8. bool returnBody = true,
  9. bool encodeBodyUtf8 = false,
  10. bool decodeUtf8 = false,
  11. bool needAuth = false,
  12. bool? needTenant,
  13. bool showSuccessMessage = false,
  14. bool showErrorMessage = true,
  15. String? successMessage,
  16. bool replaceApiUrl = false,
  17. String? completeApiUrl,
})

Context-optional variant of makeApiCall.

Identical surface and semantics to makeApiCall but context is now optional. When omitted, auth & tenant headers are resolved through AuthSingleton (which must be bound at bootstrap) and 401/403 redirect handling is skipped — alerts and the actual HTTP call still run.

Picks defaults from the bound ApiConfig when available:

  • needTenant falls back to config.needTenantByDefault
  • any header in config.defaultHeaders is merged unless already set

Existing call sites of makeApiCall remain untouched.

Implementation

Future<ApiCallResponse> makeApiCallWithConfig({
  required String apiUrl,
  required ApiCallType callType,
  BuildContext? context,
  Map<String, dynamic> headers = const {},
  Map<String, dynamic> params = const {},
  String? body,
  BodyType? bodyType = BodyType.JSON,
  bool returnBody = true,
  bool encodeBodyUtf8 = false,
  bool decodeUtf8 = false,
  bool needAuth = false,
  bool? needTenant,
  bool showSuccessMessage = false,
  bool showErrorMessage = true,
  String? successMessage,
  bool replaceApiUrl = false,
  String? completeApiUrl,
}) async {
  final cfg = _config;
  final effectiveNeedTenant = needTenant ?? cfg?.needTenantByDefault ?? false;

  // Merge default headers from config, but never overwrite caller-provided keys.
  Map<String, dynamic> mergedHeaders = {...headers};
  if (cfg != null) {
    cfg.defaultHeaders.forEach((k, v) {
      mergedHeaders.putIfAbsent(k, () => v);
    });
  }

  mergedHeaders = context != null
      ? await initHeader(mergedHeaders, needAuth, effectiveNeedTenant, context)
      : await _initHeaderFromSingleton(mergedHeaders, needAuth, effectiveNeedTenant);

  if (replaceApiUrl && completeApiUrl != null && completeApiUrl.isNotEmpty) {
    apiUrl = completeApiUrl;
  } else {
    apiUrl = _baseUrl + _apiVersion + apiUrl;
    if (!apiUrl.startsWith('http')) {
      apiUrl = 'https://$apiUrl';
    }
  }

  ApiCallResponse result;
  switch (callType) {
    case ApiCallType.GET:
    case ApiCallType.DELETE:
      result = await urlRequest(callType, apiUrl, mergedHeaders, params, returnBody, decodeUtf8);
      break;
    case ApiCallType.POST:
    case ApiCallType.PUT:
    case ApiCallType.PATCH:
      result = await requestWithBody(callType, apiUrl, mergedHeaders, params, body, bodyType, returnBody, encodeBodyUtf8, decodeUtf8);
      break;
  }

  if (result.succeeded) {
    if (showSuccessMessage) {
      AlertManager.showSuccess(
        "Successo",
        successMessage ?? "Operazione completata con successo",
        alertPosition: AlertPosition.bottom,
      );
    }
  } else {
    if (context != null) _handleResponse(result, context);
    if (result.statusCode != 401 && result.statusCode != 403 && showErrorMessage) {
      AlertManager.showDanger(
        result.error?.error?.toString() ?? "Errore ${result.statusCode}",
        result.error?.message ?? "Si è verificato un errore durante l'operazione",
        alertPosition: AlertPosition.bottom,
      );
    }
  }
  return result;
}