feat: update pre-request and post-response script handling in CollectionStateNotifier

- Changed return type of handlePreRequestScript and handlePostResponseScript to Future<RequestModel> for better type safety.
- Updated the logic to return the modified request model after executing scripts.
- Ensured that the request model is updated correctly in the sendRequest method after executing the pre-request script.
This commit is contained in:
Udhay-Adithya
2025-05-19 16:54:32 +05:30
parent 464dd9aff8
commit fe4858ecd0
2 changed files with 565 additions and 478 deletions

View File

@@ -581,18 +581,33 @@ const ad = {
/** /**
* Adds or updates a header. If a header with the same name (case-sensitive) * Adds or updates a header. If a header with the same name (case-sensitive)
* already exists, it updates its value. Otherwise, adds a new header. * already exists, it updates its value. Otherwise, adds a new header.
* Also updates the isHeaderEnabledList to include {true} by default
* @param {string} key The header name. * @param {string} key The header name.
* @param {string} value The header value. * @param {string} value The header value.
* @param {boolean} isHeaderEnabledList value.
*/ */
set: (key, value) => { set: (key, value) => {
if (!request || typeof request !== 'object' || !Array.isArray(request.headers)) return; // Safety check if (!request || typeof request !== 'object' || !Array.isArray(request.headers)) return;
if (typeof key !== 'string') return; // Safety check if (typeof key !== 'string') return;
const stringValue = String(value); // Ensure value is a string
const existingHeaderIndex = request.headers.findIndex(h => typeof h === 'object' && h.name === key); const stringValue = String(value);
const existingHeaderIndex = request.headers.findIndex(
h => typeof h === 'object' && h.name === key
);
if (!Array.isArray(request.isHeaderEnabledList)) {
request.isHeaderEnabledList = [];
}
if (existingHeaderIndex > -1) { if (existingHeaderIndex > -1) {
request.headers[existingHeaderIndex].value = stringValue; request.headers[existingHeaderIndex].value = stringValue;
request.isHeaderEnabledList[existingHeaderIndex] = true;
} else { } else {
request.headers.push({ name: key, value: stringValue }); request.headers.push({
name: key,
value: stringValue
});
request.isHeaderEnabledList.push(true);
} }
}, },
/** /**
@@ -600,34 +615,57 @@ const ad = {
* @param {string} key The header name. * @param {string} key The header name.
* @returns {string|undefined} The header value or undefined if not found. * @returns {string|undefined} The header value or undefined if not found.
*/ */
get: (key) => { get: (key) => {
if (!request || typeof request !== 'object' || !Array.isArray(request.headers)) return undefined; // Safety check if (!request || typeof request !== 'object' || !Array.isArray(request.headers)) return undefined;
const header = request.headers.find(h => typeof h === 'object' && h.name === key); const header = request.headers.find(h => typeof h === 'object' && h.name === key);
return header ? header.value : undefined; return header ? header.value : undefined;
}, },
/** /**
* Removes all headers with the given name (case-sensitive). * Removes all headers with the given name (case-sensitive).
* @param {string} key The header name to remove. * @param {string} key The header name to remove.
*/ */
remove: (key) => { remove: (key) => {
if (!request || typeof request !== 'object' || !Array.isArray(request.headers)) return; // Safety check if (!request || typeof request !== 'object' || !Array.isArray(request.headers)) return;
request.headers = request.headers.filter(h => !(typeof h === 'object' && h.name === key));
if (!Array.isArray(request.isHeaderEnabledList)) {
request.isHeaderEnabledList = [];
}
const indicesToRemove = [];
request.headers.forEach((h, index) => {
if (typeof h === 'object' && h.name === key) {
indicesToRemove.push(index);
}
});
// Remove from end to start to prevent index shifting
for (let i = indicesToRemove.length - 1; i >= 0; i--) {
const idx = indicesToRemove[i];
request.headers.splice(idx, 1);
request.isHeaderEnabledList.splice(idx, 1);
}
}, },
/** /**
* Checks if a header with the given name exists (case-sensitive). * Checks if a header with the given name exists (case-sensitive).
* @param {string} key The header name. * @param {string} key The header name.
* @returns {boolean} True if the header exists, false otherwise. * @returns {boolean} True if the header exists, false otherwise.
*/ */
has: (key) => { has: (key) => {
if (!request || typeof request !== 'object' || !Array.isArray(request.headers)) return false; // Safety check if (!request || typeof request !== 'object' || !Array.isArray(request.headers)) return false;
return request.headers.some(h => typeof h === 'object' && h.name === key); return request.headers.some(h => typeof h === 'object' && h.name === key);
}, },
/** /**
* Clears all request headers. * Clears all request headers along with isHeaderEnabledList.
*/ */
clear: () => { clear: () => {
if (!request || typeof request !== 'object') return; // Safety check if (!request || typeof request !== 'object') return;
request.headers = []; request.headers = [];
request.isHeaderEnabledList = [];
} }
}, },
@@ -644,17 +682,26 @@ const ad = {
* @param {string} value The parameter value. * @param {string} value The parameter value.
*/ */
set: (key, value) => { set: (key, value) => {
if (!request || typeof request !== 'object' || !Array.isArray(request.params)) return; // Safety check if (!request || typeof request !== 'object' || !Array.isArray(request.params)) return;
if (typeof key !== 'string') return; // Safety check if (typeof key !== 'string') return;
const stringValue = String(value); // Ensure value is a string
// Note: Unlike headers, duplicate param keys are sometimes meaningful. const stringValue = String(value);
// This simple 'set' replaces the *first* occurrence or adds if not found.
// A different method like 'add' could be used to allow duplicates. if (!Array.isArray(request.isParamEnabledList)) {
request.isParamEnabledList = [];
}
const existingParamIndex = request.params.findIndex(p => typeof p === 'object' && p.name === key); const existingParamIndex = request.params.findIndex(p => typeof p === 'object' && p.name === key);
if (existingParamIndex > -1) { if (existingParamIndex > -1) {
request.params[existingParamIndex].value = stringValue; request.params[existingParamIndex].value = stringValue;
request.isParamEnabledList[existingParamIndex] = true;
} else { } else {
request.params.push({ name: key, value: stringValue }); request.params.push({
name: key,
value: stringValue
});
request.isParamEnabledList.push(true);
} }
}, },
/** /**
@@ -672,8 +719,24 @@ const ad = {
* @param {string} key The parameter name to remove. * @param {string} key The parameter name to remove.
*/ */
remove: (key) => { remove: (key) => {
if (!request || typeof request !== 'object' || !Array.isArray(request.params)) return; // Safety check if (!request || typeof request !== 'object' || !Array.isArray(request.params)) return;
request.params = request.params.filter(p => !(typeof p === 'object' && p.name === key));
if (!Array.isArray(request.isParamEnabledList)) {
request.isParamEnabledList = [];
}
const indicesToRemove = [];
request.params.forEach((p, index) => {
if (typeof p === 'object' && p.name === key) {
indicesToRemove.push(index);
}
});
for (let i = indicesToRemove.length - 1; i >= 0; i--) {
const idx = indicesToRemove[i];
request.params.splice(idx, 1);
request.isParamEnabledList.splice(idx, 1);
}
}, },
/** /**
* Checks if a query parameter with the given name exists (case-sensitive). * Checks if a query parameter with the given name exists (case-sensitive).
@@ -688,8 +751,9 @@ const ad = {
* Clears all query parameters. * Clears all query parameters.
*/ */
clear: () => { clear: () => {
if (!request || typeof request !== 'object') return; // Safety check if (!request || typeof request !== 'object') return;
request.params = []; request.params = [];
request.isParamEnabledList = [];
} }
}, },
@@ -804,20 +868,26 @@ const ad = {
* The HTTP status code of the response. * The HTTP status code of the response.
* @type {number|undefined} * @type {number|undefined}
*/ */
get status() { return (response && typeof response === 'object') ? response.statusCode : undefined; }, get status() {
return (response && typeof response === 'object') ? response.statusCode : undefined;
},
/** /**
* The response body as a string. If the response was binary, this might be decoded text * The response body as a string. If the response was binary, this might be decoded text
* based on Content-Type or potentially garbled. Use `bodyBytes` for raw binary data access (if provided). * based on Content-Type or potentially garbled. Use `bodyBytes` for raw binary data access (if provided).
* @type {string|undefined} * @type {string|undefined}
*/ */
get body() { return (response && typeof response === 'object') ? response.body : undefined; }, get body() {
return (response && typeof response === 'object') ? response.body : undefined;
},
/** /**
* The response body automatically formatted (e.g., pretty-printed JSON). Provided by Dart. * The response body automatically formatted (e.g., pretty-printed JSON). Provided by Dart.
* @type {string|undefined} * @type {string|undefined}
*/ */
get formattedBody() { return (response && typeof response === 'object') ? response.formattedBody : undefined; }, get formattedBody() {
return (response && typeof response === 'object') ? response.formattedBody : undefined;
},
/** /**
* The raw response body as an array of bytes (numbers). * The raw response body as an array of bytes (numbers).
@@ -825,7 +895,9 @@ const ad = {
* Accessing large byte arrays in JS might be memory-intensive. * Accessing large byte arrays in JS might be memory-intensive.
* @type {number[]|undefined} * @type {number[]|undefined}
*/ */
get bodyBytes() { return (response && typeof response === 'object') ? response.bodyBytes : undefined; }, get bodyBytes() {
return (response && typeof response === 'object') ? response.bodyBytes : undefined;
},
/** /**
@@ -843,14 +915,18 @@ const ad = {
* Header names are likely lowercase from the http package. * Header names are likely lowercase from the http package.
* @type {object|undefined} e.g., {'content-type': 'application/json', ...} * @type {object|undefined} e.g., {'content-type': 'application/json', ...}
*/ */
get headers() { return (response && typeof response === 'object') ? response.headers : undefined; }, get headers() {
return (response && typeof response === 'object') ? response.headers : undefined;
},
/** /**
* An object containing the request headers that were actually sent (useful for verification). * An object containing the request headers that were actually sent (useful for verification).
* Header names are likely lowercase. * Header names are likely lowercase.
* @type {object|undefined} e.g., {'user-agent': '...', ...} * @type {object|undefined} e.g., {'user-agent': '...', ...}
*/ */
get requestHeaders() { return (response && typeof response === 'object') ? response.requestHeaders : undefined; }, get requestHeaders() {
return (response && typeof response === 'object') ? response.requestHeaders : undefined;
},
/** /**
@@ -959,7 +1035,9 @@ const ad = {
log: (...args) => { log: (...args) => {
try { try {
sendMessage('consoleLog', JSON.stringify(args)); sendMessage('consoleLog', JSON.stringify(args));
} catch(e) { /* Ignore stringify errors for console? Or maybe log the error itself? */ } } catch (e) {
/* Ignore stringify errors for console? Or maybe log the error itself? */
}
}, },
/** /**
* Logs warning messages. * Logs warning messages.
@@ -968,7 +1046,9 @@ const ad = {
warn: (...args) => { warn: (...args) => {
try { try {
sendMessage('consoleWarn', JSON.stringify(args)); sendMessage('consoleWarn', JSON.stringify(args));
} catch(e) { /* Ignore */ } } catch (e) {
/* Ignore */
}
}, },
/** /**
* Logs error messages. * Logs error messages.
@@ -977,7 +1057,9 @@ const ad = {
error: (...args) => { error: (...args) => {
try { try {
sendMessage('consoleError', JSON.stringify(args)); sendMessage('consoleError', JSON.stringify(args));
} catch(e) { /* Ignore */ } } catch (e) {
/* Ignore */
}
} }
}, },

View File

@@ -267,7 +267,7 @@ class CollectionStateNotifier
unsave(); unsave();
} }
Future<void> handlePreRequestScript( Future<RequestModel> handlePreRequestScript(
RequestModel requestModel, RequestModel requestModel,
EnvironmentModel? originalEnvironmentModel, EnvironmentModel? originalEnvironmentModel,
) async { ) async {
@@ -275,10 +275,8 @@ class CollectionStateNotifier
currentRequestModel: requestModel, currentRequestModel: requestModel,
activeEnvironment: originalEnvironmentModel?.toJson() ?? {}, activeEnvironment: originalEnvironmentModel?.toJson() ?? {},
); );
final newRequestModel =
requestModel =
requestModel.copyWith(httpRequestModel: scriptResult.updatedRequest); requestModel.copyWith(httpRequestModel: scriptResult.updatedRequest);
if (originalEnvironmentModel != null) { if (originalEnvironmentModel != null) {
final updatedEnvironmentMap = scriptResult.updatedEnvironment; final updatedEnvironmentMap = scriptResult.updatedEnvironment;
@@ -322,14 +320,17 @@ class CollectionStateNotifier
} else { } else {
debugPrint( debugPrint(
"Skipped environment update as originalEnvironmentModel was null."); "Skipped environment update as originalEnvironmentModel was null.");
if (scriptResult.updatedEnvironment.isNotEmpty) { if (scriptResult.updatedEnvironment.isNotEmpty) {
debugPrint( debugPrint(
"Warning: Pre-request script updated environment variables, but no active environment was selected to save them to."); "Warning: Pre-request script updated environment variables, but no active environment was selected to save them to.");
} }
return requestModel;
} }
return newRequestModel;
} }
Future<void> handlePostResponseScript( Future<RequestModel> handlePostResponseScript(
RequestModel requestModel, RequestModel requestModel,
EnvironmentModel? originalEnvironmentModel, EnvironmentModel? originalEnvironmentModel,
) async { ) async {
@@ -338,7 +339,7 @@ class CollectionStateNotifier
activeEnvironment: originalEnvironmentModel?.toJson() ?? {}, activeEnvironment: originalEnvironmentModel?.toJson() ?? {},
); );
requestModel = final newRequestModel =
requestModel.copyWith(httpResponseModel: scriptResult.updatedResponse); requestModel.copyWith(httpResponseModel: scriptResult.updatedResponse);
if (originalEnvironmentModel != null) { if (originalEnvironmentModel != null) {
@@ -388,7 +389,9 @@ class CollectionStateNotifier
debugPrint( debugPrint(
"Warning: Post-response script updated environment variables, but no active environment was selected to save them to."); "Warning: Post-response script updated environment variables, but no active environment was selected to save them to.");
} }
return requestModel;
} }
return newRequestModel;
} }
Future<void> sendRequest() async { Future<void> sendRequest() async {
@@ -408,6 +411,7 @@ class CollectionStateNotifier
} }
if (requestModel != null && requestModel.preRequestScript.isNotEmpty) { if (requestModel != null && requestModel.preRequestScript.isNotEmpty) {
requestModel =
await handlePreRequestScript(requestModel, originalEnvironmentModel); await handlePreRequestScript(requestModel, originalEnvironmentModel);
} }
@@ -432,7 +436,7 @@ class CollectionStateNotifier
noSSL: noSSL, noSSL: noSSL,
); );
late final RequestModel newRequestModel; late RequestModel newRequestModel;
if (responseRec.$1 == null) { if (responseRec.$1 == null) {
newRequestModel = requestModel.copyWith( newRequestModel = requestModel.copyWith(
responseStatus: -1, responseStatus: -1,
@@ -468,7 +472,8 @@ class CollectionStateNotifier
httpResponseModel: httpResponseModel, httpResponseModel: httpResponseModel,
); );
if (requestModel.postRequestScript.isNotEmpty) { if (requestModel.postRequestScript.isNotEmpty) {
handlePostResponseScript(newRequestModel, originalEnvironmentModel); newRequestModel = await handlePostResponseScript(
newRequestModel, originalEnvironmentModel);
} }
ref.read(historyMetaStateNotifier.notifier).addHistoryRequest(model); ref.read(historyMetaStateNotifier.notifier).addHistoryRequest(model);
} }