0
2
grain-serve import reads hatk's grain.db into a new index
Only what this pull adds. It is built on grain runs on a Rust server modelled on knotless, without hatk.
crates/grain-serve/src/index/import/tests.rsnew+786−0
786 lines changed: not loaded until asked for.
crates/grain-serve/src/index/import.rsnew+698−0
698 lines changed: not loaded until asked for.
crates/grain-serve/src/index/mod.rs+1−0
| @@ -14,6 +14,7 @@ | ||
//! leaves alone what the index has from a later one ([`Applied::Stale`]). | ||
| ||
pub mod backfill; | ||
pub mod import; | ||
pub mod records; | ||
pub mod schema; | ||
| ||
crates/grain-serve/src/main.rs+8−0
| @@ -3,6 +3,9 @@ | ||
//! set and fed from `GRAIN_JETSTREAM` when that is, and taking service | ||
//! tokens for the DID of `GRAIN_PUBLIC_URL` when set | ||
//! ([`grain_serve::Settings`]). | ||
//! | ||
//! `grain-serve import <hatk's grain.db>` reads hatk's database into a new | ||
//! index, at `GRAIN_INDEX` or `--into` ([`grain_serve::index::import`]). | ||
| ||
use anyhow::Context; | ||
use grain::env::{Ctx, Env}; | ||
| @@ -10,6 +13,11 @@ use std::sync::Arc; | ||
| ||
#[tokio::main] | ||
async fn main() -> anyhow::Result<()> { | ||
let args: Vec<String> = std::env::args().collect(); | ||
if args.get(1).is_some_and(|a| a == "import") { | ||
let var = |name: &str| std::env::var(name).ok(); | ||
std::process::exit(grain_serve::index::import::main(&args[2..], var)); | ||
} | ||
tracing_subscriber::fmt() | ||
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) | ||
.init(); | ||
crates/grain-serve/tests/main.rs+43−1
| @@ -1,6 +1,7 @@ | ||
//! The `grain-serve` binary: it serves where `GRAIN_LISTEN` says, keeps its | ||
//! index where `GRAIN_INDEX` says, stops | ||
//! when told to, and says why when it cannot listen. | ||
//! when told to, says why when it cannot listen, and imports hatk's | ||
//! database with `import`. | ||
| ||
use std::process::{Command, Stdio}; | ||
use std::time::Duration; | ||
| @@ -70,3 +71,44 @@ fn says_it_cannot_follow_jetstream_without_an_index() { | ||
let said = String::from_utf8_lossy(&out.stderr); | ||
assert!(said.contains("GRAIN_JETSTREAM needs GRAIN_INDEX"), "{said}"); | ||
} | ||
| ||
#[test] | ||
fn imports_hatks_database_into_its_index() { | ||
let dir = tempfile::tempdir().unwrap(); | ||
let old = dir.path().join("grain.db"); | ||
let db = rusqlite::Connection::open(&old).unwrap(); | ||
db.execute_batch( | ||
"CREATE TABLE _repos (did TEXT PRIMARY KEY, status TEXT NOT NULL DEFAULT 'pending', | ||
handle TEXT, backfilled_at TEXT, rev TEXT, retry_count INTEGER NOT NULL DEFAULT 0, | ||
retry_after INTEGER NOT NULL DEFAULT 0); | ||
INSERT INTO _repos (did, status) VALUES ('did:plc:alice', 'takendown');", | ||
) | ||
.unwrap(); | ||
drop(db); | ||
let index = dir.path().join("index.db"); | ||
let out = Command::new(env!("CARGO_BIN_EXE_grain-serve")) | ||
.arg("import") | ||
.arg(&old) | ||
.env("GRAIN_INDEX", &index) | ||
.output() | ||
.unwrap(); | ||
let said = String::from_utf8_lossy(&out.stdout); | ||
assert!(out.status.success(), "{said}"); | ||
assert!( | ||
said.contains("takedown (_repos, status takendown)"), | ||
"{said}" | ||
); | ||
assert!(said.ends_with("written\n"), "{said}"); | ||
let held = rusqlite::Connection::open(&index).unwrap(); | ||
let did: String = held | ||
.query_row("SELECT did FROM takedown", [], |r| r.get(0)) | ||
.unwrap(); | ||
assert_eq!(did, "did:plc:alice"); | ||
| ||
let usage = Command::new(env!("CARGO_BIN_EXE_grain-serve")) | ||
.arg("import") | ||
.output() | ||
.unwrap(); | ||
assert_eq!(usage.status.code(), Some(2)); | ||
assert!(String::from_utf8_lossy(&usage.stderr).starts_with("usage: grain-serve import")); | ||
} | ||
crates/grain-serve/Cargo.toml+1−0
| @@ -10,6 +10,7 @@ rust-version.workspace = true | ||
grain.workspace = true | ||
anyhow.workspace = true | ||
axum.workspace = true | ||
base64.workspace = true | ||
bytes.workspace = true | ||
futures.workspace = true | ||
reqwest.workspace = true | ||
Cargo.lock+1−0
| @@ -620,6 +620,7 @@ version = "0.1.0" | ||
dependencies = [ | ||
"anyhow", | ||
"axum", | ||
"base64 0.22.1", | ||
"bytes", | ||
"futures", | ||
"grain", | ||