From f034dc5a9e96b1cbd32599c1cfa8147ff7a355e6 Mon Sep 17 00:00:00 2001 From: Pavel Baksy Date: Mon, 18 May 2026 17:37:35 +0200 Subject: [PATCH] Rewrite and condense documentation, fix factual errors Condense most pages to concise reference format. Add Import.md. Fix: history file is history.json not session.json, import uses URL input not file picker, getVariable() returns null not undefined. --- API-Reference.md | 611 ++++------------------------------- Architecture.md | 32 +- Export.md | 147 +-------- FAQ.md | 249 +++----------- Getting-Started.md | 165 ++-------- History.md | 122 +------ Home.md | 2 + Import.md | 62 ++++ Installation.md | 173 ++-------- Projects-and-Environments.md | 139 +------- Scripts.md | 441 +++---------------------- Sensitive-Variables.md | 159 ++------- Variables.md | 216 ++----------- 13 files changed, 346 insertions(+), 2172 deletions(-) create mode 100644 Import.md diff --git a/API-Reference.md b/API-Reference.md index a403538..926d0c9 100644 --- a/API-Reference.md +++ b/API-Reference.md @@ -1,575 +1,86 @@ # API Reference -Complete JavaScript API reference for Roster preprocessing and postprocessing scripts. +JavaScript API available in Roster [[Scripts]]. -## Overview +**Language:** JavaScript (ES5+) via GJS +**Execution:** Synchronous, sandboxed -Roster provides a JavaScript API accessible from [[Scripts]] for automating request modifications and response processing. +## Request Object (preprocessing only) -**Language:** JavaScript (ES5+) -**Runtime:** GJS (GNOME JavaScript) -**Context:** Sandboxed, synchronous execution +| Property | Type | Description | +|----------|------|-------------| +| `request.method` | string | `"GET"`, `"POST"`, `"PUT"`, `"DELETE"` | +| `request.url` | string | Full URL | +| `request.headers` | object | Header key-value pairs | +| `request.body` | string | Request body | -## Global Objects +All properties are writable. -### Preprocessing Script Context +## Response Object (postprocessing only) -Available in preprocessing scripts only: +| Property | Type | Description | +|----------|------|-------------| +| `response.body` | string | Response body | +| `response.headers` | object | Header key-value pairs | +| `response.statusCode` | number | HTTP status code | +| `response.statusText` | string | Status text (`"OK"`, ...) | +| `response.responseTime` | number | Response time in ms | -```javascript -request // Modifiable request object -roster // Roster API (with getVariable) -console // Console output -``` +All properties are read-only. -### Postprocessing Script Context +## Roster API (both contexts) -Available in postprocessing scripts only: +| Method | Available in | Description | +|--------|-------------|-------------| +| `roster.getVariable(name)` | preprocessing | Returns string or `null` | +| `roster.setVariable(name, value)` | both | Creates or updates variable | +| `roster.setVariables({...})` | both | Batch set | +| `roster.project.name` | preprocessing | Current project name | +| `roster.project.environments` | preprocessing | Array of environment names | -```javascript -response // Read-only response object -roster // Roster API (without getVariable) -console // Console output -``` +Variable names must match `/^\w+$/` (alphanumeric + underscore). Values are always strings. Sensitive variables are automatically routed to/from GNOME Keyring. -## Request Object +## Console -**Available in:** Preprocessing scripts only -**Type:** Mutable +`console.log(...)` and `console.error(...)` — output appears in the Preprocessing/Postprocessing results tab. -### Properties +## Available Built-ins -#### `request.method` +Standard JS built-ins: `Date`, `JSON`, `Math`, `String`, `Array`, `Object`, etc. -**Type:** `string` -**Values:** `"GET"`, `"POST"`, `"PUT"`, `"DELETE"` -**Description:** HTTP method for the request +No `fetch`, `setTimeout`, `require`, or file system access. -**Example:** -```javascript -// Change method from GET to POST -request.method = "POST"; -console.log('Method changed to:', request.method); -``` +## Type Notes -#### `request.url` - -**Type:** `string` -**Description:** Complete URL for the request - -**Example:** -```javascript -// Add query parameter -request.url = request.url + '?timestamp=' + Date.now(); - -// Replace placeholder -const userId = roster.getVariable('user_id'); -request.url = request.url.replace('{userId}', userId); - -// Change domain -request.url = request.url.replace('localhost', 'api.example.com'); -``` - -#### `request.headers` - -**Type:** `object` -**Description:** HTTP headers as key-value pairs - -**Example:** -```javascript -// Add header -request.headers['Authorization'] = 'Bearer ' + token; - -// Modify existing header -request.headers['Content-Type'] = 'application/json'; - -// Delete header -delete request.headers['X-Old-Header']; - -// Read header -const contentType = request.headers['Content-Type']; -``` - -#### `request.body` - -**Type:** `string` -**Description:** Request body content - -**Example:** -```javascript -// Set body -request.body = JSON.stringify({ name: "John", age: 30 }); - -// Modify existing body -const data = JSON.parse(request.body); -data.timestamp = new Date().toISOString(); -request.body = JSON.stringify(data); - -// Clear body -request.body = ""; -``` - -## Response Object - -**Available in:** Postprocessing scripts only -**Type:** Immutable (read-only) - -### Properties - -#### `response.body` - -**Type:** `string` -**Description:** Response body content - -**Example:** -```javascript -// Parse JSON response -const data = JSON.parse(response.body); -console.log('User name:', data.name); - -// Check if response contains text -if (response.body.includes('error')) { - console.error('Response contains error'); -} - -// Get response length -console.log('Response size:', response.body.length, 'bytes'); -``` - -#### `response.headers` - -**Type:** `object` -**Description:** Response headers as key-value pairs (read-only) - -**Example:** -```javascript -// Read header -const contentType = response.headers['Content-Type']; -console.log('Content type:', contentType); - -// Check if header exists -if (response.headers['X-Rate-Limit-Remaining']) { - const remaining = response.headers['X-Rate-Limit-Remaining']; - console.log('Rate limit remaining:', remaining); -} - -// List all headers -for (const key in response.headers) { - console.log(key + ':', response.headers[key]); -} -``` - -#### `response.statusCode` - -**Type:** `number` -**Description:** HTTP status code (200, 404, 500, etc.) - -**Example:** -```javascript -// Check if successful -if (response.statusCode === 200) { - console.log('Success!'); -} else if (response.statusCode >= 400 && response.statusCode < 500) { - console.error('Client error:', response.statusCode); -} else if (response.statusCode >= 500) { - console.error('Server error:', response.statusCode); -} - -// Handle specific status codes -switch (response.statusCode) { - case 200: - console.log('OK'); - break; - case 201: - console.log('Created'); - break; - case 401: - console.error('Unauthorized'); - break; - case 404: - console.error('Not found'); - break; - default: - console.log('Status:', response.statusCode); -} -``` - -#### `response.statusText` - -**Type:** `string` -**Description:** HTTP status text ("OK", "Not Found", etc.) - -**Example:** -```javascript -console.log('Response:', response.statusCode, response.statusText); -// Output: "Response: 200 OK" - -if (response.statusText === "OK") { - // Process successful response -} -``` - -#### `response.responseTime` - -**Type:** `number` -**Description:** Response time in milliseconds - -**Example:** -```javascript -console.log('Response took', response.responseTime, 'ms'); - -if (response.responseTime > 1000) { - console.warn('Slow response:', response.responseTime, 'ms'); -} - -// Store for monitoring -roster.setVariable('last_response_time', response.responseTime.toString()); -``` - -## Roster API - -**Available in:** Both preprocessing and postprocessing scripts -**Namespace:** `roster` - -### Methods - -#### `roster.getVariable(name)` - -**Available in:** Preprocessing scripts only -**Returns:** `string` or `undefined` -**Description:** Get variable value from selected environment - -**Parameters:** -- `name` (string): Variable name - -**Example:** -```javascript -// Get variable -const apiKey = roster.getVariable('api_key'); -if (apiKey) { - request.headers['X-API-Key'] = apiKey; -} else { - console.error('API key not defined'); -} - -// Get with default value -const timeout = roster.getVariable('timeout') || '30'; -console.log('Using timeout:', timeout); -``` - -**Notes:** -- Returns `undefined` if variable doesn't exist -- Gets value from selected environment -- Sensitive variables are automatically decrypted - -#### `roster.setVariable(name, value)` - -**Available in:** Both preprocessing and postprocessing scripts -**Returns:** `undefined` -**Description:** Set or update variable in selected environment - -**Parameters:** -- `name` (string): Variable name (must match `/^\w+$/`) -- `value` (string): Variable value - -**Example:** -```javascript -// Set variable -roster.setVariable('user_id', '12345'); -console.log('Saved user ID'); - -// Update existing variable -const token = data.access_token; -roster.setVariable('auth_token', token); - -// Create new variable (auto-created if doesn't exist) -roster.setVariable('session_id', generateSessionId()); -``` - -**Notes:** -- Creates variable if it doesn't exist -- Updates value in selected environment -- Variable name must be alphanumeric + underscore -- Sensitive variables automatically routed to keyring -- Value converted to string - -#### `roster.setVariables(object)` - -**Available in:** Both preprocessing and postprocessing scripts -**Returns:** `undefined` -**Description:** Set or update multiple variables at once (batch operation) - -**Parameters:** -- `object` (object): Key-value pairs of variable names and values - -**Example:** -```javascript -// Set multiple variables -roster.setVariables({ - user_id: data.user.id, - user_name: data.user.name, - user_email: data.user.email, - session_id: data.session_id -}); - -// Extract response data -const data = JSON.parse(response.body); -roster.setVariables({ - access_token: data.access_token, - refresh_token: data.refresh_token, - expires_at: data.expires_at -}); -console.log('Saved authentication tokens'); -``` - -**Notes:** -- More efficient than multiple `setVariable()` calls -- Same validation rules as `setVariable()` - -### Properties - -#### `roster.project.name` - -**Available in:** Preprocessing scripts only -**Type:** `string` -**Description:** Current project name - -**Example:** -```javascript -const projectName = roster.project.name; -console.log('Project:', projectName); - -// Add project name to request -request.headers['X-Project'] = projectName; -``` - -#### `roster.project.environments` - -**Available in:** Preprocessing scripts only -**Type:** `string[]` (array of strings) -**Description:** Array of environment names in current project - -**Example:** -```javascript -const envs = roster.project.environments; -console.log('Available environments:', envs.join(', ')); -// Output: "Available environments: Production, Staging, Development" - -// Check if environment exists -if (envs.includes('Production')) { - console.log('Production environment available'); -} - -// Log count -console.log('Environment count:', envs.length); -``` - -## Console API - -**Available in:** Both preprocessing and postprocessing scripts -**Namespace:** `console` - -### Methods - -#### `console.log(...args)` - -**Returns:** `undefined` -**Description:** Print message to script output - -**Example:** -```javascript -console.log('Simple message'); -console.log('User ID:', userId); -console.log('Request sent to', request.url); -console.log('Response status:', response.statusCode, response.statusText); - -// Multiple arguments -const name = 'John'; -const age = 30; -console.log('User:', name, 'Age:', age); -``` - -**Output location:** -- Preprocessing: "Preprocessing" tab in request panel -- Postprocessing: "Postprocessing" tab in request panel - -#### `console.error(...args)` - -**Returns:** `undefined` -**Description:** Print error message to script output - -**Example:** -```javascript -if (response.statusCode !== 200) { - console.error('Request failed with status:', response.statusCode); -} - -if (!data.access_token) { - console.error('No access token in response'); -} - -try { - const data = JSON.parse(response.body); -} catch (e) { - console.error('JSON parse error:', e.message); -} -``` - -**Visual difference:** -- May be styled differently than `console.log()` in output -- Indicates errors or warnings - -## Built-in JavaScript Objects - -Standard JavaScript objects available in GJS: - -### Date - -```javascript -// Current timestamp -const now = new Date(); -console.log('Current time:', now.toISOString()); - -// Unix timestamp -const timestamp = Date.now(); -console.log('Timestamp:', timestamp); - -// Date arithmetic -const expiresAt = new Date(Date.now() + 3600000); // +1 hour -roster.setVariable('token_expires', expiresAt.toISOString()); -``` - -### JSON - -```javascript -// Parse JSON -const data = JSON.parse(response.body); -console.log('Parsed data:', data.name); - -// Stringify JSON -const body = { name: "John", age: 30 }; -request.body = JSON.stringify(body); - -// Pretty print -request.body = JSON.stringify(body, null, 2); -``` - -### Math - -```javascript -// Random number -const requestId = Math.floor(Math.random() * 1000000); -roster.setVariable('request_id', requestId.toString()); - -// Rounding -const responseTimeSeconds = Math.round(response.responseTime / 1000); -console.log('Response time:', responseTimeSeconds, 'seconds'); -``` - -### String - -```javascript -// String manipulation -const url = request.url.replace('http://', 'https://'); -const uppercase = data.name.toUpperCase(); -const trimmed = data.description.trim(); - -// String methods -if (response.body.includes('error')) { - console.error('Response contains error'); -} - -const parts = request.url.split('/'); -const lastPart = parts[parts.length - 1]; -``` - -### Array - -```javascript -// Array methods -const data = JSON.parse(response.body); -const itemIds = data.items.map(item => item.id); -console.log('Item IDs:', itemIds.join(', ')); - -// Filter -const active = data.items.filter(item => item.active); -console.log('Active items:', active.length); - -// Find -const firstItem = data.items.find(item => item.id === '123'); -if (firstItem) { - console.log('Found item:', firstItem.name); -} -``` - -### Object - -```javascript -// Object methods -const headers = Object.keys(response.headers); -console.log('Header count:', headers.length); - -// Merge objects -const defaults = { timeout: 30, retries: 3 }; -const config = Object.assign({}, defaults, userConfig); - -// Check property -if (data.hasOwnProperty('access_token')) { - roster.setVariable('auth_token', data.access_token); -} -``` - -## Error Handling - -Scripts should handle errors gracefully: - -```javascript -// Try-catch for parsing -try { - const data = JSON.parse(response.body); - roster.setVariable('user_id', data.user.id); -} catch (e) { - console.error('Failed to parse JSON:', e.message); - console.error('Response body:', response.body); -} - -// Check before accessing -if (data && data.user && data.user.id) { - roster.setVariable('user_id', data.user.id); -} else { - console.error('Invalid response structure'); -} - -// Validate response status -if (response.statusCode === 200) { - const data = JSON.parse(response.body); - // Process data -} else { - console.error('Request failed:', response.statusCode); -} -``` - -## Limitations - -Scripts run in a sandboxed, synchronous environment: -- No async operations (setTimeout, Promises, async/await) -- No external libraries or npm packages -- No file system or shell access -- No network requests (use Roster's request system) -- Only built-in JavaScript objects available - -## Type Reference - -**Variable names** must match `/^\w+$/` (alphanumeric + underscore only). - -**Variables are always strings**. Convert numbers and booleans when setting/getting: +Variables are always strings. Convert when needed: ```javascript roster.setVariable('count', data.count.toString()); const count = parseInt(roster.getVariable('count'), 10); ``` -## Next Steps +## Examples -- [[Scripts]] - Practical examples and workflows -- [[Variables]] - Learn about environment variables -- [[Sensitive-Variables]] - Secure credential storage +### Authentication flow + +**POST /auth/login — postprocessing:** +```javascript +const data = JSON.parse(response.body); +roster.setVariables({ + access_token: data.access_token, + refresh_token: data.refresh_token, + user_id: data.user_id +}); +``` + +**Authenticated request — preprocessing:** +```javascript +const token = roster.getVariable('access_token'); +request.headers['Authorization'] = 'Bearer ' + token; +``` + +### Dynamic headers + +```javascript +request.headers['X-Request-ID'] = Math.random().toString(36).slice(2); +request.headers['X-Timestamp'] = new Date().toISOString(); +``` diff --git a/Architecture.md b/Architecture.md index 8038dc9..affafb9 100644 --- a/Architecture.md +++ b/Architecture.md @@ -89,14 +89,14 @@ Postprocessing Script → Display Response → Save to History ``` ~/.local/share/cz.bugsy.roster/ ├── requests.json # Projects, environments, regular variables -└── session.json # History, window state +└── history.json # Request history ``` **Flatpak:** ``` ~/.var/app/cz.bugsy.roster/data/cz.bugsy.roster/ ├── requests.json -└── session.json +└── history.json ``` ### Data Format @@ -104,18 +104,23 @@ Postprocessing Script → Display Response → Save to History **requests.json:** ```json { + "version": 1, "projects": [ { "id": "uuid", "name": "Project Name", "icon": "icon-name", + "created_at": "2025-01-01T00:00:00+00:00", + "variable_names": ["base_url", "api_key"], + "sensitive_variables": ["api_key"], "environments": [ { "id": "uuid", "name": "Production", + "created_at": "2025-01-01T00:00:00+00:00", "variables": { "base_url": "https://api.example.com", - "api_key": "" // Empty if sensitive + "api_key": "" } } ], @@ -123,12 +128,19 @@ Postprocessing Script → Display Response → Save to History { "id": "uuid", "name": "Get Users", - "method": "GET", - "url": "{{base_url}}/users", - "headers": {}, - "body": "", - "preprocessing_script": "", - "postprocessing_script": "" + "created_at": "2025-01-01T00:00:00+00:00", + "modified_at": "2025-01-01T00:00:00+00:00", + "request": { + "method": "GET", + "url": "{{base_url}}/users", + "headers": {}, + "body": "", + "syntax": "RAW" + }, + "scripts": { + "preprocessing": "", + "postprocessing": "" + } } ] } @@ -136,6 +148,8 @@ Postprocessing Script → Display Response → Save to History } ``` +Note: sensitive variable values are stored in GNOME Keyring (empty string in JSON is a placeholder). + ### Sensitive Variables Stored in GNOME Keyring with schema: diff --git a/Export.md b/Export.md index 1b85b02..35edf92 100644 --- a/Export.md +++ b/Export.md @@ -1,12 +1,8 @@ # Export -Roster can export requests to other formats for use with command-line tools or other HTTP clients. +Click the **Export** button (toolbar) to copy the current request as a cURL command to clipboard. -## Supported Formats - -### cURL - -Export requests as cURL commands for use in terminal or scripts. +Variables are substituted with values from the selected environment before export. **Example output:** ```bash @@ -16,141 +12,4 @@ curl -X POST 'https://api.example.com/users' \ -d '{"name":"John","email":"john@example.com"}' ``` -### Other Formats - -Additional export formats (HTTPie, wget, etc.) may be added in future releases. - -## How to Export - -### Export Current Request - -1. Configure your request in the request editor -2. Click the **Export** button in the toolbar -3. Select export format (e.g., "cURL") -4. Exported command is copied to clipboard - -### Export from History - -1. Open a request from [[History]] -2. Click the **Export** button -3. Select format -4. Command copied to clipboard - -### Export Saved Requests - -1. Open a saved request from project -2. Click the **Export** button -3. Select format -4. Command copied to clipboard - -## Variable Substitution - -When exporting requests that contain variables: - -**Variables are substituted with current environment values:** -- `{{base_url}}` replaced with actual URL -- `{{api_key}}` replaced with actual key value -- All `{{variable}}` placeholders resolved - -The exported command contains the final values, not the placeholder syntax. - -### Sensitive Variables - -[[Sensitive-Variables]] are exported with their actual decrypted values. Be careful when sharing exported commands containing sensitive data. - -**Security note:** Exported cURL commands may contain: -- API keys in headers -- Passwords in request body -- Tokens in authorization headers - -Review exported commands before sharing. - -## Using Exported Commands - -### cURL - -Paste the exported cURL command in your terminal: - -```bash -curl -X GET 'https://api.example.com/users' \ - -H 'Authorization: Bearer token123' -``` - -### Scripts - -Use exported commands in shell scripts: - -```bash -#!/bin/bash -# exported_request.sh - -curl -X POST 'https://api.example.com/users' \ - -H 'Content-Type: application/json' \ - -d '{"name":"John"}' -``` - -### Documentation - -Include exported commands in API documentation or tutorials. - -## Export Features - -### Headers - -All request headers are included in export: -- Custom headers -- Content-Type -- Authorization -- User-Agent -- etc. - -### Request Body - -Request body is included with appropriate flags: -- `-d` for POST data -- Properly escaped JSON -- Multiline bodies formatted correctly - -### Method - -HTTP method included with `-X` flag: -- `-X GET` -- `-X POST` -- `-X PUT` -- `-X DELETE` - -## Limitations - -- Only cURL format currently supported -- No batch export of multiple requests -- Exported to clipboard only (no file export) -- Scripts (preprocessing/postprocessing) are not exported - -## Tips - -**Test exported commands:** -Always test exported cURL commands in a safe environment before using in production. - -**Redact sensitive data:** -Remove or replace sensitive values before sharing exported commands: -```bash -# Original export -curl -H 'Authorization: Bearer abc123xyz' - -# Redacted for sharing -curl -H 'Authorization: Bearer YOUR_TOKEN_HERE' -``` - -**Save to file:** -Redirect clipboard content to file: -```bash -# Paste from clipboard and save -pbpaste > request.sh # macOS -xclip -o > request.sh # Linux with xclip -``` - -## Next Steps - -- [[Getting-Started]] - Learn to create requests -- [[Variables]] - Use variables in requests -- [[History]] - Review past requests +Sensitive variable values are included in plain text — review before sharing. diff --git a/FAQ.md b/FAQ.md index 03129ae..7f64e14 100644 --- a/FAQ.md +++ b/FAQ.md @@ -2,243 +2,66 @@ ## General -### What is Roster? +**What is Roster?** +A native GNOME HTTP client for testing APIs, built with GTK 4 and libadwaita. -Roster is a native GNOME HTTP client for testing and debugging APIs. It provides a clean interface for sending HTTP requests, viewing responses, and automating workflows. +**What platforms does it support?** +Linux with GNOME 40+. Not available for Windows or macOS. -### Is Roster free? - -Yes! Roster is free and open-source software licensed under GPL-3.0-or-later. - -### What platforms does Roster support? - -Roster is designed for the GNOME desktop environment on Linux. It works on any Linux distribution with GNOME 40+. - -### Can I use Roster on Windows or macOS? - -Roster is specifically built for GNOME/Linux and is not currently available for Windows or macOS. +**Is it free?** +Yes, GPL-3.0-or-later. ## Installation -### How do I install Roster? +**How do I install Roster?** +From Flathub (recommended): `flatpak install flathub cz.bugsy.roster` +Or see [[Installation]] to build from source. -See [[Installation]] for complete instructions. You can: -- Build from source -- Install via Flatpak -- Use GNOME Builder +**Is Roster on Flathub?** +Yes. Install with `flatpak install flathub cz.bugsy.roster` or find it in GNOME Software. -### Will Roster be on Flathub? +## Variables -Support for Flathub is planned for future releases. +**What's the difference between regular and sensitive variables?** +Regular variables are stored in plain-text JSON. Sensitive variables are encrypted in GNOME Keyring. Use sensitive variables for API keys, tokens, and passwords — see [[Sensitive-Variables]]. -### What dependencies does Roster need? +**Can I use variables in the request body?** +Yes — `{{variable_name}}` works in URL, headers, and body. -GTK 4, libadwaita 1, Python 3, libsoup3, libsecret, and GJS. See [[Installation]] for details. - -## Variables and Environments - -### What's the difference between regular and sensitive variables? - -**Regular variables** are stored in plain text JSON files. Use for non-sensitive data like URLs. - -**Sensitive variables** are encrypted and stored in GNOME Keyring. Use for API keys, passwords, and tokens. - -See [[Sensitive-Variables]] for details. - -### Can I use variables in the request body? - -Yes! Use `{{variable_name}}` syntax in: -- URL -- Headers -- Request body - -### How do I switch between environments? - -Use the environment dropdown at the top of the request panel. Switching environments updates all variable values. - -### Can variables from one project be used in another? - -No. Variables are scoped to projects and cannot be shared between projects. +**Can variables be shared between projects?** +No, variables are scoped to their project. ## Security -### Is it safe to store API keys in Roster? +**Is it safe to store API keys?** +Yes, if you mark them as sensitive (lock icon). Regular variables are plain text. -**If you use sensitive variables: Yes.** - -Mark variables as sensitive (click the lock icon), and values are encrypted in GNOME Keyring. - -**If you use regular variables: No.** - -Regular variables are stored in plain text. Always mark secrets as sensitive! - -### Where are my secrets stored? - -Sensitive variables are encrypted in GNOME Keyring (`~/.local/share/keyrings/`) using your login password. Same security as browser passwords and WiFi credentials. - -### Can I view my keyring secrets? - -Yes! Open "Passwords and Keys" application (Seahorse), navigate to "Login" keyring, and look for entries starting with "Roster:". - -### What happens if I forget to mark a variable as sensitive? - -You can click the lock icon at any time to mark it as sensitive. Values will be moved from JSON to keyring automatically. +**Where are secrets stored?** +In GNOME Keyring (`~/.local/share/keyrings/`), same as browser passwords and WiFi credentials. Visible in Seahorse under "Login" keyring. ## Scripts -### What language do scripts use? +**What language?** +JavaScript (ES5+) via GJS. -JavaScript (ES5+), executed via GJS (GNOME JavaScript runtime). - -### Can I use npm packages in scripts? - -No. Scripts run in a sandboxed environment without access to external libraries or npm packages. - -### Can scripts access the file system? - -No. Scripts cannot read/write files, execute shell commands, or access system resources. - -### Why can't I use async/await? - -Scripts execute synchronously. No async operations (setTimeout, Promises, async/await, fetch) are available. - -### Can I make HTTP requests from scripts? - -No. Use Roster's request system and chain requests using variables instead. - -See [[Scripts]] for complete documentation. - -## Requests and Responses - -### Where is request history stored? - -**Native:** `~/.local/share/cz.bugsy.roster/session.json` -**Flatpak:** `~/.var/app/cz.bugsy.roster/data/cz.bugsy.roster/session.json` - -### How long is history kept? - -History persists across sessions until you clear it manually. - -### Can I export requests? - -Yes! Click the export button and choose a format: -- cURL -- More formats may be added in future versions - -### Does Roster support file uploads? - -Multipart form uploads are not currently supported. Planned for future releases. - -### Can I test WebSocket connections? - -WebSocket support is not currently available. +**Can I use npm packages / async / file system?** +No — scripts are sandboxed and synchronous. See [[Scripts]] for full limitations. ## Troubleshooting -### Roster won't start +**Roster won't start** +Check dependencies: `pkg-config --modversion gtk4 libadwaita-1` -**Check dependencies:** -```bash -# Verify GTK 4 and libadwaita are installed -pkg-config --modversion gtk4 libadwaita-1 -``` +**Keyring not accessible** +Ensure GNOME Keyring is unlocked. For Flatpak, verify `--talk-name=org.freedesktop.secrets` permission. -**Check logs:** -```bash -# Native -roster +**Variables not substituting** +Check: environment is selected, variable name matches exactly (case-sensitive), syntax is `{{name}}`. -# Flatpak -flatpak run cz.bugsy.roster -``` - -### Cannot access GNOME Keyring - -**Ensure keyring is unlocked:** -1. Open "Passwords and Keys" (Seahorse) -2. Right-click "Login" keyring -3. Select "Unlock" - -**For Flatpak, check permissions:** -```bash -flatpak info --show-permissions cz.bugsy.roster -# Should include: --talk-name=org.freedesktop.secrets -``` - -### Variables not substituting - -**Check:** -1. Environment is selected (dropdown at top of request) -2. Variable names match exactly (case-sensitive) -3. Syntax is correct: `{{variable_name}}` -4. Variable is defined in selected environment - -### Scripts not executing - -**Check:** -1. Script has no syntax errors -2. Check script output panels for errors -3. GJS (gjs package) is installed - -### Request failing with SSL errors - -**For self-signed certificates:** - -Roster uses system SSL certificates. Add your certificate to the system trust store or use development environments without SSL validation. - -## Data and Privacy - -### Does Roster collect any data? - -No. Roster does not collect, transmit, or share any data. All information stays on your local machine. - -### Can I backup my projects? - -Yes! Backup these files: -- `~/.local/share/cz.bugsy.roster/requests.json` (projects and variables) -- `~/.local/share/keyrings/` (sensitive variables) - -### How do I reset Roster? - -Delete the data directory: -```bash -# Native -rm -rf ~/.local/share/cz.bugsy.roster/ - -# Flatpak -rm -rf ~/.var/app/cz.bugsy.roster/data/cz.bugsy.roster/ -``` - -**Warning:** This deletes all projects, requests, and history! - -Sensitive variables in keyring must be deleted separately via Seahorse. +**How do I reset Roster?** +Delete `~/.local/share/cz.bugsy.roster/` (native) or `~/.var/app/cz.bugsy.roster/data/cz.bugsy.roster/` (Flatpak). Keyring entries must be removed separately via Seahorse. ## Contributing -### How can I contribute? - -Contributions welcome! See [[Contributing]] for guidelines. - -### Where do I report bugs? - -Report issues at: https://git.bugsy.cz/beval/roster/issues - -### Can I request features? - -Yes! Open an issue with the "enhancement" label. - -## Comparison - -### How is Roster different from Postman/Insomnia? - -Roster is a lightweight, native GNOME application focused on simplicity. Unlike Postman/Insomnia (Electron-based with cloud features), Roster is Linux-only with no cloud dependencies. - -### How is Roster different from HTTPie? - -Roster provides a GUI with project management, while HTTPie is a command-line tool. - -## Still Have Questions? - -- Check the [[Home|Wiki]] for more documentation -- Open an issue: https://git.bugsy.cz/beval/roster/issues -- Read the source: https://git.bugsy.cz/beval/roster +Report bugs and request features: https://git.bugsy.cz/beval/roster/issues +See [[Contributing]] for contribution guidelines. diff --git a/Getting-Started.md b/Getting-Started.md index b3dc3df..f47abe7 100644 --- a/Getting-Started.md +++ b/Getting-Started.md @@ -1,156 +1,35 @@ # Getting Started -This guide walks you through sending your first HTTP request with Roster. +After [[Installation]], launch Roster from the application menu or run `roster` (Flatpak: `flatpak run cz.bugsy.roster`). -## Launch Roster +## Sending a Request -After [[Installation]], launch Roster from: -- Your application menu (search for "Roster") -- Command line: `roster` (or `flatpak run cz.bugsy.roster` for Flatpak) +1. The app opens with a new request tab +2. Set the URL (e.g. `https://api.github.com/users/octocat`) and method (`GET`) +3. Optionally add headers in the **Headers** tab +4. Press **Ctrl+Return** (or click **Send**) +5. The response appears in the right panel — status, headers, body with syntax highlighting -## Your First Request +## Saving a Request -### Step 1: Configure the Request +Click **Save** (or **Ctrl+S**), enter a name, and select a project. Unsaved changes show a dot (•) on the tab label. -Roster starts with a new request tab. +## History -**Set the URL:** -``` -https://api.github.com/users/octocat -``` +Every sent request is automatically saved to history (bottom panel). Click any entry to open it in a new tab. -**Set the Method:** -- Use the dropdown menu (default is `GET`) +## Tabs -**Add Headers (Optional):** -1. Click the **"Headers"** tab -2. Add a header: - - Key: `User-Agent` - - Value: `Roster/0.6.0` - -### Step 2: Send the Request - -Click the **"Send"** button (or press **Ctrl+Return**) - -### Step 3: View the Response - -The response appears in the right panel: - -**Status Line:** -``` -200 OK 123 ms 1.2 KB -``` - -**Response Headers:** -Click the **"Headers"** tab to view all response headers. - -**Response Body:** -The JSON response from GitHub API showing user information. - -## Saving Requests - -### Save for Later - -1. Click the **"Save"** button in the header -2. Select project -2. Enter a name: `Get GitHub User` -3. Click **"Save"** - -The request is now saved to your project. - -### Access Saved Requests - -1. Click the project -2. Your project shows saved requests -3. Click a request to open it in a new tab - -## Request History - -Every request you send is automatically saved to history. - -**View History:** -1. Navigate to bottom history panel -2. Browse past requests -3. Click any entry to open it in a new tab - -## Working with Tabs - -### Multiple Tabs - -Open multiple requests simultaneously: -- **Ctrl+T** - New request tab -- **Ctrl+W** - Close current tab -- **Ctrl+Tab** / **Ctrl+Shift+Tab** - Switch between tabs - -### Modified Indicator - -Unsaved changes are marked with a dot (•) on the tab label. - -## Common Request Types - -### GET Request -``` -Method: GET -URL: https://api.example.com/users -Headers: (optional) -Body: (none) -``` - -### POST Request -``` -Method: POST -URL: https://api.example.com/users -Headers: Content-Type: application/json -Body: {"name": "John", "email": "john@example.com"} -``` - -### PUT Request -``` -Method: PUT -URL: https://api.example.com/users/123 -Headers: Content-Type: application/json -Body: {"name": "John Updated"} -``` - -### DELETE Request -``` -Method: DELETE -URL: https://api.example.com/users/123 -Headers: (optional) -Body: (none) -``` - -## Response Features - -### Syntax Highlighting - -Roster automatically detects and highlights JSON, XML, and HTML responses. - -### Response Metrics - -The status line shows status code, response time, and total size including headers: -``` -200 OK 123 ms 5.4 KB -``` - -## Keyboard Shortcuts - -Essential shortcuts to speed up your workflow: - -See [[Keyboard-Shortcuts]] for the complete list. +| Action | Shortcut | +|--------|----------| +| New tab | **Ctrl+T** | +| Close tab | **Ctrl+W** | +| Switch tabs | **Ctrl+Tab** / **Ctrl+Shift+Tab** | ## Next Steps -Now that you know the basics, explore more advanced features: - -- [[Projects-and-Environments]] - Organize requests and manage environments -- [[Variables]] - Use variables to avoid repetition -- [[Sensitive-Variables]] - Store API keys securely -- [[Scripts]] - Automate workflows with JavaScript -- [[Export]] - Export requests to cURL and other formats - -## Tips - -- Use `httpbin.org` endpoints for testing (e.g., `https://httpbin.org/get`) -- Save frequently used requests with descriptive names -- Check history panel to review and compare past responses +- [[Projects-and-Environments]] — organize requests +- [[Variables]] — avoid repeating URLs and headers +- [[Sensitive-Variables]] — store API keys securely +- [[Scripts]] — automate with JavaScript +- [[Import]] — import from OpenAPI/Swagger or WSDL diff --git a/History.md b/History.md index 151b7cd..75680ac 100644 --- a/History.md +++ b/History.md @@ -1,124 +1,22 @@ # History -Roster automatically saves every HTTP request you send to history, allowing you to review and replay past requests. +Every request you send is automatically saved to history and persists across sessions. -## What is History? +## Accessing History -History captures complete request and response data for every request sent: -- Request method, URL, headers, body -- Response status, headers, body, timing -- Timestamp of when request was sent -- Environment used (if applicable) +History appears in the **bottom panel** of the main window. Each entry shows method, URL, status code, timestamp, and response time, sorted newest first. -History persists across sessions and survives application restarts. +Click any entry to open it in a new tab — you can modify and resend it. -## Viewing History +## Storage -### Access History Panel - -History appears in the bottom panel of the main window. - -### History Entries - -Each entry shows: -- Request method and URL -- Response status code -- Timestamp (relative or absolute) -- Response time - -Entries are sorted by timestamp (most recent first). - -### Open from History - -Click any history entry to open it in a new tab with: -- Complete request details -- Full response -- All headers - -This creates a new editable request tab - you can modify and resend. - -## Replaying Requests - -To replay a request from history: - -1. Click the history entry -2. Request opens in new tab with original configuration -3. Click **"Send"** to execute again - -The replayed request: -- Uses current environment variables (not original values) -- Can be modified before sending -- Creates a new history entry when sent - -## Using History - -### Compare Responses - -Open multiple history entries in tabs to compare responses across time or different environments. - -### Debug Issues - -Review past requests to: -- See what was actually sent -- Check response timing trends -- Verify header values -- Examine error responses - -### Create New Requests - -Use history entries as templates: -1. Open history entry -2. Modify URL, headers, or body -3. Save as new request - -## History Storage - -**Native:** -``` -~/.local/share/cz.bugsy.roster/session.json -``` - -**Flatpak:** -``` -~/.var/app/cz.bugsy.roster/data/cz.bugsy.roster/session.json -``` - -History is stored in JSON format with full request/response data. +- Native: `~/.local/share/cz.bugsy.roster/history.json` +- Flatpak: `~/.var/app/cz.bugsy.roster/data/cz.bugsy.roster/history.json` ## Clearing History -Currently, history can be cleared by deleting the `session.json` file: +No built-in UI — delete `history.json` and restart the app. -```bash -# Native -rm ~/.local/share/cz.bugsy.roster/session.json +## Privacy -# Flatpak -rm ~/.var/app/cz.bugsy.roster/data/cz.bugsy.roster/session.json -``` - -Then restart Roster. - -## Privacy Note - -History contains complete request and response data, including: -- URLs and query parameters -- Request/response headers -- Request/response bodies - -If requests contain sensitive data: -- Use [[Sensitive-Variables]] for credentials -- Be aware history file contains plaintext data -- Clear history periodically if needed - -## Limitations - -- History is stored locally only (no cloud sync) -- No built-in UI to clear history (must delete file manually) -- No search/filter functionality (planned for future releases) -- No export functionality for history entries - -## Next Steps - -- [[Getting-Started]] - Learn to send requests -- [[Export]] - Export requests to other tools +History contains full request and response data in plaintext, including headers and bodies. Clear it periodically if requests contain sensitive data. diff --git a/Home.md b/Home.md index 55b1f69..1f954b2 100644 --- a/Home.md +++ b/Home.md @@ -11,6 +11,7 @@ Roster is a native GNOME application for testing and debugging HTTP APIs with su - Secure credential storage using GNOME Keyring - JavaScript preprocessing and postprocessing scripts - Request history and export to cURL +- Import from OpenAPI/Swagger (2.0 + 3.x) and WSDL ## Quick Links @@ -25,6 +26,7 @@ Roster is a native GNOME application for testing and debugging HTTP APIs with su - [[Scripts]] - Preprocessing and postprocessing automation - [[Export]] - Export requests to other tools - [[History]] - Track and replay requests +- [[Import]] - Import from OpenAPI/Swagger and WSDL ### Reference - [[API-Reference]] - Complete JavaScript API documentation diff --git a/Import.md b/Import.md new file mode 100644 index 0000000..743a765 --- /dev/null +++ b/Import.md @@ -0,0 +1,62 @@ +# Import + +Roster can import API definitions from OpenAPI/Swagger and WSDL endpoints, automatically creating saved requests for all discovered operations. + +## Supported Formats + +### OpenAPI / Swagger + +- **OpenAPI 2.0** (Swagger) — JSON or YAML +- **OpenAPI 3.x** — JSON or YAML + +Imports all defined API operations as separate saved requests, including: +- HTTP method and path +- Base URL from `host`/`servers` field +- Request headers (`Content-Type`, `Accept`) +- JSON body template generated from the request schema + +### WSDL (Web Services Description Language) + +- **WSDL 1.1** +- **WSDL 2.0** + +Imports all defined operations as SOAP requests, including: +- Endpoint URL from the WSDL binding +- `Content-Type: text/xml` header +- XML body template generated from the input message schema + +## How to Import + +### OpenAPI / Swagger + +1. Click the **Import** button (papyrus icon) in the sidebar toolbar +2. Select **"Import from OpenAPI / Swagger"** +3. Enter the **Spec URL** of the OpenAPI/Swagger definition (must start with `http://` or `https://`) +4. Click **Fetch** — Roster downloads and parses the spec +5. Select the operations you want to import (or select all) +6. Choose or create a **project** to import into +7. Click **"Import"** + +### WSDL + +1. Click the **Import** button (papyrus icon) in the sidebar toolbar +2. Select **"Import from WSDL"** +3. Enter the **WSDL URL** of the service description (WSDL 1.1 or 2.0) +4. Click **Fetch WSDL** — Roster downloads and parses the service description +5. Select the operations you want to import (or select all) +6. Choose or create a **project** to import into +7. Click **"Import"** + +The imported requests are saved to the selected project and ready to send. + +## Tips + +- Imported requests use placeholder values from the schema (e.g., empty strings for required fields). Fill in actual values before sending. +- Variable substitution works in imported requests — add `{{variable_name}}` to the URL or body after importing. +- For OpenAPI imports with `{{server}}` or similar base URL variables, set up an environment with the actual server URL. + +## Next Steps + +- [[Projects-and-Environments]] - Organize imported requests into projects +- [[Variables]] - Add environment variables to imported requests +- [[Getting-Started]] - Learn how to send requests diff --git a/Installation.md b/Installation.md index 2fad411..8523761 100644 --- a/Installation.md +++ b/Installation.md @@ -1,34 +1,19 @@ # Installation -This guide covers how to build and install Roster from source. +## Flathub (recommended) -## Dependencies +```bash +flatpak install flathub cz.bugsy.roster +``` -Roster requires the following dependencies: +Or search for "Roster" in GNOME Software. -### Runtime Dependencies -- **GTK 4** (>= 4.0) -- **libadwaita 1** (>= 1.0) -- **Python 3** (>= 3.8) -- **libsoup3** - HTTP client library (provided by GNOME Platform) -- **libsecret** - Secure credential storage (provided by GNOME Platform) -- **GJS** - GNOME JavaScript runtime for script execution +## Build from Source -### Build Dependencies -- **Meson** (>= 1.0.0) -- **Ninja** -- **pkg-config** -- **gettext** - For internationalization -- **glib-compile-schemas** -- **glib-compile-resources** +**Dependencies:** -## Installation Methods - -### Method 1: Build from Source (Native) - -This method builds and installs Roster directly on your system. - -#### Step 1: Install Dependencies +- Meson >= 1.0, Ninja, pkg-config, gettext +- GTK 4, libadwaita 1, libsoup3, libsecret, GJS, PyGObject **Fedora:** ```bash @@ -40,152 +25,44 @@ sudo dnf install meson ninja-build gtk4-devel libadwaita-devel libsoup3-devel li sudo apt install meson ninja-build libgtk-4-dev libadwaita-1-dev libsoup-3.0-dev libsecret-1-dev gjs python3 gettext desktop-file-utils appstream ``` -**Arch Linux:** +**Arch:** ```bash -sudo pacman -S meson ninja gtk4 libadwaita libsoup3 libsecret gjs python3 gettext +sudo pacman -S meson ninja gtk4 libadwaita libsoup3 libsecret gjs python gettext ``` -#### Step 2: Clone Repository - ```bash git clone https://git.bugsy.cz/beval/roster.git cd roster -``` - -#### Step 3: Build - -```bash meson setup builddir meson compile -C builddir -``` - -#### Step 4: Install - -```bash sudo meson install -C builddir ``` -#### Step 5: Run - -```bash -roster -``` - -Or launch from your application menu. - -### Method 2: Build with Flatpak (Recommended) - -Flatpak provides a sandboxed environment with all dependencies included. - -#### Step 1: Install Flatpak Builder - -**Fedora:** -```bash -sudo dnf install flatpak-builder -``` - -**Ubuntu/Debian:** -```bash -sudo apt install flatpak-builder -``` - -#### Step 2: Add Flathub Remote - -```bash -flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo -``` - -#### Step 3: Install GNOME SDK +## Build Flatpak Locally ```bash flatpak install flathub org.gnome.Platform//49 org.gnome.Sdk//49 -``` - -#### Step 4: Build Flatpak - -```bash -git clone https://git.bugsy.cz/beval/roster.git -cd roster flatpak-builder --user --install --force-clean build-dir cz.bugsy.roster.json -``` - -#### Step 5: Run - -```bash flatpak run cz.bugsy.roster ``` -### Method 3: GNOME Builder (For Development) +## GNOME Builder -GNOME Builder provides an integrated development environment. - -#### Step 1: Install GNOME Builder - -```bash -flatpak install flathub org.gnome.Builder -``` - -#### Step 2: Open Project - -1. Launch GNOME Builder -2. Click "Clone Repository" -3. Enter: `https://git.bugsy.cz/beval/roster.git` -4. Click "Clone" - -#### Step 3: Build and Run - -1. Click the "Build" button (hammer icon) -2. Click the "Run" button (play icon) - -## Uninstallation - -### Native Installation - -```bash -cd roster/builddir -sudo ninja uninstall -``` - -### Flatpak Installation - -```bash -flatpak uninstall cz.bugsy.roster -``` +Clone `https://git.bugsy.cz/beval/roster.git` in GNOME Builder and hit Run. ## File Locations -### Native Installation +| | Native | Flatpak | +|---|---|---| +| Data | `~/.local/share/cz.bugsy.roster/` | `~/.var/app/cz.bugsy.roster/data/cz.bugsy.roster/` | +| Sensitive vars | GNOME Keyring | GNOME Keyring | -- **Binary**: `/usr/local/bin/roster` (or `/usr/bin/roster`) -- **Application data**: `~/.local/share/cz.bugsy.roster/` -- **Requests/Projects**: `~/.local/share/cz.bugsy.roster/requests.json` -- **Session state**: `~/.local/share/cz.bugsy.roster/session.json` -- **Sensitive variables**: GNOME Keyring (encrypted) +## Uninstall -### Flatpak Installation +```bash +# Source build +sudo ninja -C builddir uninstall -- **Binary**: Managed by Flatpak -- **Application data**: `~/.var/app/cz.bugsy.roster/data/cz.bugsy.roster/` -- **Requests/Projects**: `~/.var/app/cz.bugsy.roster/data/cz.bugsy.roster/requests.json` -- **Session state**: `~/.var/app/cz.bugsy.roster/data/cz.bugsy.roster/session.json` -- **Sensitive variables**: GNOME Keyring (encrypted) - -## Troubleshooting - -**Build errors:** -- "meson: command not found" → Install Meson -- "Package not found" errors → Install missing development packages - -**Runtime errors:** -- "Failed to access Secret Service" → Ensure GNOME Keyring is unlocked -- "Module 'gi' not found" → Install PyGObject - -**Flatpak:** -- "Nothing matches org.gnome.Platform" → Add Flathub remote and install GNOME SDK -- No network access → Verify `--share=network` permission - -## Next Steps - -- [[Getting-Started]] - Learn how to use Roster -- [[Projects-and-Environments]] - Organize your API testing -- [[Variables]] - Use variables in requests +# Flatpak +flatpak uninstall cz.bugsy.roster +``` diff --git a/Projects-and-Environments.md b/Projects-and-Environments.md index a29a3d1..a2cec55 100644 --- a/Projects-and-Environments.md +++ b/Projects-and-Environments.md @@ -1,141 +1,28 @@ # Projects and Environments -Projects help organize related HTTP requests, while environments allow you to manage different configurations (development, staging, production). - ## Projects -### What is a Project? +A project groups saved requests, variables, and environments together. Projects appear in the left sidebar. -A project is a container for: -- **Saved requests** - Your HTTP requests -- **Variables** - Shared variables across requests -- **Environments** - Different configurations (dev, prod, etc.) - -Example projects: -- "GitHub API" -- "My App Backend" -- "Payment Integration" -- "Authentication Service" - -### Creating a Project - -1. Click the "+" icon in top-left -2. Enter project name -3. Click **"Create"** - -A default "Default" environment is automatically created. - -### Accessing Projects - -Projects appear in the sidebar on the left side: -- Click a project to expand/collapse -- Shows saved requests within the project -- Active project highlighted - -### Editing a Project - -1. Click the "three-dots" symbol near the project name in sidebar -2. Select **"Edit Project"** -3. Enter new name or click icon to choose from grid -4. Click **"Save"** - -### Deleting a Project - -1. Right-click the project in sidebar -2. Select **"Delete"** -3. Confirm deletion - -**Warning:** This deletes all requests, environments, and variables in the project! - -## Environments - -### What is an Environment? - -An environment is a set of variable values. Common environments: -- **Production** - Live API with real credentials -- **Staging** - Pre-production testing -- **Development** - Local development server -- **Testing** - Automated test environment - -### Managing Environments - -See [[Variables]] for complete environment and variable management documentation. - -**Quick access:** -1. Select a project -2. Click the "three-dots" symbol near the project name in sidebar -3. Click **"Manage Environments" -3. Manage environments and variables in dialog - -### Default Environment - -Each project starts with a "Default" environment. You can: -- Rename it -- Add more environments -- Delete it (if you have at least one other environment) +- **Create:** click **+** in the sidebar header +- **Edit / Delete:** click **⋯** next to the project name +- **Icon:** editable in the project edit dialog ## Saved Requests -### Saving a Request +- **Save:** **Ctrl+S**, enter name and select project +- **Open:** click request name in the sidebar → opens in a new tab +- **Delete:** click **×** next to the request name in sidebar -1. Configure your request (URL, method, headers, body) -2. Click **"Save"** button in header -3. Enter request name (e.g., "Get User Profile") -4. Select a project -5. Click **"Save"** +Unsaved changes show a dot (•) on the tab label. -### Opening a Saved Request +## Environments and Variables -1. Navigate to your project -2. Click on a saved request -3. Request opens in a new tab - -### Editing a Saved Request - -1. Open the request -2. Make changes -3. Click **"Save"** (or Ctrl+S) -4. Select Project and enter request name -5. Click Save - -**Modified indicator:** Unsaved changes show a dot (•) on the tab label. - -### Deleting a Saved Request - -1. Click "x" icon near the request name in sidebar -2. Confirm deletion - -Requests can have preprocessing and postprocessing scripts attached. See [[Scripts]] for details. +See [[Variables]] for full documentation. Quick access: **⋯ → Manage Environments** next to the project name. ## Data Storage -### File Location +Projects and variables: `~/.local/share/cz.bugsy.roster/requests.json` +(Flatpak: `~/.var/app/cz.bugsy.roster/data/cz.bugsy.roster/requests.json`) -**Native:** -``` -~/.local/share/cz.bugsy.roster/requests.json -``` - -**Flatpak:** -``` -~/.var/app/cz.bugsy.roster/data/cz.bugsy.roster/requests.json -``` - -### Sensitive Variables - -Regular variables stored in JSON. -Sensitive variables stored in GNOME Keyring (encrypted). - -See [[Sensitive-Variables]] for details. - -### Backup - -To backup your projects: -1. Copy the `requests.json` file -2. For sensitive variables: backup your GNOME Keyring (see [[Sensitive-Variables]]) - -## Next Steps - -- [[Variables]] - Learn about variables and environments -- [[Sensitive-Variables]] - Secure credential storage -- [[Scripts]] - Automate workflows +Sensitive variables: GNOME Keyring — see [[Sensitive-Variables]]. diff --git a/Scripts.md b/Scripts.md index 2ac8907..02bbefc 100644 --- a/Scripts.md +++ b/Scripts.md @@ -1,393 +1,78 @@ # Scripts -Roster supports JavaScript preprocessing and postprocessing scripts to automate request modifications and response data extraction. +Roster supports JavaScript preprocessing and postprocessing scripts via **GJS** (GNOME JavaScript). -## Overview +**Preprocessing** — runs before the request is sent; can modify `request`. +**Postprocessing** — runs after the response; can read `response` and set variables. -Scripts allow you to: -- **Modify requests** before they're sent (preprocessing) -- **Extract data** from responses after they're received (postprocessing) -- **Chain requests** together by passing data between them -- **Automate workflows** like authentication flows - -Scripts are executed using **GJS** (GNOME JavaScript), the same JavaScript runtime used throughout GNOME. - -## Script Types - -### Preprocessing Scripts - -**Run BEFORE the HTTP request is sent.** - -Use cases: -- Modify request headers, URL, body, or method -- Add dynamic values (timestamps, request IDs, signatures) -- Read environment variables -- Set/update environment variables -- Add authentication headers - -### Postprocessing Scripts - -**Run AFTER receiving the HTTP response.** - -Use cases: -- Extract data from response body -- Parse JSON/XML responses -- Store values in environment variables for use in subsequent requests -- Validate response data -- Chain requests together - -## Adding Scripts to Requests - -### Step 1: Open Script Editor - -Use the Scripts tab in the request editor - -The script editor tab contains two panels: -- **Preprocessing** panel -- **Postprocessing** panel - -### Step 2: Write Your Script - -1. Select the appropriate panel -2. Write JavaScript code -3. Click **"Save"** or **Ctrl+S** - -### Step 3: Run Request with Scripts - -1. Open the request -2. Click **"Send"** -3. Preprocessing script runs first (if present) -4. HTTP request sent -5. Postprocessing script runs after response received (if present) +Open the **Scripts** tab in the request editor. Save with **Ctrl+S**. ## Preprocessing API -### Available Objects - -#### Request Object (Modifiable) - ```javascript -request.method // String: "GET", "POST", "PUT", "DELETE" -request.url // String: Full URL -request.headers // Object: Header key-value pairs -request.body // String: Request body +request.method // "GET", "POST", "PUT", "DELETE" +request.url // Full URL string +request.headers // Header key-value object +request.body // Request body string + +roster.getVariable(name) +roster.setVariable(name, value) +roster.setVariables({key: value}) +roster.project.name +roster.project.environments // string[] + +console.log(...) / console.error(...) ``` -All properties can be modified. - -#### Roster API - -```javascript -// Variables -roster.getVariable(name) // Get variable from selected environment -roster.setVariable(name, value) // Set/update variable -roster.setVariables({key: value}) // Batch set variables - -// Project Information -roster.project.name // Current project name -roster.project.environments // Array of environment names -``` - -#### Console Output - -```javascript -console.log(message) // Output shown in preprocessing results tab -console.error(message) // Error output -``` - -### Examples - -#### Example 1: Add Dynamic Authentication Header - +**Example:** ```javascript const token = roster.getVariable('auth_token'); request.headers['Authorization'] = 'Bearer ' + token; request.headers['X-Request-Time'] = new Date().toISOString(); -console.log('Added auth header'); -``` - -#### Example 2: Modify Request Based on Environment - -```javascript -const env = roster.getVariable('environment_name'); -if (env === 'production') { - request.url = request.url.replace('localhost', 'api.example.com'); - console.log('Switched to production URL'); -} else { - console.log('Using development URL'); -} -``` - -#### Example 3: Generate Request Signature - -```javascript -const apiKey = roster.getVariable('api_key'); -const timestamp = Date.now().toString(); -const requestId = Math.random().toString(36).substring(7); - -request.headers['X-API-Key'] = apiKey; -request.headers['X-Timestamp'] = timestamp; -request.headers['X-Request-ID'] = requestId; - -// Save for later reference -roster.setVariable('last_request_id', requestId); -console.log('Request ID:', requestId); -``` - -#### Example 4: Add HMAC Signature - -```javascript -// Note: GJS doesn't have crypto module, this is pseudocode -const apiKey = roster.getVariable('api_key'); -const secretKey = roster.getVariable('secret_key'); -const timestamp = Date.now().toString(); - -// You would use a crypto library here -const signature = generateHMAC(request.body + timestamp, secretKey); - -request.headers['X-API-Key'] = apiKey; -request.headers['X-Timestamp'] = timestamp; -request.headers['X-Signature'] = signature; -``` - -#### Example 5: Modify Request Body - -```javascript -// Parse existing body -const body = JSON.parse(request.body); - -// Add dynamic fields -body.timestamp = new Date().toISOString(); -body.requestId = Math.random().toString(36).substring(7); -body.version = '2.0'; - -// Update request body -request.body = JSON.stringify(body, null, 2); -console.log('Modified request body'); ``` ## Postprocessing API -### Available Objects - -#### Response Object (Read-Only) - ```javascript -response.body // String: Response body -response.headers // Object: Header key-value pairs -response.statusCode // Number: HTTP status code (200, 404, etc.) -response.statusText // String: Status text ("OK", "Not Found", etc.) -response.responseTime // Number: Response time in milliseconds +response.body // string, read-only +response.headers // object, read-only +response.statusCode // number +response.statusText // string +response.responseTime // number (ms) + +roster.setVariable(name, value) +roster.setVariables({key: value}) +// roster.getVariable() is NOT available here + +console.log(...) / console.error(...) ``` -All properties are read-only. - -#### Roster API - -```javascript -// Variables -roster.setVariable(name, value) // Set/update variable -roster.setVariables({key: value}) // Batch set variables -``` - -Note: `roster.getVariable()` is NOT available in postprocessing. - -#### Console Output - -```javascript -console.log(message) // Output shown in postprocessing results tab -console.error(message) // Error output -``` - -### Examples - -#### Example 1: Extract Authentication Token - -```javascript -const data = JSON.parse(response.body); -if (data.access_token) { - roster.setVariable('auth_token', data.access_token); - console.log('Saved auth token'); -} else { - console.error('No access token in response'); -} -``` - -#### Example 2: Extract Multiple Values - +**Example:** ```javascript const data = JSON.parse(response.body); roster.setVariables({ - user_id: data.user.id, - user_name: data.user.name, - user_email: data.user.email, - session_id: data.session.id + access_token: data.access_token, + user_id: data.user_id }); -console.log('Extracted user:', data.user.name); ``` -#### Example 3: Validate and Store Response +## Execution Order -```javascript -const data = JSON.parse(response.body); +1. Preprocessing script +2. Variable substitution (`{{variable_name}}`) +3. HTTP request +4. Postprocessing script +5. Response displayed and saved to history -if (response.statusCode === 200 && data.items) { - roster.setVariable('item_count', data.items.length.toString()); +## Limitations - if (data.items.length > 0) { - roster.setVariable('first_item_id', data.items[0].id); - roster.setVariable('first_item_name', data.items[0].name); - } - - console.log('Found', data.items.length, 'items'); -} else { - console.error('Error: Invalid response or no items'); -} -``` - -#### Example 4: Extract Pagination Token - -```javascript -const data = JSON.parse(response.body); - -if (data.next_page_token) { - roster.setVariable('next_page', data.next_page_token); - console.log('Next page token saved'); -} else { - roster.setVariable('next_page', ''); - console.log('No more pages'); -} -``` - -#### Example 5: Parse Response Headers - -```javascript -// Extract rate limit information from headers -const rateLimit = response.headers['X-RateLimit-Limit']; -const rateRemaining = response.headers['X-RateLimit-Remaining']; -const rateReset = response.headers['X-RateLimit-Reset']; - -if (rateLimit && rateRemaining && rateReset) { - roster.setVariables({ - rate_limit: rateLimit, - rate_remaining: rateRemaining, - rate_reset: rateReset - }); - console.log('Rate limit:', rateRemaining, '/', rateLimit); -} else { - console.log('No rate limit headers'); -} -``` - -## Complete Workflows - -### Workflow 1: OAuth Token Flow - -**Request 1: Login (POST /auth/login)** - -Postprocessing: -```javascript -// Extract and store tokens from login response -const data = JSON.parse(response.body); - -if (response.statusCode === 200) { - roster.setVariables({ - access_token: data.access_token, - refresh_token: data.refresh_token, - user_id: data.user_id, - expires_at: data.expires_at - }); - console.log('Logged in as user:', data.user_id); -} else { - console.error('Login failed:', response.statusText); -} -``` - -**Request 2: Get User Profile (GET /users/{userId})** - -Preprocessing: -```javascript -// Use stored token and user ID in request -const token = roster.getVariable('access_token'); -const userId = roster.getVariable('user_id'); - -// Add authentication -request.headers['Authorization'] = 'Bearer ' + token; - -// Substitute user ID in URL -request.url = request.url.replace('{userId}', userId); - -console.log('Making authenticated request for user:', userId); -``` - -### Workflow 2: Paginated API Requests - -**Request: Get Items with Pagination** - -Preprocessing: -```javascript -const nextPage = roster.getVariable('next_page'); - -if (nextPage) { - request.url = request.url + '?page_token=' + nextPage; - console.log('Fetching next page'); -} else { - console.log('Fetching first page'); -} -``` - -Postprocessing: -```javascript -const data = JSON.parse(response.body); - -// Store items count -roster.setVariable('items_count', data.items.length.toString()); - -// Store next page token for subsequent request -if (data.next_page_token) { - roster.setVariable('next_page', data.next_page_token); - console.log('Page loaded. More pages available.'); -} else { - roster.setVariable('next_page', ''); - console.log('Page loaded. No more pages.'); -} -``` - -### Workflow 3: Dynamic API Versioning - -**Preprocessing:** -```javascript -const apiVersion = roster.getVariable('api_version') || 'v1'; -request.url = request.url.replace('/api/', '/api/' + apiVersion + '/'); -request.headers['X-API-Version'] = apiVersion; -console.log('Using API version:', apiVersion); -``` - -**Postprocessing:** -```javascript -// Check if server suggests upgrading API version -const suggestedVersion = response.headers['X-Suggested-API-Version']; - -if (suggestedVersion) { - console.log('Server suggests API version:', suggestedVersion); - roster.setVariable('suggested_api_version', suggestedVersion); -} -``` - -## Script Execution Flow - -When sending a request with scripts: -1. Execute preprocessing script (if present) - can modify request -2. Apply variable substitution (`{{variable_name}}`) -3. Send HTTP request -4. Execute postprocessing script (if present) - can read response, set variables -5. Display response and save to history +- Synchronous only — no `async/await`, `setTimeout`, `fetch` +- No file system, shell, or external library access ## Error Handling -If a script throws an error, the error is shown in the script output panel. The request still executes (preprocessing) or response still displays (postprocessing). Variable changes before the error are preserved. +Script errors are shown in the output panel; the request still executes. -Always use try-catch for JSON parsing: ```javascript try { const data = JSON.parse(response.body); @@ -397,46 +82,4 @@ try { } ``` -## Best Practices - -- **Keep scripts simple** - One clear purpose per script -- **Handle errors** - Use try-catch for JSON parsing and check status codes -- **Use logging** - Track execution with console.log for debugging -- **Never log secrets** - Log confirmations, not actual token/key values - -## Limitations - -### No External Libraries - -GJS environment is sandboxed: -- Cannot import npm packages -- Cannot require() external modules -- Standard JavaScript built-ins available - -### No Asynchronous Operations - -Scripts execute synchronously: -- No setTimeout/setInterval -- No async/await -- No fetch() or XMLHttpRequest -- Use Roster's request system instead - -### Security Restrictions - -Scripts cannot: -- Access file system directly -- Make network requests (other than the main request) -- Execute shell commands -- Access system resources - -## Debugging Scripts - -- Use console.log liberally to track script execution -- Check Preprocessing/Postprocessing panels for output and errors -- Start with simple test scripts and build up complexity - -## Next Steps - -- [[API-Reference]] - Complete API documentation -- [[Variables]] - Learn about environment variables -- [[Sensitive-Variables]] - Secure storage for tokens and keys +See [[API-Reference]] for the full API reference. diff --git a/Sensitive-Variables.md b/Sensitive-Variables.md index 9fe20a9..21946d5 100644 --- a/Sensitive-Variables.md +++ b/Sensitive-Variables.md @@ -1,153 +1,40 @@ # Sensitive Variables -Sensitive variables provide secure storage for API keys, passwords, and tokens using GNOME Keyring encryption instead of plain text files. +Sensitive variables store their values in **GNOME Keyring** (encrypted) instead of plain-text JSON. Use them for API keys, passwords, and tokens. -## Overview +## Marking a Variable as Sensitive -Regular [[Variables]] are stored in plain text JSON files at `~/.local/share/cz.bugsy.roster/requests.json`. This is fine for non-sensitive data like base URLs or timeout values, but **dangerous for secrets**. +In the Environments dialog, click the **lock icon** on a variable row. The icon toggles between locked (keyring) and unlocked (JSON). Existing values are automatically migrated when you toggle. -**Sensitive variables** solve this by storing values encrypted in GNOME Keyring, the same secure storage used by: -- Firefox and GNOME Web for passwords -- Evolution for email credentials -- Network Manager for WiFi passwords -- SSH for key passphrases +Sensitive variable values display as bullets (••••) in the dialog. -## How It Works +## Usage in Requests -### Storage Comparison +Works exactly like regular variables — use `{{variable_name}}` syntax. Roster retrieves and decrypts the value transparently before sending. -**Regular Variable (Insecure):** -``` -File: ~/.local/share/cz.bugsy.roster/requests.json -Format: Plain text (anyone can read) -Content: "api_key": "secret-abc123-visible-to-all" +## Script Access + +Scripts access sensitive variables the same way as regular ones: + +```javascript +// Preprocessing +const key = roster.getVariable('api_key'); // decrypted automatically + +// Postprocessing — if 'access_token' is marked sensitive, it goes to keyring +roster.setVariable('access_token', data.access_token); ``` -**Sensitive Variable (Secure):** -``` -File: ~/.local/share/cz.bugsy.roster/requests.json -Format: Plain text -Content: "api_key": "" (empty placeholder) +## Keyring Schema -Keyring: GNOME Keyring (encrypted) -Format: Encrypted with your login password -Content: "secret-abc123-visible-to-all" (encrypted at rest) -``` +- **Schema:** `cz.bugsy.roster.EnvironmentVariable` +- **Attributes:** `project_id`, `environment_id`, `variable_name` -## Usage - -### Mark Variable as Sensitive - -Open the Environments dialog for your project. - -Each variable row has a **lock icon** to toggle between secure/insecure storage. - -**Before entering secrets (recommended):** -1. Create variable (e.g., `api_key`) -2. Click the **lock icon** - it changes to locked state -3. Variable name becomes bold and colored -4. Enter your secret value - -**After entering secrets (migration):** -1. Click the lock icon on existing variable -2. Values automatically move from JSON to keyring - -### Enter Secret Values - -Values in sensitive variables: -- Display as bullets (••••••) instead of characters -- Automatically save to GNOME Keyring (encrypted) - -### Use in Requests - -Sensitive variables work exactly like regular variables: - -``` -GET {{base_url}}/users -Authorization: Bearer {{api_key}} -``` - -When you send the request: -1. Roster retrieves `api_key` from GNOME Keyring -2. Decrypts it using your login password -3. Substitutes it into the request -4. Sends the request with the real value - -## Viewing Secrets in Keyring - -Open "Passwords and Keys" (Seahorse) and navigate to "Login" keyring. Look for entries labeled `Roster: ProjectName/EnvironmentName/VariableName`. - -You can view, edit, or delete secrets from Seahorse (requires authentication). - -## Making Variables Non-Sensitive - -If you marked a variable as sensitive by mistake: - -1. Click the lock icon again -2. Icon changes from locked to unlocked -3. Values move from keyring back to JSON -4. You can now see values in plain text - -**Warning:** Only do this for non-sensitive data! Once moved to JSON, the values are visible to anyone who can read your files. - -## Security - -- Encrypted at rest with your GNOME login password -- Keyring unlocks automatically on login -- Only accessible by your user account -- Flatpak requires explicit permission +Secrets are visible in **Passwords and Keys** (Seahorse) under the Login keyring. ## Automatic Cleanup -Roster automatically removes keyring secrets when you delete variables, environments, or projects. Renamed variables are also automatically updated in the keyring. +Roster removes keyring entries when you delete or rename variables, environments, or projects. -## Script Integration +## Flatpak -Scripts can access sensitive variables transparently: - -### Reading Sensitive Variables - -```javascript -// Preprocessing script -const apiKey = roster.getVariable('api_key'); // Retrieved from keyring -request.headers['X-API-Key'] = apiKey; -console.log('Using API key'); // DON'T log the actual value! -``` - -### Writing Sensitive Variables - -```javascript -// Postprocessing script -const data = JSON.parse(response.body); - -// If 'access_token' is marked as sensitive, it goes to keyring -roster.setVariable('access_token', data.access_token); -console.log('Saved access token to keyring'); -``` - -Scripts don't need to know whether variables are sensitive - storage is handled automatically. - -## Troubleshooting - -**Keyring not unlocked:** -- Ensure you're logged into GNOME (keyring unlocks automatically) -- Manual unlock: Open Seahorse, right-click "Login", select "Unlock" - -**Flatpak permission denied:** -- Verify `cz.bugsy.roster.json` includes `--talk-name=org.freedesktop.secrets` -- Check: `flatpak info --show-permissions cz.bugsy.roster` - -**Secrets missing after reinstall:** -- Keyring persists across installs - check Seahorse under "Login" keyring - -## Platform Notes - -Both native and Flatpak installations use the same system keyring. Secrets work across install methods. - -Flatpak requires D-Bus permission: `--talk-name=org.freedesktop.secrets` - -## Next Steps - -- [[Variables]] - Learn about regular variables -- [[Scripts]] - Use variables in automation scripts -- [[API-Reference]] - Complete JavaScript API documentation +Requires D-Bus permission `--talk-name=org.freedesktop.secrets` in the Flatpak manifest. diff --git a/Variables.md b/Variables.md index be47e36..c84266b 100644 --- a/Variables.md +++ b/Variables.md @@ -1,216 +1,48 @@ # Variables -Variables allow you to reuse values across multiple requests and switch between different environments (development, staging, production) with ease. +Variables let you reuse values across requests and switch between environments (dev, staging, prod) with one click. -## What are Variables? +## Syntax -Variables are placeholder values that you can reference in your requests using the `{{variable_name}}` syntax. When you send a request, Roster automatically replaces these placeholders with the actual values from your selected environment. +Use `{{variable_name}}` in URL, headers, or body. When sending, Roster replaces placeholders with values from the selected environment. -**Example:** ``` -URL: {{base_url}}/users -Headers: - Authorization: Bearer {{api_token}} +GET {{base_url}}/users +Authorization: Bearer {{api_token}} ``` -When sent with the "Production" environment: -``` -URL: https://api.example.com/users -Headers: - Authorization: Bearer prod-token-abc123 -``` +## Managing Environments and Variables -## Environments +Open via **⋯ → Manage Environments** next to the project name in the sidebar. -Environments are collections of variable values. Common environment names: -- **Production** - Live API credentials -- **Staging** - Pre-production testing -- **Development** - Local development server +- **Add environment:** click **+** next to the environment list +- **Add variable:** click **+** at the bottom of the variable list +- **Delete:** click **×** on the variable row or environment header +- **Rename:** click the name inline -Each project can have multiple environments, and each environment has its own set of variable values. +Values are entered in the table cells per environment. Close the dialog to save. -## Creating Environments +## Selecting an Environment -### Step 1: Open Environments Dialog +Use the environment dropdown at the top of the request panel. All `{{...}}` placeholders resolve to values from the selected environment. -1. Select a project from the sidebar -2. Click the "Three-Dots" button near the project name -3. Select **"Manage Environments"** +Undefined variables are highlighted with a warning color; the literal `{{variable_name}}` is sent as-is. -### Step 2: Add Environment - -1. Click **+** button near Environments -2. Enter name (e.g., "Production", "Development") -3. Click **"Add"** - -The environment appears as a new column in the table. - -### Step 3: Add Variables - -1. Click the **"+"** button at the bottom of the variable list -2. Enter variable name (e.g., `base_url`) -3. Click **"Add"** - -The variable appears as a new row in the table. - -### Step 4: Set Values - -Click in the cells to enter values for each environment: - -| Variable | Production | Development | -|----------|-----------|-------------| -| base_url | `https://api.example.com` | `http://localhost:3000` | -| timeout | `30` | `60` | - -Close Manage Environments table to save. - -## Using Variables in Requests - -### Variable Syntax - -Use double curly braces: `{{variable_name}}` - -Variables work in: -- **URL**: `{{base_url}}/users` -- **Headers**: `Authorization: Bearer {{token}}` -- **Request Body**: `{"api_key": "{{api_key}}"}` - -### Select Environment - -1. Open a request tab -2. Use the **environment dropdown** (top of request panel) -3. Select environment (e.g., "Production") -4. All variables in the request will use values from this environment - -### Visual Indicators - -**Defined variables** - Normal appearance - -**Undefined variables** - Highlighted with a warning color -- Variable name exists in `{{...}}` syntax -- But not defined in current environment - -## Managing Variables - -### Rename Variable - -1. Open **Manage Environments** dialog -2. Click on variable name -3. Edit the name - -The variable is renamed across all environments automatically. - -### Delete Variable - -1. Open **Environments** dialog -2. Click the **x** next to variable name -3. Confirm deletion - -The variable is removed from all environments. - -### Rename Environment - -1. Open **Environments** dialog -2. Click the **edit icon** on environment column header -3. Enter new name -4. Click **"Save"** - -### Delete Environment - -1. Open **Environments** dialog -2. Click the **trash icon** on environment column header -3. Confirm deletion - -All variable values for that environment are deleted. - -**Note:** Each project must have at least one environment. - -## Common Variable Patterns - -Common variables to define across environments: -- `base_url` - API base URL (e.g., `https://api.example.com` vs `http://localhost:3000`) -- `api_version` - API version (e.g., `v2` vs `v2-beta`) -- `timeout` - Request timeout values -- `env` - Environment identifier for headers -- `test_user_id` - Test data IDs - -## Script Integration - -Variables can be accessed and modified from [[Scripts]]: - -### Reading Variables (Preprocessing) +## Script Access ```javascript -const baseUrl = roster.getVariable('base_url'); -const apiKey = roster.getVariable('api_key'); +// Preprocessing +const url = roster.getVariable('base_url'); -console.log('Using base URL:', baseUrl); -request.headers['X-API-Key'] = apiKey; -``` - -### Writing Variables (Postprocessing) - -```javascript -// Extract token from response and save for next request -const data = JSON.parse(response.body); +// Preprocessing or postprocessing roster.setVariable('auth_token', data.access_token); -roster.setVariable('user_id', data.user_id); -console.log('Saved token for user:', data.user_id); +roster.setVariables({ token: data.token, user_id: data.id }); ``` -### Batch Updates +## Sensitive Variables -```javascript -// Update multiple variables at once -roster.setVariables({ - access_token: data.access_token, - refresh_token: data.refresh_token, - user_id: data.user.id, - user_name: data.user.name -}); -``` +For API keys and passwords, use [[Sensitive-Variables]] — values are stored in GNOME Keyring instead of plain-text JSON. -Scripts automatically update variables in the currently selected environment. +## Storage -## Variable Scoping - -**Project Level:** -- Variables are defined at the project level -- All requests in the project can access these variables - -**Environment Level:** -- Variable values are specific to each environment -- Switching environments switches all values at once - -**Not Global:** -- Variables from one project cannot be used in another project -- Each project has its own isolated set of variables - -## Best Practices - -- **Keep environments consistent** - All environments should have the same variable names with different values -- **Use sensitive variables for secrets** - Never store API keys, passwords, or tokens in regular variables (they're plain text). Use [[Sensitive-Variables]] instead - -## Undefined Variables - -When `{{variable_name}}` is not defined in the current environment: -- Entry field is highlighted with warning color -- The literal text `{{variable_name}}` is sent in the request - -To fix, add the variable in the Environments dialog. - -## Environment Switching - -Use the environment dropdown to test the same request against different targets. For example, `GET {{base_url}}/users` will hit `localhost:3000/users` in Development or `api.example.com/users` in Production. - -## Advanced Topics - -- **Variables in JSON body** - Use `{{variable}}` syntax. Remove quotes for numeric values -- **Nested variables** - Variables can reference other variables (e.g., `{{base_url}}/{{api_version}}`) -- **Script-created variables** - Scripts can create variables with `roster.setVariable()` - -## Next Steps - -- [[Sensitive-Variables]] - Store API keys and secrets securely -- [[Scripts]] - Automate variable management with JavaScript -- [[API-Reference]] - Complete API for working with variables +Regular variables: `~/.local/share/cz.bugsy.roster/requests.json` (Flatpak: `~/.var/app/cz.bugsy.roster/data/cz.bugsy.roster/requests.json`)