valence_core/runtime/
factory.rs1use std::sync::Arc;
9
10use serde_json::Value;
11
12use crate::actor::Actor;
13use crate::actor_policy::{ActorJsonPolicy, ActorTrust};
14use crate::error::{Error, Result};
15use crate::ports::actor::{ActorFactory, JsonActorFactory};
16use crate::ports::endpoints::DatabaseEndpointResolver;
17use crate::ports::secrets::SecretProvider;
18use crate::router::DatabaseRouter;
19use crate::runtime::{Valence, ValenceBuilder};
20use valence_telemetry::TelemetrySink;
21
22pub trait ValenceFactory: Send + Sync + 'static {
24 fn build(&self, actor_json: &Value) -> Result<Valence>;
29}
30
31#[derive(Clone)]
33pub struct RouterValenceFactoryConfig {
34 pub default_backend_key: String,
36 pub telemetry_sink: Option<Arc<dyn TelemetrySink>>,
38 pub secret_provider: Option<Arc<dyn SecretProvider>>,
40 pub actor_factory: Option<Arc<dyn ActorFactory>>,
42 pub endpoint_resolver: Option<Arc<dyn DatabaseEndpointResolver>>,
44 pub actor_json_policy: Option<Arc<dyn ActorJsonPolicy>>,
46 pub actor_trust: ActorTrust,
48}
49
50impl RouterValenceFactoryConfig {
51 #[must_use]
53 pub fn new(default_backend_key: impl Into<String>) -> Self {
54 Self {
55 default_backend_key: default_backend_key.into(),
56 telemetry_sink: None,
57 secret_provider: None,
58 actor_factory: None,
59 endpoint_resolver: None,
60 actor_json_policy: None,
61 actor_trust: ActorTrust::External,
62 }
63 }
64
65 #[must_use]
67 pub fn actor_json_policy(mut self, policy: impl ActorJsonPolicy + 'static) -> Self {
68 self.actor_json_policy = Some(Arc::new(policy));
69 self
70 }
71}
72
73#[derive(Clone)]
75pub struct RouterValenceFactory {
76 router: Arc<DatabaseRouter>,
77 config: RouterValenceFactoryConfig,
78}
79
80impl RouterValenceFactory {
81 #[must_use]
83 pub fn new(router: Arc<DatabaseRouter>, config: RouterValenceFactoryConfig) -> Self {
84 Self { router, config }
85 }
86
87 pub fn arc(
89 router: Arc<DatabaseRouter>,
90 config: RouterValenceFactoryConfig,
91 ) -> Arc<dyn ValenceFactory> {
92 Arc::new(Self::new(router, config))
93 }
94}
95
96impl ValenceFactory for RouterValenceFactory {
97 fn build(&self, actor_json: &Value) -> Result<Valence> {
98 if let Some(policy) = &self.config.actor_json_policy {
99 policy.validate(self.config.actor_trust, actor_json)?;
100 }
101
102 let actor_factory = self
103 .config
104 .actor_factory
105 .clone()
106 .unwrap_or_else(|| Arc::new(JsonActorFactory));
107 let _actor_ctx = actor_factory.build(actor_json)?;
108
109 let actor: Actor = serde_json::from_value(actor_json.clone()).map_err(|e| {
112 Error::Validation(format!("actor_json is not a valid valence::Actor: {e}"))
113 })?;
114
115 let mut builder = ValenceBuilder::new()
116 .database_router(Arc::clone(&self.router))
117 .default_backend_key(self.config.default_backend_key.clone())
118 .actor_factory(actor_factory)
119 .with_actor(actor);
120
121 if let Some(sink) = &self.config.telemetry_sink {
122 builder = builder.telemetry_sink(Arc::clone(sink));
123 }
124 if let Some(secrets) = &self.config.secret_provider {
125 builder = builder.secret_provider(Arc::clone(secrets));
126 }
127 if let Some(endpoints) = &self.config.endpoint_resolver {
128 builder = builder.endpoint_resolver(Arc::clone(endpoints));
129 }
130
131 builder.build()
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138 use crate::actor::Actor;
139 use crate::backend::DatabaseBackend;
140 use async_trait::async_trait;
141 use std::sync::Arc;
142
143 #[derive(Debug)]
144 struct MockBackend;
145
146 #[async_trait]
147 impl DatabaseBackend for MockBackend {
148 fn engine_id(&self) -> &'static str {
149 "mem"
150 }
151
152 fn capabilities(&self) -> crate::backend::BackendCapabilities {
153 crate::backend::BackendCapabilities::mem()
154 }
155
156 async fn execute_compiled_query(
157 &self,
158 _compiled: &crate::compiled_query::CompiledQuery,
159 ) -> crate::error::Result<Vec<serde_json::Value>> {
160 Ok(vec![])
161 }
162
163 async fn get_record(
164 &self,
165 _table: &str,
166 _id: &str,
167 ) -> crate::error::Result<Option<serde_json::Value>> {
168 Ok(None)
169 }
170
171 async fn create_record(
172 &self,
173 _table: &str,
174 _content: serde_json::Value,
175 ) -> crate::error::Result<serde_json::Value> {
176 Ok(serde_json::json!({}))
177 }
178
179 async fn update_record(
180 &self,
181 _table: &str,
182 _id: &str,
183 _content: serde_json::Value,
184 ) -> crate::error::Result<serde_json::Value> {
185 Ok(serde_json::json!({}))
186 }
187
188 async fn upsert_record(
189 &self,
190 _table: &str,
191 _id: &str,
192 _content: serde_json::Value,
193 ) -> crate::error::Result<serde_json::Value> {
194 Ok(serde_json::json!({}))
195 }
196
197 async fn delete_record(&self, _table: &str, _id: &str) -> crate::error::Result<()> {
198 Ok(())
199 }
200
201 async fn relate_edge(
202 &self,
203 _from: &crate::record_id::RecordId,
204 _edge_table: &str,
205 _to: &crate::record_id::RecordId,
206 ) -> crate::error::Result<()> {
207 Ok(())
208 }
209
210 async fn unrelate_edge(
211 &self,
212 _from: &crate::record_id::RecordId,
213 _edge_table: &str,
214 _to: &crate::record_id::RecordId,
215 ) -> crate::error::Result<()> {
216 Ok(())
217 }
218
219 async fn get_edge_targets(
220 &self,
221 _from: &crate::record_id::RecordId,
222 _edge_table: &str,
223 ) -> crate::error::Result<Vec<crate::record_id::RecordId>> {
224 Ok(vec![])
225 }
226 }
227
228 fn factory() -> RouterValenceFactory {
229 let valence = Valence::builder()
230 .add_backend("default", Arc::new(MockBackend))
231 .build()
232 .expect("build");
233 let router = Arc::clone(valence.database_router());
234 RouterValenceFactory::new(router, RouterValenceFactoryConfig::new("default"))
235 }
236
237 #[test]
238 fn build_binds_user_actor_from_json() {
239 let f = factory();
240 let actor_json = serde_json::to_value(Actor::User {
241 user_id: "u1".into(),
242 })
243 .expect("json");
244 let v = f.build(&actor_json).expect("build");
245 assert_eq!(v.actor().user_id(), Some("u1"));
246 }
247
248 #[test]
249 fn build_rejects_invalid_actor_json() {
250 let f = factory();
251 let err = f
252 .build(&serde_json::json!({"kind": "not-an-actor"}))
253 .expect_err("invalid");
254 assert!(err.to_string().contains("actor_json"));
255 }
256
257 #[test]
258 fn build_rejects_external_system_when_policy_installed() {
259 use crate::actor_policy::RejectExternalSystemActor;
260 let valence = Valence::builder()
261 .add_backend("default", Arc::new(MockBackend))
262 .build()
263 .expect("build");
264 let router = Arc::clone(valence.database_router());
265 let config =
266 RouterValenceFactoryConfig::new("default").actor_json_policy(RejectExternalSystemActor);
267 let f = RouterValenceFactory::new(router, config);
268 let system = serde_json::to_value(Actor::System {
269 operation: "x".into(),
270 })
271 .expect("json");
272 let err = f.build(&system).expect_err("system");
273 assert!(err.to_string().contains("System"));
274 }
275}