mirror of
https://github.com/foss42/apidash.git
synced 2025-12-02 10:49:49 +08:00
feat: implement js runtime provider and remove old script service
This commit is contained in:
@@ -19,7 +19,6 @@ void main() async {
|
||||
|
||||
var settingsModel = await getSettingsFromSharedPrefs();
|
||||
var onboardingStatus = await getOnboardingStatusFromSharedPrefs();
|
||||
initializeJsRuntime();
|
||||
final initStatus = await initApp(
|
||||
kIsDesktop,
|
||||
settingsModel: settingsModel,
|
||||
|
||||
@@ -7,7 +7,6 @@ import 'providers.dart';
|
||||
import '../models/models.dart';
|
||||
import '../services/services.dart';
|
||||
import '../utils/utils.dart';
|
||||
import '../models/terminal_models.dart';
|
||||
|
||||
final selectedIdStateProvider = StateProvider<String?>((ref) => null);
|
||||
|
||||
@@ -321,7 +320,9 @@ class CollectionStateNotifier
|
||||
RequestModel executionRequestModel = requestModel!.copyWith();
|
||||
|
||||
if (!requestModel.preRequestScript.isNullOrEmpty()) {
|
||||
executionRequestModel = await handlePreRequestScript(
|
||||
executionRequestModel = await ref
|
||||
.read(jsRuntimeNotifierProvider.notifier)
|
||||
.handlePreRequestScript(
|
||||
executionRequestModel,
|
||||
originalEnvironmentModel,
|
||||
(envModel, updatedValues) {
|
||||
@@ -525,7 +526,9 @@ class CollectionStateNotifier
|
||||
.addHistoryRequest(historyModel!);
|
||||
|
||||
if (!requestModel.postRequestScript.isNullOrEmpty()) {
|
||||
newRequestModel = await handlePostResponseScript(
|
||||
newRequestModel = await ref
|
||||
.read(jsRuntimeNotifierProvider.notifier)
|
||||
.handlePostResponseScript(
|
||||
newRequestModel,
|
||||
originalEnvironmentModel,
|
||||
(envModel, updatedValues) {
|
||||
|
||||
379
lib/providers/js_runtime_notifier.dart
Normal file
379
lib/providers/js_runtime_notifier.dart
Normal file
@@ -0,0 +1,379 @@
|
||||
// ignore_for_file: avoid_print
|
||||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
import 'package:apidash_core/apidash_core.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_js/flutter_js.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import '../models/models.dart';
|
||||
import '../utils/utils.dart';
|
||||
import '../providers/terminal_providers.dart';
|
||||
|
||||
class JsRuntimeState {
|
||||
const JsRuntimeState({
|
||||
this.initialized = false,
|
||||
this.lastError,
|
||||
this.executedScriptCount = 0,
|
||||
});
|
||||
|
||||
final bool initialized;
|
||||
final String? lastError;
|
||||
final int executedScriptCount;
|
||||
|
||||
JsRuntimeState copyWith({
|
||||
bool? initialized,
|
||||
String? lastError,
|
||||
int? executedScriptCount,
|
||||
}) =>
|
||||
JsRuntimeState(
|
||||
initialized: initialized ?? this.initialized,
|
||||
lastError: lastError ?? this.lastError,
|
||||
executedScriptCount: executedScriptCount ?? this.executedScriptCount,
|
||||
);
|
||||
}
|
||||
|
||||
final jsRuntimeNotifierProvider =
|
||||
StateNotifierProvider<JsRuntimeNotifier, JsRuntimeState>((ref) {
|
||||
final notifier = JsRuntimeNotifier(ref);
|
||||
notifier._initialize();
|
||||
return notifier;
|
||||
});
|
||||
|
||||
class JsRuntimeNotifier extends StateNotifier<JsRuntimeState> {
|
||||
JsRuntimeNotifier(this.ref) : super(const JsRuntimeState());
|
||||
|
||||
final Ref ref;
|
||||
late final JavascriptRuntime _runtime;
|
||||
|
||||
void _initialize() {
|
||||
if (state.initialized) return;
|
||||
_runtime = getJavascriptRuntime();
|
||||
_setupJsBridge();
|
||||
state = state.copyWith(initialized: true);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
// Guard: runtime may already be disposed by underlying provider disposal
|
||||
try {
|
||||
if (state.initialized) {
|
||||
_runtime.dispose();
|
||||
}
|
||||
} catch (_) {
|
||||
// swallow disposal errors
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
JsEvalResult evaluate(String code) {
|
||||
// If disposed, prevent usage
|
||||
if (!mounted) {
|
||||
throw StateError('JsRuntimeNotifier used after dispose');
|
||||
}
|
||||
try {
|
||||
final res = _runtime.evaluate(code);
|
||||
state = state.copyWith(
|
||||
executedScriptCount: state.executedScriptCount + 1,
|
||||
lastError: res.isError ? res.stringResult : state.lastError,
|
||||
);
|
||||
log(res.stringResult);
|
||||
return res;
|
||||
} on PlatformException catch (e) {
|
||||
final msg = 'Platform ERROR: ${e.details}';
|
||||
state = state.copyWith(lastError: msg);
|
||||
log(msg);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<
|
||||
({
|
||||
HttpRequestModel updatedRequest,
|
||||
Map<String, dynamic> updatedEnvironment
|
||||
})> executePreRequestScript({
|
||||
required RequestModel currentRequestModel,
|
||||
required Map<String, dynamic> activeEnvironment,
|
||||
}) async {
|
||||
if ((currentRequestModel.preRequestScript ?? '').trim().isEmpty) {
|
||||
return (
|
||||
updatedRequest: currentRequestModel.httpRequestModel!,
|
||||
updatedEnvironment: activeEnvironment,
|
||||
);
|
||||
}
|
||||
|
||||
final httpRequest = currentRequestModel.httpRequestModel;
|
||||
final userScript = currentRequestModel.preRequestScript;
|
||||
final requestJson = jsonEncode(httpRequest?.toJson());
|
||||
final environmentJson = jsonEncode(activeEnvironment);
|
||||
final dataInjection = '''
|
||||
var injectedRequestJson = ${jsEscapeString(requestJson)};
|
||||
var injectedEnvironmentJson = ${jsEscapeString(environmentJson)};
|
||||
var injectedResponseJson = null; // Not needed for pre-request
|
||||
''';
|
||||
final fullScript = '''
|
||||
(function() {
|
||||
$dataInjection
|
||||
$kJSSetupScript
|
||||
$userScript
|
||||
return JSON.stringify({ request: request, environment: environment });
|
||||
})();
|
||||
''';
|
||||
|
||||
HttpRequestModel resultingRequest = httpRequest!;
|
||||
Map<String, dynamic> resultingEnvironment = Map.from(activeEnvironment);
|
||||
try {
|
||||
final res = _runtime.evaluate(fullScript);
|
||||
state = state.copyWith(
|
||||
executedScriptCount: state.executedScriptCount + 1,
|
||||
lastError: res.isError ? res.stringResult : state.lastError,
|
||||
);
|
||||
if (res.isError) {
|
||||
print('Pre-request script execution error: ${res.stringResult}');
|
||||
} else if (res.stringResult.isNotEmpty) {
|
||||
final decoded = jsonDecode(res.stringResult);
|
||||
if (decoded is Map<String, dynamic>) {
|
||||
if (decoded['request'] is Map) {
|
||||
try {
|
||||
resultingRequest = HttpRequestModel.fromJson(
|
||||
Map<String, Object?>.from(decoded['request'] as Map),
|
||||
);
|
||||
} catch (e) {
|
||||
print('Error deserializing modified request from script: $e');
|
||||
}
|
||||
}
|
||||
if (decoded['environment'] is Map) {
|
||||
resultingEnvironment =
|
||||
Map<String, dynamic>.from(decoded['environment'] as Map);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
final msg = 'Dart-level error during pre-request script execution: $e';
|
||||
state = state.copyWith(lastError: msg);
|
||||
print(msg);
|
||||
}
|
||||
return (
|
||||
updatedRequest: resultingRequest,
|
||||
updatedEnvironment: resultingEnvironment,
|
||||
);
|
||||
}
|
||||
|
||||
Future<
|
||||
({
|
||||
HttpResponseModel updatedResponse,
|
||||
Map<String, dynamic> updatedEnvironment
|
||||
})> executePostResponseScript({
|
||||
required RequestModel currentRequestModel,
|
||||
required Map<String, dynamic> activeEnvironment,
|
||||
}) async {
|
||||
if ((currentRequestModel.postRequestScript ?? '').trim().isEmpty) {
|
||||
return (
|
||||
updatedResponse: currentRequestModel.httpResponseModel!,
|
||||
updatedEnvironment: activeEnvironment,
|
||||
);
|
||||
}
|
||||
|
||||
final httpRequest = currentRequestModel.httpRequestModel; // for future use
|
||||
final httpResponse = currentRequestModel.httpResponseModel;
|
||||
final userScript = currentRequestModel.postRequestScript;
|
||||
final requestJson = jsonEncode(httpRequest?.toJson());
|
||||
final responseJson = jsonEncode(httpResponse?.toJson());
|
||||
final environmentJson = jsonEncode(activeEnvironment);
|
||||
final dataInjection = '''
|
||||
var injectedRequestJson = ${jsEscapeString(requestJson)};
|
||||
var injectedEnvironmentJson = ${jsEscapeString(environmentJson)};
|
||||
var injectedResponseJson = ${jsEscapeString(responseJson)};
|
||||
''';
|
||||
final fullScript = '''
|
||||
(function() {
|
||||
$dataInjection
|
||||
$kJSSetupScript
|
||||
$userScript
|
||||
return JSON.stringify({ response: response, environment: environment });
|
||||
})();
|
||||
''';
|
||||
|
||||
HttpResponseModel resultingResponse = httpResponse!;
|
||||
Map<String, dynamic> resultingEnvironment = Map.from(activeEnvironment);
|
||||
try {
|
||||
final res = _runtime.evaluate(fullScript);
|
||||
state = state.copyWith(
|
||||
executedScriptCount: state.executedScriptCount + 1,
|
||||
lastError: res.isError ? res.stringResult : state.lastError,
|
||||
);
|
||||
if (res.isError) {
|
||||
print('Post-response script execution error: ${res.stringResult}');
|
||||
} else if (res.stringResult.isNotEmpty) {
|
||||
final decoded = jsonDecode(res.stringResult);
|
||||
if (decoded is Map<String, dynamic>) {
|
||||
if (decoded['response'] is Map) {
|
||||
try {
|
||||
resultingResponse = HttpResponseModel.fromJson(
|
||||
Map<String, Object?>.from(decoded['response'] as Map),
|
||||
);
|
||||
} catch (e) {
|
||||
print('Error deserializing modified response from script: $e');
|
||||
}
|
||||
}
|
||||
if (decoded['environment'] is Map) {
|
||||
resultingEnvironment =
|
||||
Map<String, dynamic>.from(decoded['environment'] as Map);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
final msg = 'Dart-level error during post-response script execution: $e';
|
||||
state = state.copyWith(lastError: msg);
|
||||
print(msg);
|
||||
}
|
||||
return (
|
||||
updatedResponse: resultingResponse,
|
||||
updatedEnvironment: resultingEnvironment,
|
||||
);
|
||||
}
|
||||
|
||||
// High-level helpers (migrated from pre_post_script_utils) -----------------
|
||||
|
||||
Future<RequestModel> handlePreRequestScript(
|
||||
RequestModel requestModel,
|
||||
EnvironmentModel? originalEnvironmentModel,
|
||||
void Function(EnvironmentModel, List<EnvironmentVariableModel>)? updateEnv,
|
||||
) async {
|
||||
final scriptResult = await executePreRequestScript(
|
||||
currentRequestModel: requestModel,
|
||||
activeEnvironment: originalEnvironmentModel?.toJson() ?? {},
|
||||
);
|
||||
final newRequestModel =
|
||||
requestModel.copyWith(httpRequestModel: scriptResult.updatedRequest);
|
||||
if (originalEnvironmentModel != null) {
|
||||
final updatedEnvironmentMap = scriptResult.updatedEnvironment;
|
||||
final List<EnvironmentVariableModel> newValues = [];
|
||||
final Map<String, dynamic> 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);
|
||||
}
|
||||
}
|
||||
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,
|
||||
),
|
||||
);
|
||||
}
|
||||
updateEnv?.call(originalEnvironmentModel, newValues);
|
||||
} else {
|
||||
if (scriptResult.updatedEnvironment.isNotEmpty) {
|
||||
print(
|
||||
'Warning: Pre-request script updated environment variables, but no active environment was selected to save them to.');
|
||||
return requestModel;
|
||||
}
|
||||
return newRequestModel;
|
||||
}
|
||||
return newRequestModel;
|
||||
}
|
||||
|
||||
Future<RequestModel> handlePostResponseScript(
|
||||
RequestModel requestModel,
|
||||
EnvironmentModel? originalEnvironmentModel,
|
||||
void Function(EnvironmentModel, List<EnvironmentVariableModel>)? updateEnv,
|
||||
) async {
|
||||
final scriptResult = await executePostResponseScript(
|
||||
currentRequestModel: requestModel,
|
||||
activeEnvironment: originalEnvironmentModel?.toJson() ?? {'values': []},
|
||||
);
|
||||
final newRequestModel =
|
||||
requestModel.copyWith(httpResponseModel: scriptResult.updatedResponse);
|
||||
if (originalEnvironmentModel != null) {
|
||||
final updatedEnvironmentMap = scriptResult.updatedEnvironment;
|
||||
final List<EnvironmentVariableModel> newValues = [];
|
||||
final Map<String, dynamic> 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);
|
||||
}
|
||||
}
|
||||
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,
|
||||
),
|
||||
);
|
||||
}
|
||||
updateEnv?.call(originalEnvironmentModel, newValues);
|
||||
} else {
|
||||
if (scriptResult.updatedEnvironment.isNotEmpty) {
|
||||
print(
|
||||
'Warning: Post-response script updated environment variables, but no active environment was selected to save them to.');
|
||||
}
|
||||
return requestModel;
|
||||
}
|
||||
return newRequestModel;
|
||||
}
|
||||
|
||||
void _setupJsBridge() {
|
||||
_runtime.onMessage('consoleLog', (args) => _handleConsole('log', args));
|
||||
_runtime.onMessage('consoleWarn', (args) => _handleConsole('warn', args));
|
||||
_runtime.onMessage('consoleError', (args) => _handleConsole('error', args));
|
||||
_runtime.onMessage('fatalError', (args) => _handleFatal(args));
|
||||
}
|
||||
|
||||
void _handleConsole(String level, dynamic args) {
|
||||
try {
|
||||
final term = ref.read(terminalStateProvider.notifier);
|
||||
final List<String> argList = (args is List)
|
||||
? args.map((e) => e.toString()).toList()
|
||||
: [args.toString()];
|
||||
term.logJs(level: level, args: argList);
|
||||
} catch (e) {
|
||||
print('[JS ${level.toUpperCase()} HANDLER ERROR]: $args, Error: $e');
|
||||
}
|
||||
}
|
||||
|
||||
void _handleFatal(dynamic args) {
|
||||
try {
|
||||
final term = ref.read(terminalStateProvider.notifier);
|
||||
if (args is Map<String, dynamic>) {
|
||||
final message = args['message']?.toString() ?? 'Unknown fatal error';
|
||||
final error = args['error']?.toString();
|
||||
final stack = args['stack']?.toString();
|
||||
term.logJs(
|
||||
level: 'fatal',
|
||||
args: [if (error != null) error, message],
|
||||
stack: stack,
|
||||
);
|
||||
} else {
|
||||
term.logJs(level: 'fatal', args: ['Malformed fatal payload', '$args']);
|
||||
}
|
||||
} catch (e) {
|
||||
print('[JS FATAL ERROR decoding error]: $args, Error: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to properly escape strings for JS embedding
|
||||
String jsEscapeString(String s) => jsonEncode(s);
|
||||
@@ -5,3 +5,4 @@ export 'history_providers.dart';
|
||||
export 'terminal_providers.dart';
|
||||
export 'settings_providers.dart';
|
||||
export 'ui_providers.dart';
|
||||
export 'js_runtime_notifier.dart';
|
||||
|
||||
@@ -1,275 +0,0 @@
|
||||
// ignore_for_file: avoid_print
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
import 'package:apidash_core/apidash_core.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_js/flutter_js.dart';
|
||||
import '../models/models.dart';
|
||||
import '../utils/utils.dart';
|
||||
|
||||
late JavascriptRuntime jsRuntime;
|
||||
|
||||
void initializeJsRuntime() {
|
||||
jsRuntime = getJavascriptRuntime();
|
||||
setupJsBridge();
|
||||
}
|
||||
|
||||
void disposeJsRuntime() {
|
||||
jsRuntime.dispose();
|
||||
}
|
||||
|
||||
void evaluate(String code) {
|
||||
try {
|
||||
JsEvalResult jsResult = jsRuntime.evaluate(code);
|
||||
log(jsResult.stringResult);
|
||||
} on PlatformException catch (e) {
|
||||
log('ERROR: ${e.details}');
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: These log statements can be printed in a custom api dash terminal.
|
||||
void setupJsBridge() {
|
||||
jsRuntime.onMessage('consoleLog', (args) {
|
||||
try {
|
||||
if (args is List) {
|
||||
print('[JS LOG]: ${args.map((e) => e.toString()).join(' ')}');
|
||||
} else {
|
||||
print('[JS LOG]: $args');
|
||||
}
|
||||
} catch (e) {
|
||||
print('[JS LOG ERROR]: $args, Error: $e');
|
||||
}
|
||||
});
|
||||
|
||||
jsRuntime.onMessage('consoleWarn', (args) {
|
||||
try {
|
||||
if (args is List) {
|
||||
print('[JS WARN]: ${args.map((e) => e.toString()).join(' ')}');
|
||||
} else {
|
||||
print('[JS WARN]: $args');
|
||||
}
|
||||
} catch (e) {
|
||||
print('[JS WARN ERROR]: $args, Error: $e');
|
||||
}
|
||||
});
|
||||
|
||||
jsRuntime.onMessage('consoleError', (args) {
|
||||
try {
|
||||
if (args is List) {
|
||||
print('[JS ERROR]: ${args.map((e) => e.toString()).join(' ')}');
|
||||
} else {
|
||||
print('[JS ERROR]: $args');
|
||||
}
|
||||
} catch (e) {
|
||||
print('[JS ERROR ERROR]: $args, Error: $e');
|
||||
}
|
||||
});
|
||||
|
||||
jsRuntime.onMessage('fatalError', (args) {
|
||||
try {
|
||||
// 'fatalError' message is constructed as a JSON object in setupScript
|
||||
if (args is Map<String, dynamic>) {
|
||||
print('[JS FATAL ERROR]: ${args['message']}');
|
||||
if (args['error'] != null) print(' Error: ${args['error']}');
|
||||
if (args['stack'] != null) print(' Stack: ${args['stack']}');
|
||||
} else {
|
||||
print('[JS FATAL ERROR decoding error]: $args, Expected a Map.');
|
||||
}
|
||||
} catch (e) {
|
||||
print('[JS FATAL ERROR decoding error]: $args, Error: $e');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<
|
||||
({
|
||||
HttpRequestModel updatedRequest,
|
||||
Map<String, dynamic> updatedEnvironment
|
||||
})> executePreRequestScript({
|
||||
required RequestModel currentRequestModel,
|
||||
required Map<String, dynamic> activeEnvironment,
|
||||
}) async {
|
||||
if ((currentRequestModel.preRequestScript ?? "").trim().isEmpty) {
|
||||
// No script, return original data
|
||||
// return (
|
||||
// updatedRequest: currentRequestModel.httpRequestModel,
|
||||
// updatedEnvironment: activeEnvironment
|
||||
// );
|
||||
}
|
||||
|
||||
final httpRequest = currentRequestModel.httpRequestModel;
|
||||
final userScript = currentRequestModel.preRequestScript;
|
||||
|
||||
// Prepare Data
|
||||
final requestJson = jsonEncode(httpRequest?.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 = null; // Not needed for pre-request
|
||||
""";
|
||||
|
||||
// Concatenate & Add Return
|
||||
final fullScript = """
|
||||
(function() {
|
||||
// --- Data Injection (now constants within the IIFE scope) ---
|
||||
$dataInjection
|
||||
|
||||
// --- Setup Script (will declare variables within the IIFE scope) ---
|
||||
$kJSSetupScript
|
||||
|
||||
// --- 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<String, dynamic> resultingEnvironment = Map.from(activeEnvironment);
|
||||
|
||||
try {
|
||||
// Execute
|
||||
final JsEvalResult result = jsRuntime.evaluate(fullScript);
|
||||
|
||||
// Process Results
|
||||
if (result.isError) {
|
||||
print("Pre-request script execution error: ${result.stringResult}");
|
||||
//TODO: Handle error - log this error in the logs console
|
||||
} else if (result.stringResult.isNotEmpty) {
|
||||
final resultMap = jsonDecode(result.stringResult);
|
||||
if (resultMap is Map<String, dynamic>) {
|
||||
// Deserialize Request
|
||||
if (resultMap.containsKey('request') && resultMap['request'] is Map) {
|
||||
try {
|
||||
resultingRequest = HttpRequestModel.fromJson(
|
||||
Map<String, Object?>.from(resultMap['request']));
|
||||
} catch (e) {
|
||||
print("Error deserializing modified request from script: $e");
|
||||
//TODO: Handle error - log this error in the logs console
|
||||
}
|
||||
}
|
||||
// Get Environment Modifications
|
||||
if (resultMap.containsKey('environment') &&
|
||||
resultMap['environment'] is Map) {
|
||||
resultingEnvironment =
|
||||
Map<String, dynamic>.from(resultMap['environment']);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
print("Dart-level error during pre-request script execution: $e");
|
||||
}
|
||||
|
||||
return (
|
||||
updatedRequest: resultingRequest,
|
||||
updatedEnvironment: resultingEnvironment
|
||||
);
|
||||
}
|
||||
|
||||
Future<
|
||||
({
|
||||
HttpResponseModel updatedResponse,
|
||||
Map<String, dynamic> updatedEnvironment
|
||||
})> executePostResponseScript({
|
||||
required RequestModel currentRequestModel,
|
||||
required Map<String, dynamic> 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) ---
|
||||
$kJSSetupScript
|
||||
|
||||
// --- 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<String, dynamic> 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);
|
||||
if (resultMap is Map<String, dynamic>) {
|
||||
// Deserialize Request
|
||||
if (resultMap.containsKey('response') && resultMap['response'] is Map) {
|
||||
try {
|
||||
resultingResponse = HttpResponseModel.fromJson(
|
||||
Map<String, Object?>.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<String, dynamic>.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(
|
||||
s); // jsonEncode handles escaping correctly for JS string literals
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
import 'package:apidash_core/apidash_core.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import '../models/models.dart';
|
||||
import '../services/services.dart';
|
||||
|
||||
Future<RequestModel> handlePreRequestScript(
|
||||
RequestModel requestModel,
|
||||
EnvironmentModel? originalEnvironmentModel,
|
||||
void Function(EnvironmentModel, List<EnvironmentVariableModel>)? updateEnv,
|
||||
) async {
|
||||
final scriptResult = await executePreRequestScript(
|
||||
currentRequestModel: requestModel,
|
||||
activeEnvironment: originalEnvironmentModel?.toJson() ?? {},
|
||||
);
|
||||
final newRequestModel =
|
||||
requestModel.copyWith(httpRequestModel: scriptResult.updatedRequest);
|
||||
if (originalEnvironmentModel != null) {
|
||||
final updatedEnvironmentMap = scriptResult.updatedEnvironment;
|
||||
|
||||
final List<EnvironmentVariableModel> newValues = [];
|
||||
final Map<String, dynamic> 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,
|
||||
),
|
||||
);
|
||||
}
|
||||
updateEnv?.call(originalEnvironmentModel, 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.");
|
||||
return requestModel;
|
||||
}
|
||||
return newRequestModel;
|
||||
}
|
||||
return newRequestModel;
|
||||
}
|
||||
|
||||
Future<RequestModel> handlePostResponseScript(
|
||||
RequestModel requestModel,
|
||||
EnvironmentModel? originalEnvironmentModel,
|
||||
void Function(EnvironmentModel, List<EnvironmentVariableModel>)? updateEnv,
|
||||
) async {
|
||||
final scriptResult = await executePostResponseScript(
|
||||
currentRequestModel: requestModel,
|
||||
activeEnvironment: originalEnvironmentModel?.toJson() ?? {"values": []},
|
||||
);
|
||||
|
||||
final newRequestModel =
|
||||
requestModel.copyWith(httpResponseModel: scriptResult.updatedResponse);
|
||||
|
||||
if (originalEnvironmentModel != null) {
|
||||
final updatedEnvironmentMap = scriptResult.updatedEnvironment;
|
||||
|
||||
final List<EnvironmentVariableModel> newValues = [];
|
||||
final Map<String, dynamic> 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,
|
||||
),
|
||||
);
|
||||
}
|
||||
updateEnv?.call(originalEnvironmentModel, newValues);
|
||||
} else {
|
||||
debugPrint(
|
||||
"Skipped environment update as originalEnvironmentModel was null.");
|
||||
if (scriptResult.updatedEnvironment.isNotEmpty) {
|
||||
debugPrint(
|
||||
"Warning: Post-response script updated environment variables, but no active environment was selected to save them to.");
|
||||
}
|
||||
return requestModel;
|
||||
}
|
||||
return newRequestModel;
|
||||
}
|
||||
@@ -6,7 +6,6 @@ export 'header_utils.dart';
|
||||
export 'history_utils.dart';
|
||||
export 'http_utils.dart';
|
||||
export 'js_utils.dart';
|
||||
export 'pre_post_script_utils.dart';
|
||||
export 'save_utils.dart';
|
||||
export 'ui_utils.dart';
|
||||
export 'window_utils.dart';
|
||||
|
||||
Reference in New Issue
Block a user