teaql_runtime/data_service/
context.rs1use std::sync::Arc;
2
3use teaql_core::{
4 DeleteCommand, Entity, InsertCommand, Record, RecoverCommand, SelectQuery, SmartList,
5 UpdateCommand,
6};
7
8use crate::{
9 ContextError, DataServiceError, GraphMutationPlan, GraphNode, RuntimeError, UserContext,
10};
11
12use super::{
13 AggregationCacheBackend, ContextDataService, EntityDataService, InMemoryAggregationCache,
14 RuntimeDataService, UserContextMetadata, helpers::invalidate_aggregation_cache_namespace,
15};
16
17impl UserContext {
18 pub fn data_service<E>(&self) -> Result<ContextDataService<'_, E>, ContextError>
19 where
20 E: teaql_data_service::QueryExecutor
21 + teaql_data_service::MutationExecutor
22 + Send
23 + Sync
24 + 'static,
25 {
26 if self.metadata.is_none() {
27 return Err(ContextError::MissingResource("metadata".to_owned()));
28 }
29
30 let executor = self.require_resource::<E>()?;
31 Ok(ContextDataService {
32 metadata: UserContextMetadata { context: self },
33 executor,
34 })
35 }
36
37 pub fn entity_data_service<E>(
38 &self,
39 entity: impl Into<String>,
40 ) -> Result<EntityDataService<'_, E>, ContextError>
41 where
42 E: teaql_data_service::QueryExecutor
43 + teaql_data_service::MutationExecutor
44 + Send
45 + Sync
46 + 'static,
47 {
48 let entity = entity.into();
49 if !self.has_entity_data_service(&entity) {
50 return Err(ContextError::MissingEntityDataService(entity));
51 }
52 Ok(EntityDataService {
53 entity,
54 data_service: self.data_service::<E>()?,
55 trace_context: Vec::new(),
56 })
57 }
58
59 pub fn register_executor<E>(&mut self, executor: E)
63 where
64 E: teaql_data_service::QueryExecutor
65 + teaql_data_service::MutationExecutor
66 + Send
67 + Sync
68 + 'static,
69 {
70 use std::sync::Arc;
71 self.insert_resource::<Arc<dyn crate::DynGraphSaver>>(Arc::new(
72 crate::entity_save::GraphSaverFor::<E>::new(),
73 ));
74 self.insert_resource(executor);
75 }
76}
77
78impl<'a, E> ContextDataService<'a, E>
79where
80 E: teaql_data_service::QueryExecutor
81 + teaql_data_service::MutationExecutor
82 + Send
83 + Sync
84 + 'static,
85{
86 fn data_service(&self) -> RuntimeDataService<'_, UserContextMetadata<'_>, E> {
87 RuntimeDataService::new(&self.metadata, self.executor)
88 }
89
90 pub async fn fetch_all(
91 &self,
92 mut query: SelectQuery,
93 ) -> Result<Vec<Record>, DataServiceError<E::Error>> {
94 let final_comment = self.resolve_final_comment(&query.trace_chain, query.comment.clone());
95 query.comment = final_comment;
96 self.data_service().fetch_all(&query).await
97 }
98
99 pub async fn fetch_smart_list(
100 &self,
101 query: &SelectQuery,
102 ) -> Result<SmartList<Record>, DataServiceError<E::Error>> {
103 self.data_service().fetch_smart_list(query).await
104 }
105
106 pub async fn fetch_entities<T>(
107 &self,
108 query: &SelectQuery,
109 ) -> Result<SmartList<T>, DataServiceError<E::Error>>
110 where
111 T: Entity,
112 {
113 self.data_service().fetch_entities(query).await
114 }
115
116 pub async fn fetch_enhanced_entities<T>(
117 &self,
118 query: &SelectQuery,
119 ) -> Result<SmartList<T>, DataServiceError<E::Error>>
120 where
121 T: Entity,
122 {
123 self.data_service().fetch_enhanced_entities(query).await
124 }
125
126 pub async fn insert(&self, command: &InsertCommand) -> Result<u64, DataServiceError<E::Error>> {
127 let affected = self.data_service().insert(command).await?;
128 self.invalidate_aggregation_cache_for(&command.entity);
129 Ok(affected)
130 }
131
132 pub async fn update(&self, command: &UpdateCommand) -> Result<u64, DataServiceError<E::Error>> {
133 let affected = self.data_service().update(command).await?;
134 self.invalidate_aggregation_cache_for(&command.entity);
135 Ok(affected)
136 }
137
138 pub async fn batch_insert(
139 &self,
140 command: &teaql_core::BatchInsertCommand,
141 ) -> Result<u64, DataServiceError<E::Error>> {
142 let affected = self.data_service().batch_insert(command).await?;
143 self.invalidate_aggregation_cache_for(&command.entity);
144 Ok(affected)
145 }
146
147 pub async fn batch_update(
148 &self,
149 command: &teaql_core::BatchUpdateCommand,
150 ) -> Result<u64, DataServiceError<E::Error>> {
151 let affected = self.data_service().batch_update(command).await?;
152 self.invalidate_aggregation_cache_for(&command.entity);
153 Ok(affected)
154 }
155
156 pub async fn delete(&self, command: &DeleteCommand) -> Result<u64, DataServiceError<E::Error>> {
157 let affected = self.data_service().delete(command).await?;
158 self.invalidate_aggregation_cache_for(&command.entity);
159 Ok(affected)
160 }
161
162 pub async fn recover(
163 &self,
164 command: &RecoverCommand,
165 ) -> Result<u64, DataServiceError<E::Error>> {
166 let affected = self.data_service().recover(command).await?;
167 self.invalidate_aggregation_cache_for(&command.entity);
168 Ok(affected)
169 }
170
171 pub(super) fn invalidate_aggregation_cache_for(&self, entity: &str) {
172 if let Some(cache) = self
173 .metadata
174 .context
175 .get_resource::<Arc<dyn AggregationCacheBackend>>()
176 {
177 invalidate_aggregation_cache_namespace(cache.as_ref(), entity);
178 }
179 if let Some(cache) = self
180 .metadata
181 .context
182 .get_resource::<InMemoryAggregationCache>()
183 {
184 invalidate_aggregation_cache_namespace(cache, entity);
185 }
186 }
187
188 pub(crate) fn resolve_final_comment(
189 &self,
190 trace_chain: &[teaql_core::TraceNode],
191 comment: Option<String>,
192 ) -> Option<String> {
193 let chain_str = if trace_chain.is_empty() {
194 None
195 } else {
196 let formatted = trace_chain
197 .iter()
198 .map(|n| {
199 format!(
200 "{}({}): {}",
201 n.entity_type,
202 n.entity_id
203 .map(|id| id.to_string())
204 .unwrap_or_else(|| "pending".to_owned()),
205 n.comment
206 )
207 })
208 .collect::<Vec<_>>()
209 .join(" -> ");
210 Some(formatted)
211 };
212
213 let business_comment = chain_str.or(comment);
214 let user_id = self
215 .metadata
216 .context
217 .user_identifier()
218 .map(|s| s.to_owned());
219
220 match (user_id, business_comment) {
221 (Some(user), Some(bus)) if !user.is_empty() && !bus.is_empty() => {
222 Some(format!("[{user}] {bus}"))
223 }
224 (Some(user), _) if !user.is_empty() => Some(format!("[{user}]")),
225 (_, Some(bus)) if !bus.is_empty() => Some(bus),
226 _ => None,
227 }
228 }
229}