Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion httui-core/src/block_examples.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! Pinned response examples per HTTP block — Onda 3.
//! Pinned response examples per HTTP block.
//!
//! Examples are user-curated snapshots ("happy path 200", "422 invalid
//! email"). Unlike the cache (`block_results`) they survive cache
Expand Down
11 changes: 1 addition & 10 deletions httui-core/src/block_history/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@ const DEFAULT_HISTORY_CAP: i64 = 10;
const RETENTION_KEY: &str = "history_retention";

/// Read the user-configured history retention from `app_config`, falling
/// back to the default. Values <= 0 are treated as the default — to fully
/// disable history the per-block `history_disabled` flag is the right
/// switch (Onda 1).
/// back to the default. Values <= 0 are treated as the default.
async fn get_retention(pool: &SqlitePool) -> i64 {
let row: Option<String> = sqlx::query_scalar("SELECT value FROM app_config WHERE key = ?")
.bind(RETENTION_KEY)
Expand Down Expand Up @@ -64,7 +62,6 @@ pub async fn insert_history_entry(
.execute(pool)
.await?;

// Trim to the retention cap (user-configurable via app_config; default 10).
let cap = get_retention(pool).await;
sqlx::query(
"DELETE FROM block_run_history
Expand Down Expand Up @@ -242,7 +239,6 @@ mod tests {
.connect("sqlite::memory:")
.await
.unwrap();
// Apply the migration manually for tests.
sqlx::query(
"CREATE TABLE block_run_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
Expand Down Expand Up @@ -522,15 +518,13 @@ mod tests {
#[tokio::test]
async fn list_history_for_file_aggregates_across_aliases() {
let pool = setup().await;
// Insert 2 entries for alpha + 1 for beta in the same file.
let mut a = entry("GET", 200);
a.block_alias = "alpha".into();
insert_history_entry(&pool, a.clone()).await.unwrap();
insert_history_entry(&pool, a).await.unwrap();
let mut b = entry("POST", 201);
b.block_alias = "beta".into();
insert_history_entry(&pool, b).await.unwrap();
// A row in a different file shouldn't surface.
let mut other = entry("GET", 200);
other.file_path = "/other.md".into();
other.block_alias = "alpha".into();
Expand Down Expand Up @@ -566,7 +560,6 @@ mod tests {
.await
.unwrap();
}
// limit <= 0 → effective fallback of 50; 3 inserted rows fit easily.
let rows = list_history_for_file(&pool, "/notes/test.md", 0)
.await
.unwrap();
Expand Down Expand Up @@ -603,7 +596,6 @@ mod tests {
.await
.unwrap();
}
// 2 rows fits the default cap (10) with room to spare.
let rows = list_history(&pool, "/notes/test.md", "req1").await.unwrap();
assert_eq!(rows.len(), 2);

Expand All @@ -620,7 +612,6 @@ mod tests {
.unwrap();
}
let rows = list_history(&pool, "/notes/test.md", "req1").await.unwrap();
// 4 inserted total, default cap is 10 — all 4 visible.
assert_eq!(rows.len(), 4);
}
}
11 changes: 5 additions & 6 deletions httui-core/src/block_results.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ use sqlx::sqlite::SqlitePool;
use sqlx::Row;

/// Compute a block hash that includes content + environment + connection context.
/// This ensures cache invalidation when environment or connection changes (T31),
/// and moves hash computation server-side so frontend cannot spoof it (T35).
pub fn compute_block_hash(
content: &str,
environment_id: Option<&str>,
Expand Down Expand Up @@ -87,8 +85,9 @@ pub async fn save_block_result(
Ok(())
}

/// T38: Try to acquire an execution lock for a block.
/// Returns true if lock was acquired (caller should execute), false if another execution is in progress.
/// Try to acquire an execution lock for a block. Returns `true` if
/// acquired (caller should execute), `false` if another execution is
/// already in progress.
pub async fn try_acquire_execution_lock(
pool: &SqlitePool,
file_path: &str,
Expand All @@ -107,7 +106,7 @@ pub async fn try_acquire_execution_lock(
Ok(result.rows_affected() > 0)
}

/// T38: Release an execution lock after block execution completes.
/// Release an execution lock after block execution completes.
pub async fn release_execution_lock(
pool: &SqlitePool,
file_path: &str,
Expand All @@ -121,7 +120,7 @@ pub async fn release_execution_lock(
Ok(())
}

/// T38: Clean up stale execution locks (older than 60 seconds — execution timed out or crashed).
/// Clean up stale execution locks older than 60 seconds (timed out or crashed).
pub async fn cleanup_stale_locks(pool: &SqlitePool) -> Result<(), sqlx::Error> {
sqlx::query(
"DELETE FROM block_execution_locks WHERE locked_at < datetime('now', '-60 seconds')",
Expand Down
2 changes: 1 addition & 1 deletion httui-core/src/block_settings.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! Per-block settings — Onda 1 of the HTTP block redesign.
//! Per-block settings for HTTP blocks.
//!
//! Stores transport/UX flags that the user toggles in the block drawer,
//! keyed by `(file_path, block_alias)`. Stored separately from the fence
Expand Down
24 changes: 0 additions & 24 deletions httui-core/src/blocks/db_export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
use crate::db::connections::ColumnInfo;
use serde_json::Value;

// ─── CSV ────────────────────────────────────────────────────────────

/// RFC 4180-ish CSV. Quotes any field containing CR, LF, comma, or
/// double-quote; doubles internal quotes. `null` values become empty
/// fields (NOT the literal string "null") so spreadsheets see a real
Expand Down Expand Up @@ -66,8 +64,6 @@ fn format_cell_csv(value: Option<&Value>) -> String {
}
}

// ─── JSON ───────────────────────────────────────────────────────────

/// Pretty-printed JSON array of row objects. Trailing newline included
/// so writing the result to a file leaves a POSIX-friendly final byte.
pub fn to_json(rows: &[Value]) -> String {
Expand All @@ -76,8 +72,6 @@ pub fn to_json(rows: &[Value]) -> String {
s
}

// ─── Markdown table ─────────────────────────────────────────────────

/// GitHub-flavored Markdown table. Pipes and backslashes inside cells
/// are escaped so the table stays syntactically valid; embedded
/// newlines become spaces so each cell stays on one row.
Expand Down Expand Up @@ -125,8 +119,6 @@ fn format_cell_md(value: Option<&Value>) -> String {
}
}

// ─── INSERT statements ─────────────────────────────────────────────

/// Emit one `INSERT INTO <table> (cols) VALUES (...)` per row. Strings
/// get SQL-quoted with single quotes (escaping internal `'` by
/// doubling). Objects/arrays are serialized as JSON and stored inside
Expand Down Expand Up @@ -201,8 +193,6 @@ fn sql_literal(value: Option<&Value>) -> String {
}
}

// ─── Table-name inference ─────────────────────────────────────────

/// Best-effort pull of a table name from a SQL query: first identifier
/// after the first `FROM` keyword (case-insensitive). Used as the
/// default for INSERT export. Returns `None` when no `FROM` clause is
Expand Down Expand Up @@ -282,8 +272,6 @@ fn strip_sql_comments(sql: &str) -> String {
out
}

// ─── Convenience ─────────────────────────────────────────────────

/// `true` when there's at least one column AND one row to serialize.
/// Callers gate the export menu on this so an empty result set
/// doesn't produce a header-only CSV / empty markdown table.
Expand All @@ -307,8 +295,6 @@ mod tests {
names.iter().map(|n| col(n)).collect()
}

// ─── CSV ─────

#[test]
fn csv_emits_header_then_rows() {
let c = cols(&["id", "name"]);
Expand Down Expand Up @@ -353,8 +339,6 @@ mod tests {
assert!(csv.contains("\"{\"\"a\"\":1}\""), "got: {csv}");
}

// ─── JSON ─────

#[test]
fn json_pretty_array_with_trailing_newline() {
let rows = vec![json!({"id": 1})];
Expand All @@ -364,8 +348,6 @@ mod tests {
assert!(s.contains("\"id\": 1"));
}

// ─── Markdown ─────

#[test]
fn markdown_emits_header_separator_and_rows() {
let c = cols(&["id", "name"]);
Expand Down Expand Up @@ -394,8 +376,6 @@ mod tests {
assert!(!md.contains("line1\nline2"));
}

// ─── INSERT ─────

#[test]
fn inserts_basic_row() {
let c = cols(&["id", "name"]);
Expand Down Expand Up @@ -466,8 +446,6 @@ mod tests {
assert!(sql.contains("(\"weird name\")"));
}

// ─── infer_table_name ─────

#[test]
fn infer_simple_select() {
assert_eq!(
Expand Down Expand Up @@ -525,8 +503,6 @@ mod tests {
assert_eq!(infer_table_name(sql), Some("foods".into()));
}

// ─── has_exportable_rows ─────

#[test]
fn empty_result_not_exportable() {
assert!(!has_exportable_rows(&[], &[]));
Expand Down
27 changes: 1 addition & 26 deletions httui-core/src/blocks/http_codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,17 +101,13 @@ fn read_body(params: &Value) -> String {
.to_string()
}

// ─── shell single-quoting ───────────────────────────────────────────

/// Wrap a value in POSIX-shell single quotes, escaping any internal
/// single quotes by closing the quoted run, emitting an escaped quote,
/// and reopening — the standard `'…'\''…'` trick.
fn shell_single_quote(s: &str) -> String {
format!("'{}'", s.replace('\'', "'\\''"))
}

// ─── cURL ───────────────────────────────────────────────────────────

pub fn to_curl(params: &Value) -> String {
let method = read_method(params);
let url = build_url_with_query(params);
Expand All @@ -127,8 +123,6 @@ pub fn to_curl(params: &Value) -> String {
lines.join(" \\\n")
}

// ─── fetch (JavaScript) ─────────────────────────────────────────────

fn js_string(s: &str) -> String {
let mut out = String::with_capacity(s.len() + 2);
out.push('\'');
Expand Down Expand Up @@ -167,8 +161,6 @@ pub fn to_fetch(params: &Value) -> String {
lines.join("\n")
}

// ─── Python requests ────────────────────────────────────────────────

fn py_string(s: &str) -> String {
// Identical escape rules to `js_string` for the chars we care
// about (\, ', \n). Python's single-quoted literal rules match.
Expand Down Expand Up @@ -213,8 +205,6 @@ pub fn to_python(params: &Value) -> String {
lines.join("\n")
}

// ─── HTTPie ─────────────────────────────────────────────────────────

pub fn to_httpie(params: &Value) -> String {
let method = read_method(params);
let url = params
Expand Down Expand Up @@ -244,12 +234,9 @@ pub fn to_httpie(params: &Value) -> String {
tokens.join(" ")
}

// ─── .http file ─────────────────────────────────────────────────────

/// Emit the canonical HTTP-message body the user can paste into a
/// `.http` / `.rest` file (REST Client extension, JetBrains HTTP
/// Client, etc). One request per file — multi-request files separated
/// by `###` are out of scope for V1.
/// Client, etc). One request per file.
pub fn to_http_file(params: &Value) -> String {
let method = read_method(params);
let url = params
Expand Down Expand Up @@ -308,8 +295,6 @@ mod tests {
})
}

// ─── cURL ─────

#[test]
fn curl_emits_method_url_headers_body() {
let curl = to_curl(&fixture());
Expand Down Expand Up @@ -348,8 +333,6 @@ mod tests {
assert!(s.contains("'it'\\''s'"), "got: {s}");
}

// ─── fetch ─────

#[test]
fn fetch_emits_method_headers_body() {
let s = to_fetch(&fixture());
Expand All @@ -360,8 +343,6 @@ mod tests {
assert!(s.contains("body: '{\\'name\\':\\'alice\\'}',") || s.contains("body: "));
}

// ─── Python ─────

#[test]
fn python_emits_imports_and_call() {
let s = to_python(&fixture());
Expand Down Expand Up @@ -390,8 +371,6 @@ mod tests {
assert!(!s.contains("data="));
}

// ─── HTTPie ─────

#[test]
fn httpie_emits_request_items() {
let s = to_httpie(&fixture());
Expand All @@ -404,8 +383,6 @@ mod tests {
assert!(s.contains("--raw="));
}

// ─── .http file ─────

#[test]
fn http_file_emits_request_line_headers_body() {
let s = to_http_file(&fixture());
Expand All @@ -432,8 +409,6 @@ mod tests {
assert_eq!(s, "GET https://x\nX: 1\n");
}

// ─── encoding sanity ─────

#[test]
fn query_encoding_percent_encodes_spaces_and_specials() {
let v = json!({
Expand Down
9 changes: 0 additions & 9 deletions httui-core/src/blocks/http_normalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,6 @@ pub fn normalize_http_blocks(markdown: &str) -> String {
continue;
}

// Opening fence — emit it and consume body lines up to the next
// closing fence (or EOF).
out.push(line.to_string());
i += 1;

Expand All @@ -70,22 +68,17 @@ pub fn normalize_http_blocks(markdown: &str) -> String {

match try_normalize_legacy(&body) {
Some(canonical) => {
// Re-emit canonical body line-by-line; preserves the
// join-on-newline contract.
for nl in canonical.split('\n') {
out.push(nl.to_string());
}
}
None => {
// Not legacy → preserve original bytes. (split+push is a
// no-op on the body's internal newlines.)
for nl in &lines[body_start..body_end] {
out.push((*nl).to_string());
}
}
}

// Emit closing fence (if found) and advance past it.
if i < lines.len() {
out.push(lines[i].to_string());
i += 1;
Expand Down Expand Up @@ -284,8 +277,6 @@ mod tests {

#[test]
fn rewrites_real_world_block_from_bug_report() {
// Mirrors the exact body shape from the user's screenshot — escapes the
// newline in `body` and uses `{{...}}` placeholders.
let md = concat!(
"```http alias=testd\n",
"{\"body\":\"asdf=asdf\\nasdffdsa=asdf\",\"headers\":[{\"key\":\"Teste\",\"value\":\"{{BASE_URL}}\"},{\"key\":\"Content-Type\",\"value\":\"multipart/form-data\"}],\"method\":\"POST\",\"params\":[{\"key\":\"page\",\"value\":\"{{BASE_URL}}\"}],\"url\":\"https://httpbin.org/post\"}\n",
Expand Down
Loading
Loading