valence_core/ownership/service/
mod.rs1mod gates;
7mod helpers;
8mod read;
9mod write;
10
11pub use helpers::{
12 normalize_record_id_for_ownership, owner_ref_from_ownership_json, ownership_colocate_enabled,
13 ownership_get_join_enabled, ownership_unified_fetch_enabled, parse_owner_kind,
14 skip_ownership_for_table, OwnerDataSummary, OwnerSchemaRowCount, OwnershipGateStatus,
15 RecordOwnershipBundle, SKIP_OWNERSHIP_TABLES,
16};
17
18use std::sync::Arc;
19
20use crate::backend::DatabaseBackend;
21use crate::error::{Error, Result};
22use crate::runtime::Valence;
23use crate::schema::SchemaRegistry;
24
25use helpers::system_valence;
26
27pub struct OwnershipService;
29
30pub async fn ensure_lookup_indexes(v: &Valence) -> Result<()> {
32 let sys = system_valence(v);
33 let q = concat!(
34 "DEFINE INDEX IF NOT EXISTS idx_valence_data_ownership_model_status ",
35 "ON TABLE valence_data_ownership COLUMNS valence_model, status"
36 );
37 let compiled = crate::compiled_query::CompiledQuery::new(q.to_string(), vec![]);
38
39 let mut backends: Vec<Arc<dyn DatabaseBackend>> = Vec::new();
40
41 let mut push_unique = |backend: Arc<dyn DatabaseBackend>| {
42 if !backends.iter().any(|b| Arc::ptr_eq(b, &backend)) {
43 backends.push(backend);
44 }
45 };
46
47 if let Ok(backend) = sys.backend_for_table("valence_data_ownership") {
48 push_unique(backend);
49 }
50
51 if ownership_colocate_enabled() {
52 for table in SchemaRegistry::global().list_schemas() {
53 if skip_ownership_for_table(table) {
54 continue;
55 }
56 if let Ok(backend) = OwnershipService::ownership_backend(table, &sys) {
57 push_unique(backend);
58 }
59 }
60 }
61
62 for backend in backends {
63 backend
64 .execute_compiled_query(&compiled)
65 .await
66 .map_err(|e| Error::Database(e.to_string()))?;
67 }
68 Ok(())
69}
70
71#[cfg(test)]
72mod tests {
73 use super::helpers::{
74 normalize_record_id_for_ownership, owner_id_query_values, owner_ref_from_ownership_json,
75 parse_owner_kind, skip_ownership_for_table,
76 };
77 use crate::owner_ref::{OwnerKind, OwnerRef};
78 use serde_json::json;
79
80 #[test]
81 fn owner_id_query_values_matches_session_and_signup_user_ids() {
82 assert_eq!(
83 owner_id_query_values("user:abc-123", "user"),
84 vec!["user:abc-123".to_string(), "abc-123".to_string()]
85 );
86 assert_eq!(
87 owner_id_query_values("abc-123", "user"),
88 vec!["abc-123".to_string(), "user:abc-123".to_string()]
89 );
90 assert_eq!(
91 owner_id_query_values("service-bot", "service"),
92 vec!["service-bot".to_string()]
93 );
94 }
95
96 #[test]
97 fn parse_owner_kind_known_and_default() {
98 assert_eq!(parse_owner_kind("user"), OwnerKind::User);
99 assert_eq!(parse_owner_kind("account"), OwnerKind::Account);
100 assert_eq!(parse_owner_kind("application"), OwnerKind::Application);
101 assert_eq!(parse_owner_kind("service"), OwnerKind::Service);
102 assert_eq!(parse_owner_kind("anything_else"), OwnerKind::System);
103 }
104
105 #[test]
106 fn skip_ownership_for_platform_tables_only() {
107 assert!(skip_ownership_for_table("valence_data_ownership"));
108 assert!(skip_ownership_for_table("valence_ownership_transfer"));
109 assert!(!skip_ownership_for_table("counter"));
110 }
111
112 #[test]
113 fn owner_ref_from_ownership_json_extracts_fields() {
114 let v = json!({
115 "owner_id": "u1",
116 "owner_type": "user",
117 });
118 let r = owner_ref_from_ownership_json(&v).expect("parsed");
119 assert_eq!(r.owner_id, "u1");
120 assert_eq!(r.owner_kind, OwnerKind::User);
121 assert!(owner_ref_from_ownership_json(&json!({})).is_none());
122 }
123
124 #[test]
125 fn normalize_record_id_strips_table_prefix() {
126 assert_eq!(
127 normalize_record_id_for_ownership("counter:singleton"),
128 "singleton"
129 );
130 assert_eq!(normalize_record_id_for_ownership("bare-id"), "bare-id");
131 }
132
133 #[test]
134 fn normalize_record_id_keeps_composite_id_without_known_table() {
135 assert_eq!(
136 normalize_record_id_for_ownership("default-deployment:wiztop:0"),
137 "default-deployment:wiztop:0"
138 );
139 }
140
141 #[test]
142 fn owner_ref_round_trip_from_stored_json() {
143 let v = json!({"owner_id": "svc", "owner_type": "service"});
144 let r = owner_ref_from_ownership_json(&v).unwrap();
145 assert_eq!(
146 r,
147 OwnerRef {
148 owner_id: "svc".into(),
149 owner_kind: OwnerKind::Service,
150 }
151 );
152 }
153
154 #[test]
155 fn ownership_get_join_enabled_defaults_on() {
156 assert!(super::helpers::ownership_get_join_enabled());
157 }
158
159 #[test]
160 fn ownership_colocate_enabled_defaults_on() {
161 assert!(super::helpers::ownership_colocate_enabled());
162 }
163}