diff --git a/docs/guides/index-hooks.md b/docs/guides/index-hooks.md index e9cd5f4..4d8aa8b 100644 --- a/docs/guides/index-hooks.md +++ b/docs/guides/index-hooks.md @@ -6,7 +6,7 @@ Unlike [query and procedure scripts](scripting.md) that run in response to XRPC ## Attaching a hook -Each record-type lexicon can have one index hook. You can add it through the [dashboard](../getting-started/dashboard.md) (click "Add Index Hook" on any record lexicon's detail page) or via the [admin API](../reference/admin-api.md#upload--upsert-a-lexicon) by including the `on_index_script` field when uploading a lexicon. +Each record-type lexicon can have one index hook. You can add it through the [dashboard](../getting-started/dashboard.md) (click "Add Index Hook" on any record lexicon's detail page) or via the [admin API](../reference/admin-api.md#upload--upsert-a-lexicon) by including the `index_hook` field when uploading a lexicon. ## Script structure diff --git a/docs/reference/admin-api.md b/docs/reference/admin-api.md index 81f035d..81d46c4 100644 --- a/docs/reference/admin-api.md +++ b/docs/reference/admin-api.md @@ -55,7 +55,7 @@ curl -X POST http://localhost:3000/admin/lexicons \ | `backfill` | boolean | no | Whether uploading triggers historical backfill (default `true`) | | `target_collection` | string | no | For query/procedure lexicons, the record collection they operate on | | `script` | string | no | Lua script for query/procedure endpoints | -| `on_index_script` | string | no | [Index hook](../guides/index-hooks.md) Lua script for record lexicons | +| `index_hook` | string | no | [Index hook](../guides/index-hooks.md) Lua script for record lexicons | **Response**: `201 Created` (new) or `200 OK` (upsert) diff --git a/migrations/20260304000002_rename_on_index_script_to_index_hook.sql b/migrations/20260304000002_rename_on_index_script_to_index_hook.sql new file mode 100644 index 0000000..89702ee --- /dev/null +++ b/migrations/20260304000002_rename_on_index_script_to_index_hook.sql @@ -0,0 +1 @@ +ALTER TABLE lexicons RENAME COLUMN on_index_script TO index_hook; diff --git a/src/admin/lexicons.rs b/src/admin/lexicons.rs index b9c7485..9c926aa 100644 --- a/src/admin/lexicons.rs +++ b/src/admin/lexicons.rs @@ -57,7 +57,7 @@ pub(super) async fn upload_lexicon( body.target_collection.clone(), action.clone(), body.script.clone(), - body.on_index_script.clone(), + body.index_hook.clone(), ) .map_err(|e| AppError::BadRequest(format!("failed to parse lexicon: {e}")))?; @@ -66,8 +66,8 @@ pub(super) async fn upload_lexicon( crate::lua::validate_script(script).map_err(AppError::BadRequest)?; } - // Validate on_index_script if provided - if let Some(ref script) = body.on_index_script { + // Validate index_hook if provided + if let Some(ref script) = body.index_hook { crate::lua::validate_script(script).map_err(AppError::BadRequest)?; } @@ -77,7 +77,7 @@ pub(super) async fn upload_lexicon( // Upsert into database let row: (i32,) = sqlx::query_as( r#" - INSERT INTO lexicons (id, lexicon_json, backfill, target_collection, action, script, on_index_script, source) + INSERT INTO lexicons (id, lexicon_json, backfill, target_collection, action, script, index_hook, source) VALUES ($1, $2, $3, $4, $5, $6, $7, 'manual') ON CONFLICT (id) DO UPDATE SET lexicon_json = EXCLUDED.lexicon_json, @@ -85,7 +85,7 @@ pub(super) async fn upload_lexicon( target_collection = EXCLUDED.target_collection, action = EXCLUDED.action, script = EXCLUDED.script, - on_index_script = EXCLUDED.on_index_script, + index_hook = EXCLUDED.index_hook, source = 'manual', revision = lexicons.revision + 1, updated_at = NOW() @@ -98,7 +98,7 @@ pub(super) async fn upload_lexicon( .bind(&body.target_collection) .bind(action_str) .bind(&body.script) - .bind(&body.on_index_script) + .bind(&body.index_hook) .fetch_one(&state.db) .await .map_err(|e| AppError::Internal(format!("failed to upsert lexicon: {e}")))?; @@ -112,7 +112,7 @@ pub(super) async fn upload_lexicon( body.target_collection, action, body.script, - body.on_index_script.clone(), + body.index_hook.clone(), ) .map_err(|e| AppError::Internal(format!("failed to re-parse lexicon: {e}")))?; let is_record = parsed.lexicon_type == LexiconType::Record; @@ -143,7 +143,7 @@ pub(super) async fn upload_lexicon( detail: serde_json::json!({ "revision": revision, "has_script": has_script, - "has_on_index_script": body.on_index_script.is_some(), + "has_index_hook": body.index_hook.is_some(), "source": "manual", }), }, @@ -167,7 +167,7 @@ pub(super) async fn list_lexicons( #[allow(clippy::type_complexity)] let rows: Vec<(String, i32, Value, bool, Option, Option, Option, Option, String, Option, Option>, chrono::DateTime, chrono::DateTime)> = sqlx::query_as( - "SELECT id, revision, lexicon_json, backfill, action, target_collection, script, on_index_script, source, authority_did, last_fetched_at, created_at, updated_at FROM lexicons ORDER BY id", + "SELECT id, revision, lexicon_json, backfill, action, target_collection, script, index_hook, source, authority_did, last_fetched_at, created_at, updated_at FROM lexicons ORDER BY id", ) .fetch_all(&state.db) .await @@ -184,7 +184,7 @@ pub(super) async fn list_lexicons( action, target_collection, script, - on_index_script, + index_hook, source, authority_did, last_fetched_at, @@ -210,7 +210,7 @@ pub(super) async fn list_lexicons( action, target_collection, has_script: script.is_some(), - has_on_index_script: on_index_script.is_some(), + has_index_hook: index_hook.is_some(), source, authority_did, last_fetched_at, @@ -234,7 +234,7 @@ pub(super) async fn get_lexicon( #[allow(clippy::type_complexity)] let row: Option<(String, i32, Value, bool, Option, Option, Option, Option, String, Option, Option>, chrono::DateTime, chrono::DateTime)> = sqlx::query_as( - "SELECT id, revision, lexicon_json, backfill, action, target_collection, script, on_index_script, source, authority_did, last_fetched_at, created_at, updated_at FROM lexicons WHERE id = $1", + "SELECT id, revision, lexicon_json, backfill, action, target_collection, script, index_hook, source, authority_did, last_fetched_at, created_at, updated_at FROM lexicons WHERE id = $1", ) .bind(&id) .fetch_optional(&state.db) @@ -249,7 +249,7 @@ pub(super) async fn get_lexicon( action, target_collection, script, - on_index_script, + index_hook, source, authority_did, last_fetched_at, @@ -280,8 +280,8 @@ pub(super) async fn get_lexicon( "target_collection": target_collection, "has_script": has_script, "script": script, - "has_on_index_script": on_index_script.is_some(), - "on_index_script": on_index_script, + "has_index_hook": index_hook.is_some(), + "index_hook": index_hook, "source": source, "authority_did": authority_did, "last_fetched_at": last_fetched_at, diff --git a/src/admin/types.rs b/src/admin/types.rs index 28921bb..22756cc 100644 --- a/src/admin/types.rs +++ b/src/admin/types.rs @@ -14,7 +14,7 @@ pub(super) struct LexiconSummary { pub(super) action: Option, pub(super) target_collection: Option, pub(super) has_script: bool, - pub(super) has_on_index_script: bool, + pub(super) has_index_hook: bool, pub(super) source: String, pub(super) authority_did: Option, pub(super) last_fetched_at: Option>, @@ -33,7 +33,7 @@ pub(super) struct UploadLexiconBody { pub(super) target_collection: Option, pub(super) action: Option, pub(super) script: Option, - pub(super) on_index_script: Option, + pub(super) index_hook: Option, } fn default_backfill() -> bool { diff --git a/src/lexicon.rs b/src/lexicon.rs index adfa67a..51edd51 100644 --- a/src/lexicon.rs +++ b/src/lexicon.rs @@ -82,7 +82,7 @@ pub struct ParsedLexicon { /// Optional Lua script that replaces the built-in handler. pub script: Option, /// Optional Lua script that runs when a record in this collection is indexed. - pub on_index_script: Option, + pub index_hook: Option, } impl ParsedLexicon { @@ -93,7 +93,7 @@ impl ParsedLexicon { target_collection: Option, action: ProcedureAction, script: Option, - on_index_script: Option, + index_hook: Option, ) -> Result { let id = raw .get("id") @@ -137,7 +137,7 @@ impl ParsedLexicon { target_collection, action, script, - on_index_script, + index_hook, }) } } @@ -173,7 +173,7 @@ impl LexiconRegistry { Option, Option, )> = sqlx::query_as( - "SELECT id, lexicon_json, revision, target_collection, action, script, on_index_script FROM lexicons", + "SELECT id, lexicon_json, revision, target_collection, action, script, index_hook FROM lexicons", ) .fetch_all(db) .await @@ -183,7 +183,7 @@ impl LexiconRegistry { inner.clear(); let mut loaded = 0u32; - for (id, json, revision, target_collection, action_str, script, on_index_script) in rows { + for (id, json, revision, target_collection, action_str, script, index_hook) in rows { let action = match ProcedureAction::from_optional_str(action_str.as_deref()) { Ok(a) => a, Err(e) => { @@ -197,7 +197,7 @@ impl LexiconRegistry { target_collection, action, script, - on_index_script, + index_hook, ) { Ok(parsed) => { inner.insert(id, parsed); @@ -261,12 +261,10 @@ impl LexiconRegistry { .collect() } - /// Get the on_index_script for a record-type lexicon by its collection NSID. - pub async fn get_on_index_script(&self, collection: &str) -> Option { + /// Get the index_hook for a record-type lexicon by its collection NSID. + pub async fn get_index_hook(&self, collection: &str) -> Option { let inner = self.inner.read().await; - inner - .get(collection) - .and_then(|lex| lex.on_index_script.clone()) + inner.get(collection).and_then(|lex| lex.index_hook.clone()) } /// Return the total count of registered lexicons. @@ -674,11 +672,11 @@ mod tests { } // ----------------------------------------------------------------------- - // on_index_script + // index_hook // ----------------------------------------------------------------------- #[test] - fn parse_preserves_on_index_script() { + fn parse_preserves_index_hook() { let parsed = ParsedLexicon::parse( record_lexicon_json(), 1, @@ -688,11 +686,11 @@ mod tests { Some("function handle() end".into()), ) .unwrap(); - assert_eq!(parsed.on_index_script, Some("function handle() end".into())); + assert_eq!(parsed.index_hook, Some("function handle() end".into())); } #[test] - fn parse_on_index_script_none_by_default() { + fn parse_index_hook_none_by_default() { let parsed = ParsedLexicon::parse( record_lexicon_json(), 1, @@ -702,11 +700,11 @@ mod tests { None, ) .unwrap(); - assert!(parsed.on_index_script.is_none()); + assert!(parsed.index_hook.is_none()); } #[tokio::test] - async fn registry_get_on_index_script_returns_script() { + async fn registry_get_index_hook_returns_script() { let reg = LexiconRegistry::new(); let parsed = ParsedLexicon::parse( record_lexicon_json(), @@ -719,14 +717,12 @@ mod tests { .unwrap(); reg.upsert(parsed).await; - let script = reg - .get_on_index_script("games.gamesgamesgamesgames.game") - .await; + let script = reg.get_index_hook("games.gamesgamesgamesgames.game").await; assert_eq!(script, Some("function handle() log('hook') end".into())); } #[tokio::test] - async fn registry_get_on_index_script_returns_none_when_absent() { + async fn registry_get_index_hook_returns_none_when_absent() { let reg = LexiconRegistry::new(); let parsed = ParsedLexicon::parse( record_lexicon_json(), @@ -739,16 +735,14 @@ mod tests { .unwrap(); reg.upsert(parsed).await; - let script = reg - .get_on_index_script("games.gamesgamesgamesgames.game") - .await; + let script = reg.get_index_hook("games.gamesgamesgamesgames.game").await; assert!(script.is_none()); } #[tokio::test] - async fn registry_get_on_index_script_returns_none_for_unknown() { + async fn registry_get_index_hook_returns_none_for_unknown() { let reg = LexiconRegistry::new(); - let script = reg.get_on_index_script("nonexistent").await; + let script = reg.get_index_hook("nonexistent").await; assert!(script.is_none()); } } diff --git a/src/tap.rs b/src/tap.rs index cb466eb..b29de6a 100644 --- a/src/tap.rs +++ b/src/tap.rs @@ -481,9 +481,7 @@ async fn handle_record_event(state: &AppState, record: &TapRecordEvent) { .await; // Fire index hook if configured. - if let Some(script) = - state.lexicons.get_on_index_script(&record.collection).await - { + if let Some(script) = state.lexicons.get_index_hook(&record.collection).await { let hook_state = state.clone(); let hook_lexicon_id = record.collection.clone(); let hook_uri = uri.clone(); @@ -553,9 +551,7 @@ async fn handle_record_event(state: &AppState, record: &TapRecordEvent) { .await; // Fire index hook if configured. - if let Some(script) = - state.lexicons.get_on_index_script(&record.collection).await - { + if let Some(script) = state.lexicons.get_index_hook(&record.collection).await { let hook_state = state.clone(); let hook_lexicon_id = record.collection.clone(); let hook_uri = uri.clone(); diff --git a/web/src/app/(dashboard)/lexicons/[id]/lexicon-detail.tsx b/web/src/app/(dashboard)/lexicons/[id]/lexicon-detail.tsx index fc28689..08b4bbc 100644 --- a/web/src/app/(dashboard)/lexicons/[id]/lexicon-detail.tsx +++ b/web/src/app/(dashboard)/lexicons/[id]/lexicon-detail.tsx @@ -75,9 +75,9 @@ export default function LexiconDetailPage() { setLuaText(lex.script ?? ""); setOriginalLua(lex.script ?? ""); } - setHookText(lex.on_index_script ?? ""); - setOriginalHook(lex.on_index_script ?? ""); - setShowHookEditor(!!lex.on_index_script); + setHookText(lex.index_hook ?? ""); + setOriginalHook(lex.index_hook ?? ""); + setShowHookEditor(!!lex.index_hook); }) .catch((e) => setError(e instanceof Error ? e.message : String(e))); }, [getToken, id]); @@ -101,7 +101,7 @@ export default function LexiconDetailPage() { lexicon_json: lexiconJson, backfill: lexicon.backfill, script: luaText || undefined, - on_index_script: hookText || undefined, + index_hook: hookText || undefined, }); load(); } catch (e: unknown) { @@ -155,7 +155,7 @@ export default function LexiconDetailPage() { lexicon.lexicon_type === "query" || lexicon.lexicon_type === "procedure"; const isRecord = lexicon.lexicon_type === "record"; - const showHook = isRecord && (showHookEditor || !!lexicon.on_index_script); + const showHook = isRecord && (showHookEditor || !!lexicon.index_hook); return (
diff --git a/web/src/lib/api.ts b/web/src/lib/api.ts index 96be10a..f8b8a53 100644 --- a/web/src/lib/api.ts +++ b/web/src/lib/api.ts @@ -110,7 +110,7 @@ export function uploadLexicon( target_collection?: string action?: string script?: string - on_index_script?: string + index_hook?: string } ) { return apiFetch<{ id: string; revision: number }>("/admin/lexicons", getToken, { diff --git a/web/src/types/lexicons.ts b/web/src/types/lexicons.ts index 0a2bc20..2e6394d 100644 --- a/web/src/types/lexicons.ts +++ b/web/src/types/lexicons.ts @@ -6,7 +6,7 @@ export interface LexiconSummary { action: string | null target_collection: string | null has_script: boolean - has_on_index_script: boolean + has_index_hook: boolean source: string authority_did: string | null last_fetched_at: string | null @@ -17,5 +17,5 @@ export interface LexiconSummary { export interface LexiconDetail extends LexiconSummary { lexicon_json: Record script: string | null - on_index_script: string | null + index_hook: string | null }