feat: add scripting user guide

This commit is contained in:
Udhay-Adithya
2025-06-20 21:30:09 +05:30
parent ad59d56975
commit 858639ef92
2 changed files with 508 additions and 10 deletions

View File

@ -0,0 +1,498 @@
# APIDash Scripting: Using the `ad` Object
APIDash allows you to write JavaScript code that runs either **before** a request is sent (pre-request script) or **after** a response is received (post-response script). These scripts give you powerful control over the request/response lifecycle and allow for automation, dynamic data manipulation, and basic testing.
The primary way to interact with APIDash data and functionality within these scripts is through the global `ad` object. This object provides structured access to request data, response data, environment variables, and logging utilities.
## `ad.request` (Available in Pre-request Scripts Only)
Use `ad.request` to inspect and modify the request *before* it is sent.
### `ad.request.headers`
Manage request headers. Headers are stored internally as an array of objects: `[{name: "Header-Name", value: "HeaderValue"}, ...]`. Note that header name comparisons in these helper methods are **case-sensitive**.
* **`ad.request.headers.set(key: string, value: string)`**
* Adds a new header or updates the value of the *first* existing header with the exact same `key`. Ensures the `value` is converted to a string.
* Example:
```javascript
// Set an Authorization header
let token = ad.environment.get("authToken");
if (token) {
ad.request.headers.set("Authorization", "Bearer " + token);
}
// Update User-Agent
ad.request.headers.set("User-Agent", "APIDash-Script/1.0");
```
* **`ad.request.headers.get(key: string): string | undefined`**
* Retrieves the value of the *first* header matching the `key`. Returns `undefined` if not found.
* Example:
```javascript
let contentType = ad.request.headers.get("Content-Type");
if (!contentType) {
ad.console.log("Content-Type header is not set yet.");
}
```
* **`ad.request.headers.remove(key: string)`**
* Removes *all* headers matching the exact `key`.
* Example:
```javascript
// Remove default Accept header if it exists
ad.request.headers.remove("Accept");
```
* **`ad.request.headers.has(key: string): boolean`**
* Checks if at least one header with the exact `key` exists.
* Example:
```javascript
if (!ad.request.headers.has("X-Request-ID")) {
ad.request.headers.set("X-Request-ID", Date.now()); // Simple example ID
}
```
* **`ad.request.headers.clear()`**
* Removes all request headers.
* Example:
```javascript
// Start with a clean slate for headers (use with caution)
// ad.request.headers.clear();
// ad.request.headers.set("Authorization", "..."); // Add back essential ones
```
### `ad.request.params`
Manage URL query parameters. Parameters are stored internally as an array of objects: `[{name: "paramName", value: "paramValue"}, ...]`. Parameter name comparisons are **case-sensitive**.
* **`ad.request.params.set(key: string, value: string)`**
* Adds a new query parameter or updates the value of the *first* existing parameter with the exact same `key`. Ensures the `value` is converted to a string.
* *Note:* HTTP allows duplicate query parameter keys. This `set` method replaces the first match or adds a new one. If you need duplicates, you might need to manipulate the underlying `request.params` array directly (use with care) or call `set` multiple times if the backend handles updates correctly.
* Example:
```javascript
// Add or update a timestamp parameter
ad.request.params.set("ts", Date.now().toString());
// Set a user ID from the environment
let userId = ad.environment.get("activeUserId");
if (userId) {
ad.request.params.set("userId", userId);
}
```
* **`ad.request.params.get(key: string): string | undefined`**
* Retrieves the value of the *first* parameter matching the `key`. Returns `undefined` if not found.
* Example:
```javascript
let existingFilter = ad.request.params.get("filter");
ad.console.log("Current filter:", existingFilter);
```
* **`ad.request.params.remove(key: string)`**
* Removes *all* parameters matching the exact `key`.
* Example:
```javascript
// Remove any debug flags before sending
ad.request.params.remove("debug");
```
* **`ad.request.params.has(key: string): boolean`**
* Checks if at least one parameter with the exact `key` exists.
* Example:
```javascript
if (!ad.request.params.has("page")) {
ad.request.params.set("page", "1");
}
```
* **`ad.request.params.clear()`**
* Removes all query parameters.
* Example:
```javascript
// Clear existing params if rebuilding the query string
// ad.request.params.clear();
// ad.request.params.set("newParam", "value");
```
### `ad.request.url`
Access or modify the entire request URL.
* **`ad.request.url.get(): string`**
* Returns the current request URL string.
* Example:
```javascript
let currentUrl = ad.request.url.get();
ad.console.log("Request URL before modification:", currentUrl);
```
* **`ad.request.url.set(newUrl: string)`**
* Sets the request URL to the provided `newUrl` string.
* Example:
```javascript
let baseUrl = ad.environment.get("apiBaseUrl");
let resourcePath = "/users/me";
if (baseUrl) {
ad.request.url.set(baseUrl + resourcePath);
}
```
### `ad.request.body`
Access or modify the request body.
* **`ad.request.body.get(): string | null | undefined`**
* Returns the current request body as a string. For `multipart/form-data`, this might return an empty or non-representative string; use `request.formData` (accessed directly for now, potential future `ad.request.formData` helpers) for structured form data.
* Example:
```javascript
let bodyContent = ad.request.body.get();
ad.console.log("Current body:", bodyContent);
```
* **`ad.request.body.set(newBody: string | object, contentType?: string)`**
* Sets the request body.
* If `newBody` is an object, it's automatically `JSON.stringify`-ed.
* If `newBody` is not an object, it's converted to a string.
* **Content-Type Handling:**
* If you provide the optional `contentType` argument (e.g., 'application/xml'), that value is used.
* If `contentType` is *not* provided:
* Defaults to `application/json` if `newBody` was an object.
* Defaults to `text/plain` otherwise.
* The calculated `Content-Type` is automatically added as a request header *unless* a `Content-Type` header already exists (case-insensitive check).
* **Side Effect:** Setting the body via this method will clear any existing `request.formData` entries, as they are mutually exclusive with a raw string/JSON body in the APIDash model. It also updates the internal `request.bodyContentType`.
* Example:
```javascript
// Set a JSON body
let userData = { name: "Test User", email: "test@example.com" };
ad.request.body.set(userData); // Automatically sets Content-Type: application/json if not already set
// Set a plain text body
ad.request.body.set("This is plain text content.", "text/plain");
// Set an XML body (Content-Type needed)
let xmlString = "<user><name>Test</name></user>";
ad.request.body.set(xmlString, "application/xml");
```
### `ad.request.method`
Access or modify the HTTP request method (verb).
* **`ad.request.method.get(): string`**
* Returns the current request method (e.g., "get", "post"). Usually lowercase.
* Example:
```javascript
let method = ad.request.method.get();
ad.console.log("Request method:", method);
```
* **`ad.request.method.set(newMethod: string)`**
* Sets the request method. The provided `newMethod` string will be converted to lowercase (e.g., "PUT" becomes "put").
* Example:
```javascript
// Change method based on environment setting
let usePatch = ad.environment.get("usePatchForUpdates");
if (ad.request.method.get() === "put" && usePatch) {
ad.request.method.set("PATCH");
}
```
## `ad.response` (Available in Post-response Scripts Only)
Use `ad.response` to access details of the response received from the server. This object is **read-only**.
* **`ad.response.status: number | undefined`**
* The HTTP status code (e.g., `200`, `404`).
* Example:
```javascript
if (ad.response.status === 201) {
ad.console.log("Resource created successfully!");
} else if (ad.response.status >= 400) {
ad.console.error("Request failed with status:", ad.response.status);
}
```
* **`ad.response.body: string | undefined`**
* The response body as a string. For binary responses, this might be decoded text or potentially garbled data depending on the Content-Type and encoding. Use `bodyBytes` for raw data.
* Example:
```javascript
let responseText = ad.response.body;
if (responseText && responseText.includes("error")) {
ad.console.warn("Response body might contain an error message.");
}
```
* **`ad.response.formattedBody: string | undefined`**
* The response body pre-formatted by APIDash (e.g., pretty-printed JSON). Useful for logging structured data clearly.
* Example:
```javascript
ad.console.log("Formatted Response Body:\n", ad.response.formattedBody);
```
* **`ad.response.bodyBytes: number[] | undefined`**
* The raw response body as an array of byte values (integers 0-255). Useful for binary data, but be mindful of potential performance/memory impact for very large responses. Depends on Dart correctly serializing `Uint8List` to `List<int>`.
* Example:
```javascript
let bytes = ad.response.bodyBytes;
if (bytes) {
ad.console.log(`Received ${bytes.length} bytes.`);
// You might perform checks on byte sequences, e.g., magic numbers for file types
// if (bytes[0] === 0x89 && bytes[1] === 0x50 && bytes[2] === 0x4E && bytes[3] === 0x47) {
// ad.console.log("Looks like a PNG file.");
// }
}
```
* **`ad.response.time: number | undefined`**
* The approximate time taken for the request-response cycle, in **milliseconds**.
* Example:
```javascript
let duration = ad.response.time;
if (duration !== undefined) {
ad.console.log(`Request took ${duration.toFixed(2)} ms.`);
if (duration > 1000) {
ad.console.warn("Request took longer than 1 second.");
}
}
```
* **`ad.response.headers: object | undefined`**
* An object containing the response headers. Header names are typically **lowercase** (due to processing by the underlying HTTP client). Values are strings.
* Example:
```javascript
let headers = ad.response.headers;
if (headers) {
ad.console.log("Response Content-Type:", headers['content-type']); // Access using lowercase key
ad.console.log("Response Date Header:", headers.date);
}
```
* **`ad.response.requestHeaders: object | undefined`**
* An object containing the headers that were actually *sent* with the request. Useful for debugging or verification. Header names are typically **lowercase**.
* Example:
```javascript
let sentHeaders = ad.response.requestHeaders;
if (sentHeaders) {
ad.console.log("Sent User-Agent:", sentHeaders['user-agent']);
}
```
* **`ad.response.json(): object | undefined`**
* Attempts to parse the `ad.response.body` as JSON.
* Returns the parsed JavaScript object/array if successful.
* Returns `undefined` if the body is empty or parsing fails (an error message is automatically logged to the APIDash console via `ad.console.error` in case of failure).
* Example:
```javascript
let jsonData = ad.response.json();
if (jsonData && jsonData.data && jsonData.data.token) {
ad.console.log("Found token in response.");
ad.environment.set("sessionToken", jsonData.data.token);
} else if (jsonData) {
ad.console.log("Parsed JSON, but expected structure not found.");
}
```
* **`ad.response.getHeader(key: string): string | undefined`**
* Retrieves a specific response header's value. The lookup is **case-insensitive**.
* Example:
```javascript
// These are equivalent because of case-insensitivity:
let contentType = ad.response.getHeader("Content-Type");
let contentTypeLower = ad.response.getHeader("content-type");
ad.console.log("Response Content-Type (case-insensitive get):", contentType);
let correlationId = ad.response.getHeader("X-Correlation-ID");
if (correlationId) {
ad.environment.set("lastCorrelationId", correlationId);
}
```
## `ad.environment` (Available in Pre & Post-response Scripts)
Use `ad.environment` to get, set, or remove variables within the currently active APIDash environment. Changes made here persist after the script runs and can be used by subsequent requests or other scripts using the same environment.
* **`ad.environment.get(key: string): any`**
* Retrieves the value of the environment variable named `key`. Returns `undefined` if not found.
* Example:
```javascript
let apiUrl = ad.environment.get("baseUrl");
let apiKey = ad.environment.get("apiKey");
ad.console.log("Using API URL:", apiUrl);
```
* **`ad.environment.set(key: string, value: any)`**
* Sets an environment variable named `key` to the given `value`. The `value` should be JSON-serializable (string, number, boolean, object, array, null).
* Example:
```javascript
// In a post-response script after login:
let responseData = ad.response.json();
if (responseData && responseData.access_token) {
ad.environment.set("oauthToken", responseData.access_token);
ad.environment.set("tokenExpiry", Date.now() + (responseData.expires_in * 1000));
ad.console.log("OAuth token saved to environment.");
}
// Store complex object
ad.environment.set("userProfile", { id: 123, name: "Default User"});
```
* **`ad.environment.unset(key: string)`**
* Removes the environment variable named `key`.
* Example:
```javascript
// Clear temporary token after use or logout
ad.environment.unset("sessionToken");
ad.console.log("Session token cleared.");
```
* **`ad.environment.has(key: string): boolean`**
* Checks if an environment variable named `key` exists.
* Example:
```javascript
if (!ad.environment.has("userId")) {
ad.console.warn("Environment variable 'userId' is not set.");
// Maybe set a default or fetch it?
// ad.environment.set("userId", "default-001");
}
```
* **`ad.environment.clear()`**
* Removes *all* variables from the active environment. Use with extreme caution!
* Example:
```javascript
// Usually used for resetting state during testing, careful!
// ad.environment.clear();
// ad.console.warn("Cleared all variables in the active environment!");
```
## `ad.console` (Available in Pre & Post-response Scripts)
Use `ad.console` to log messages to the APIDash console tab for the corresponding request. This is essential for debugging your scripts.
* **`ad.console.log(...args: any[])`**
* Logs informational messages. Accepts multiple arguments, which will be JSON-stringified and displayed in the console.
* Example:
```javascript
ad.console.log("Starting pre-request script...");
let user = ad.environment.get("currentUser");
ad.console.log("Current user from environment:", user);
ad.console.log("Request URL is:", ad.request.url.get(), "Method:", ad.request.method.get());
```
* **`ad.console.warn(...args: any[])`**
* Logs warning messages. Typically displayed differently (e.g., yellow background) in the console.
* Example:
```javascript
if (!ad.environment.has("apiKey")) {
ad.console.warn("API Key environment variable is missing!");
}
let responseTime = ad.response.time;
if (responseTime && responseTime > 2000) {
ad.console.warn("Request took over 2 seconds:", responseTime, "ms");
}
```
* **`ad.console.error(...args: any[])`**
* Logs error messages. Typically displayed prominently (e.g., red background) in the console. Also used internally by methods like `ad.response.json()` on failure.
* Example:
```javascript
if (ad.response.status >= 500) {
ad.console.error("Server error detected!", "Status:", ad.response.status);
ad.console.error("Response Body:", ad.response.body);
}
try {
// Some operation that might fail
let criticalValue = ad.response.json().mustExist;
} catch (e) {
ad.console.error("Script failed to process response:", e.toString(), e.stack);
}
```
### Key Notes:
1. **Pre/Post Script Context**
- Request manipulation only works in pre-request scripts
- Response access only works in post-response scripts
2. **Environment Variables**
- Use `{{VAR_NAME}}` syntax in values for automatic substitution
- Changes to environment variables persist for subsequent requests
3. **Data Types**
- All values are converted to strings when set in headers/params
- Use `ad.request.body.set()` with objects for proper JSON handling
4. **Error Handling**
```javascript
try {
// Potentially error-prone operations
} catch(e) {
ad.console.error('Operation failed:', e.message)
// Optionally re-throw to abort request
throw e
}
```
5. **Best Practices**
- Always check for existence before accessing values
- Use environment variables for sensitive data
- Clean up temporary variables with `ad.environment.unset()`
- Use logging strategically to track script execution
### Example Workflow
### Example 1: Post-request - Extract Data and Check Status
```javascript
// Post-response Script
// Set URL: https://api.apidash.dev/auth/login
// 1. Check response status
if (ad.response.status < 200 || ad.response.status >= 300) {
ad.console.error(`Request failed with status ${ad.response.status}.`);
ad.console.error("Response:", ad.response.body);
// Optional: Could clear a related environment variable on failure
// ad.environment.unset("last_successful_item_id");
return; // Stop processing if status indicates failure
}
ad.console.log(`Request successful with status ${ad.response.status}.`);
ad.console.log(`Took ${ad.response.time} ms.`);
// 2. Try to parse JSON response
const data = ad.response.json();
// 3. Extract and store data if available
if (data && data.access_token) {
ad.environment.set("current_session_id", data.access_token);
ad.console.log("Session ID saved to environment.");
} else {
ad.console.warn("Could not find 'access_token' in the response JSON.");
}
```
### Example 2: Pre-request - Set Auth Header
```javascript
// Pre-request Script
// Set URL : https://api.apidash.dev/profile
// 1. Get Auth token from environment
const token = ad.environment.get("access_token");
if (token) {
ad.request.headers.set("Authorization", `Bearer ${token}`);
ad.console.log("Authorization header set.");
} else {
ad.console.warn("API token not found in environment!");
}
// 2. Try to parse JSON response
const data = ad.response.json();
// 3. Extract and store final User if available
if (data && data.user && data.user.id) {
ad.environment.set("logged_in_user_id", data.user.id);
ad.console.log(`User ID ${data.user.id} saved to environment.`);
}
```

View File

@ -67,10 +67,10 @@ packages:
dependency: transitive
description:
name: async
sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
url: "https://pub.dev"
source: hosted
version: "2.12.0"
version: "2.13.0"
audio_session:
dependency: transitive
description:
@ -402,10 +402,10 @@ packages:
dependency: transitive
description:
name: fake_async
sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc"
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
url: "https://pub.dev"
source: hosted
version: "1.3.2"
version: "1.3.3"
fetch_api:
dependency: transitive
description:
@ -909,10 +909,10 @@ packages:
dependency: transitive
description:
name: leak_tracker
sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec
sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0"
url: "https://pub.dev"
source: hosted
version: "10.0.8"
version: "10.0.9"
leak_tracker_flutter_testing:
dependency: transitive
description:
@ -1840,10 +1840,10 @@ packages:
dependency: transitive
description:
name: vm_service
sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14"
sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02
url: "https://pub.dev"
source: hosted
version: "14.3.1"
version: "15.0.0"
watcher:
dependency: transitive
description:
@ -1880,10 +1880,10 @@ packages:
dependency: transitive
description:
name: webdriver
sha256: "3d773670966f02a646319410766d3b5e1037efb7f07cc68f844d5e06cd4d61c8"
sha256: "2f3a14ca026957870cfd9c635b83507e0e51d8091568e90129fbf805aba7cade"
url: "https://pub.dev"
source: hosted
version: "3.0.4"
version: "3.1.0"
webkit_inspection_protocol:
dependency: transitive
description: