diff --git a/lib/providers/collection_providers.dart b/lib/providers/collection_providers.dart index f2f0049c..afa5580b 100644 --- a/lib/providers/collection_providers.dart +++ b/lib/providers/collection_providers.dart @@ -329,6 +329,68 @@ class CollectionStateNotifier } } + Future handlePostResponseScript( + RequestModel requestModel, + EnvironmentModel? originalEnvironmentModel, + ) async { + final scriptResult = await executePostResponseScript( + currentRequestModel: requestModel, + activeEnvironment: originalEnvironmentModel?.toJson() ?? {}, + ); + + requestModel = + requestModel.copyWith(httpResponseModel: scriptResult.updatedResponse); + + if (originalEnvironmentModel != null) { + final updatedEnvironmentMap = scriptResult.updatedEnvironment; + + final List newValues = []; + final Map mutableUpdatedEnv = + Map.from(updatedEnvironmentMap); + + for (final originalVariable in originalEnvironmentModel.values) { + if (mutableUpdatedEnv.containsKey(originalVariable.key)) { + final dynamic newValue = mutableUpdatedEnv[originalVariable.key]; + newValues.add( + originalVariable.copyWith( + value: newValue == null ? '' : newValue.toString(), + enabled: true, + ), + ); + + mutableUpdatedEnv.remove(originalVariable.key); + } else { + // Variable was removed by the script (unset/clear), don't add it to newValues. + // Alternatively, you could keep it but set enabled = false: + // newValues.add(originalVariable.copyWith(enabled: false)); + } + } + + for (final entry in mutableUpdatedEnv.entries) { + final dynamic newValue = entry.value; + newValues.add( + EnvironmentVariableModel( + key: entry.key, + value: newValue == null ? '' : newValue.toString(), + enabled: true, + type: EnvironmentVariableType.variable, + ), + ); + } + ref.read(environmentsStateNotifierProvider.notifier).updateEnvironment( + originalEnvironmentModel.id, + name: originalEnvironmentModel.name, + values: newValues); + } else { + debugPrint( + "Skipped environment update as originalEnvironmentModel was null."); + if (scriptResult.updatedEnvironment.isNotEmpty) { + debugPrint( + "Warning: Pre-request script updated environment variables, but no active environment was selected to save them to."); + } + } + } + Future sendRequest() async { final requestId = ref.read(selectedIdStateProvider); ref.read(codePaneVisibleStateProvider.notifier).state = false; @@ -405,6 +467,7 @@ class CollectionStateNotifier httpRequestModel: substitutedHttpRequestModel, httpResponseModel: httpResponseModel, ); + handlePostResponseScript(newRequestModel, originalEnvironmentModel); ref.read(historyMetaStateNotifier.notifier).addHistoryRequest(model); } diff --git a/lib/services/flutter_js_service.dart b/lib/services/flutter_js_service.dart index c4e1c63f..dc8ff24e 100644 --- a/lib/services/flutter_js_service.dart +++ b/lib/services/flutter_js_service.dart @@ -6,6 +6,7 @@ import 'dart:developer'; import 'package:apidash/consts.dart'; import 'package:apidash/models/request_model.dart'; import 'package:apidash_core/models/http_request_model.dart'; +import 'package:apidash_core/models/http_response_model.dart'; import 'package:flutter/services.dart'; import 'package:flutter_js/flutter_js.dart'; @@ -133,7 +134,6 @@ Future< })(); // Immediately invoke the function """; - // TODO: Do something better to avoid null check here. HttpRequestModel resultingRequest = httpRequest!; Map resultingEnvironment = Map.from(activeEnvironment); @@ -177,6 +177,101 @@ Future< ); } +Future< + ({ + HttpResponseModel updatedResponse, + Map updatedEnvironment + })> executePostResponseScript({ + required RequestModel currentRequestModel, + required Map activeEnvironment, +}) async { + if (currentRequestModel.postRequestScript.trim().isEmpty) { + // No script, return original data + // return ( + // updatedRequest: currentRequestModel.httpRequestModel, + // updatedEnvironment: activeEnvironment + // ); + } + + final httpRequest = currentRequestModel.httpRequestModel; + final httpResponse = currentRequestModel.httpResponseModel; + final userScript = currentRequestModel.postRequestScript; + + // Prepare Data + final requestJson = jsonEncode(httpRequest?.toJson()); + final responseJson = jsonEncode(httpResponse?.toJson()); + final environmentJson = jsonEncode(activeEnvironment); + + // Inject data as JS variables + // Escape strings properly if embedding directly + final dataInjection = """ + var injectedRequestJson = ${jsEscapeString(requestJson)}; + var injectedEnvironmentJson = ${jsEscapeString(environmentJson)}; + var injectedResponseJson = ${jsEscapeString(responseJson)}; + """; + + // Concatenate & Add Return + final fullScript = """ + (function() { + // --- Data Injection (now constants within the IIFE scope) --- + $dataInjection + + // --- Setup Script (will declare variables within the IIFE scope) --- + $setupScript + + // --- User Script (will execute within the IIFE scope)--- + $userScript + + // --- Return Result (accesses variables from the IIFE scope) --- + return JSON.stringify({ response: response, environment: environment }); + })(); // Immediately invoke the function + """; + + // TODO: Do something better to avoid null check here. + // HttpRequestModel resultingRequest = httpRequest!; + HttpResponseModel resultingResponse = httpResponse!; + Map resultingEnvironment = Map.from(activeEnvironment); + + try { + // Execute + final JsEvalResult result = jsRuntime.evaluate(fullScript); + + // Process Results + if (result.isError) { + print("Post-Response script execution error: ${result.stringResult}"); + // TODO: Handle error - maybe show in UI, keep original request/env + } else if (result.stringResult.isNotEmpty) { + final resultMap = jsonDecode(result.stringResult); + log(resultMap['response'].toString()); + if (resultMap is Map) { + // Deserialize Request + if (resultMap.containsKey('response') && resultMap['response'] is Map) { + try { + resultingResponse = HttpResponseModel.fromJson( + Map.from(resultMap['response'])); + } catch (e) { + print("Error deserializing modified response from script: $e"); + //TODO: Handle error - maybe keep original response? + } + } + // Get Environment Modifications + if (resultMap.containsKey('environment') && + resultMap['environment'] is Map) { + resultingEnvironment = + Map.from(resultMap['environment']); + } + } + } + } catch (e) { + print("Dart-level error during post-response script execution: $e"); + } + + return ( + updatedResponse: resultingResponse, + updatedEnvironment: resultingEnvironment + ); +} + // Helper to properly escape strings for JS embedding String jsEscapeString(String s) { return jsonEncode( diff --git a/packages/apidash_core/lib/models/http_response_model.dart b/packages/apidash_core/lib/models/http_response_model.dart index 914aaa57..b09e711b 100644 --- a/packages/apidash_core/lib/models/http_response_model.dart +++ b/packages/apidash_core/lib/models/http_response_model.dart @@ -12,12 +12,13 @@ import '../consts.dart'; part 'http_response_model.freezed.dart'; part 'http_response_model.g.dart'; -class Uint8ListConverter implements JsonConverter?> { +class Uint8ListConverter implements JsonConverter?> { const Uint8ListConverter(); @override - Uint8List? fromJson(List? json) { - return json == null ? null : Uint8List.fromList(json); + Uint8List? fromJson(List? json) { + if (json == null) return null; + return Uint8List.fromList(json.cast()); } @override