From 36061f660a75641616e0328ba17179cf74c257a1 Mon Sep 17 00:00:00 2001 From: Udhay-Adithya Date: Thu, 1 May 2025 16:48:34 +0530 Subject: [PATCH] feat add pre-request script execution Initializes JavaScript runtime during application startup. Executes user-defined pre-request scripts before sending API requests, allowing modification of request details like URL, headers, and body. Updates script execution environment for better scoping and uses synchronous evaluation. --- lib/consts.dart | 5 ++-- lib/main.dart | 2 ++ lib/providers/collection_providers.dart | 15 +++++++++++ lib/services/flutter_js_service.dart | 34 ++++++++++++++++--------- 4 files changed, 41 insertions(+), 15 deletions(-) diff --git a/lib/consts.dart b/lib/consts.dart index 598cc577..8cc7ad02 100644 --- a/lib/consts.dart +++ b/lib/consts.dart @@ -487,7 +487,6 @@ const kMsgClearHistorySuccess = 'History cleared successfully'; const kMsgClearHistoryError = 'Error clearing history'; const kMsgShareError = "Unable to share"; - const String setupScript = r""" // === APIDash Setup Script === @@ -547,7 +546,7 @@ try { // This object provides functions to interact with the request, response, // environment, and the Dart host application. -const ad = { +var ad = { /** * Functions to modify the request object *before* it is sent. * Only available in pre-request scripts. @@ -992,4 +991,4 @@ const ad = { // User's script will be appended below this line by Dart. // Dart will also append the final JSON.stringify() call to return results. -"""; \ No newline at end of file +"""; diff --git a/lib/main.dart b/lib/main.dart index d8a4dd7f..f3c876c1 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,3 +1,4 @@ +import 'package:apidash/services/flutter_js_service.dart'; import 'package:apidash_design_system/apidash_design_system.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -11,6 +12,7 @@ void main() async { WidgetsFlutterBinding.ensureInitialized(); var settingsModel = await getSettingsFromSharedPrefs(); var onboardingStatus = await getOnboardingStatusFromSharedPrefs(); + initializeJsRuntime(); final initStatus = await initApp( kIsDesktop, settingsModel: settingsModel, diff --git a/lib/providers/collection_providers.dart b/lib/providers/collection_providers.dart index 51dd0a38..8414d8ef 100644 --- a/lib/providers/collection_providers.dart +++ b/lib/providers/collection_providers.dart @@ -1,3 +1,6 @@ +import 'dart:developer'; + +import 'package:apidash/services/flutter_js_service.dart'; import 'package:apidash_core/apidash_core.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -280,6 +283,18 @@ class CollectionStateNotifier return; } + if (requestModel != null && requestModel.preRequestScript.isNotEmpty) { + final res = await executePreRequestScript( + currentRequestModel: requestModel, + activeEnvironment: {}, + ); + requestModel = + requestModel.copyWith(httpRequestModel: res.updatedRequest); + log(res.updatedRequest.url); + log(res.updatedRequest.headersMap.toString()); + log(res.updatedRequest.body.toString()); + } + APIType apiType = requestModel!.apiType; HttpRequestModel substitutedHttpRequestModel = getSubstitutedHttpRequestModel(requestModel.httpRequestModel!); diff --git a/lib/services/flutter_js_service.dart b/lib/services/flutter_js_service.dart index 17aedc88..c4e1c63f 100644 --- a/lib/services/flutter_js_service.dart +++ b/lib/services/flutter_js_service.dart @@ -3,6 +3,7 @@ import 'dart:convert'; 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:flutter/services.dart'; @@ -12,6 +13,7 @@ late JavascriptRuntime jsRuntime; void initializeJsRuntime() { jsRuntime = getJavascriptRuntime(); + setupJsBridge(); } void disposeJsRuntime() { @@ -89,7 +91,6 @@ Future< })> executePreRequestScript({ required RequestModel currentRequestModel, required Map activeEnvironment, - required String setupScript, }) async { if (currentRequestModel.preRequestScript.trim().isEmpty) { // No script, return original data @@ -109,27 +110,37 @@ Future< // Inject data as JS variables // Escape strings properly if embedding directly final dataInjection = """ - const injectedRequestJson = ${jsEscapeString(requestJson)}; - const injectedEnvironmentJson = ${jsEscapeString(environmentJson)}; - const injectedResponseJson = null; // Not needed for pre-request + var injectedRequestJson = ${jsEscapeString(requestJson)}; + var injectedEnvironmentJson = ${jsEscapeString(environmentJson)}; + var injectedResponseJson = null; // Not needed for pre-request """; // Concatenate & Add Return final fullScript = """ - $dataInjection - $setupScript - // --- User Script --- - $userScript - // --- Return Result --- - JSON.stringify({ request: request, environment: environment }); + (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) --- + // Ensure 'request' and 'environment' are accessible here + return JSON.stringify({ request: request, environment: environment }); + })(); // Immediately invoke the function """; + + // TODO: Do something better to avoid null check here. HttpRequestModel resultingRequest = httpRequest!; Map resultingEnvironment = Map.from(activeEnvironment); try { // Execute - final JsEvalResult result = await jsRuntime.evaluateAsync(fullScript); + final JsEvalResult result = jsRuntime.evaluate(fullScript); // Process Results if (result.isError) { @@ -158,7 +169,6 @@ Future< } } catch (e) { print("Dart-level error during pre-request script execution: $e"); - // Handle Dart-level errors (e.g., JS runtime issues) } return (