1use std::hash::{Hash, Hasher};
2use std::sync::Arc;
3use std::time::{Duration, SystemTime};
4
5use teaql_core::{
6 AggregationCacheOptions, DeleteCommand, Entity, Expr, InsertCommand, Record, RecoverCommand,
7 RelationAggregate, SelectQuery, SmartList, SortDirection, UpdateCommand, Value,
8};
9
10use crate::{
11 CheckObjectStatus, ContinuousPageCursor, DataServiceError, EntityDataServiceBehavior,
12 PurposedSelectQuery, RawAuditEvent, RuntimeError, clear_record_status, mark_record_status,
13};
14
15use super::{
16 AggregationCacheBackend, ContextDataService, EntityDataService, InMemoryAggregationCache,
17 UserContextMetadata, helpers::*,
18};
19
20#[derive(Debug, Clone)]
21struct ContinuousPageExecution {
22 query_key: String,
23 direction: SortDirection,
24 page_size: u64,
25 original_offset: u64,
26 ttl_seconds: u64,
27 optimized: bool,
28 seek_cursor_id: Option<String>,
29}
30
31impl<'a, E> EntityDataService<'a, E>
32where
33 E: teaql_data_service::QueryExecutor
34 + teaql_data_service::MutationExecutor
35 + Send
36 + Sync
37 + 'static,
38{
39 pub(super) fn query_behavior(
40 &self,
41 entity: &str,
42 ) -> Option<Arc<dyn EntityDataServiceBehavior>> {
43 self.data_service
44 .metadata
45 .context
46 .entity_data_service_behavior(entity)
47 }
48
49 pub(super) fn behavior(&self) -> Option<Arc<dyn EntityDataServiceBehavior>> {
50 self.data_service
51 .metadata
52 .context
53 .entity_data_service_behavior(&self.entity)
54 }
55
56 pub fn entity(&self) -> &str {
57 &self.entity
58 }
59
60 pub fn select(&self) -> SelectQuery {
61 SelectQuery::new(self.entity.clone())
62 }
63
64 pub fn insert_command(&self) -> InsertCommand {
65 InsertCommand::new(self.entity.clone())
66 }
67
68 fn enforce_insert_policy(&self, command: &mut InsertCommand) -> Result<(), RuntimeError> {
69 if let Some(policy) = self.data_service.metadata.context.request_policy.as_ref() {
70 policy.enforce_insert(self.data_service.metadata.context, command)?;
71 }
72 Ok(())
73 }
74
75 fn enforce_update_policy(&self, command: &mut UpdateCommand) -> Result<(), RuntimeError> {
76 if let Some(policy) = self.data_service.metadata.context.request_policy.as_ref() {
77 policy.enforce_update(self.data_service.metadata.context, command)?;
78 }
79 Ok(())
80 }
81
82 fn enforce_delete_policy(&self, command: &mut DeleteCommand) -> Result<(), RuntimeError> {
83 if let Some(policy) = self.data_service.metadata.context.request_policy.as_ref() {
84 policy.enforce_delete(self.data_service.metadata.context, command)?;
85 }
86 Ok(())
87 }
88
89 fn enforce_recover_policy(&self, command: &mut RecoverCommand) -> Result<(), RuntimeError> {
90 if let Some(policy) = self.data_service.metadata.context.request_policy.as_ref() {
91 policy.enforce_recover(self.data_service.metadata.context, command)?;
92 }
93 Ok(())
94 }
95
96 fn prepare_select_query(&self, query: &SelectQuery) -> Result<SelectQuery, RuntimeError> {
97 let mut query = query.clone();
98
99 let mut full_trace = self.trace_context.clone();
100 full_trace.extend(query.trace_chain);
101 query.trace_chain = full_trace;
102
103 if let Some(behavior) = self.query_behavior(&query.entity) {
104 behavior.before_select(self.data_service.metadata.context, &mut query)?;
105 }
106 if let Some(policy) = self.data_service.metadata.context.request_policy.as_ref() {
107 policy.enforce_select(self.data_service.metadata.context, &mut query)?;
108 }
109 if !query.relations.is_empty() {
112 if let Some(descriptor) = self.data_service.metadata.context.entity(&query.entity) {
113 for load in &query.relations {
114 if let Some(relation) = descriptor.relation_by_name(&load.name) {
115 if !query.projection.contains(&relation.local_key) {
116 query.projection.push(relation.local_key.clone());
117 }
118 }
119 }
120 }
121 }
122 Ok(query)
123 }
124
125 pub fn prepare_insert_command(
126 &self,
127 command: &InsertCommand,
128 ) -> Result<InsertCommand, RuntimeError> {
129 let mut command = command.clone();
130 if let Some(behavior) = self.behavior() {
131 behavior.before_insert(self.data_service.metadata.context, &mut command)?;
132 }
133 self.enforce_insert_policy(&mut command)?;
134
135 let entity = self
136 .data_service
137 .metadata
138 .context
139 .require_entity(&command.entity)?;
140 if let Some(id_property) = entity.id_property() {
141 let needs_id = !command.values.contains_key(&id_property.name)
142 || is_unassigned_id(command.values.get(&id_property.name));
143 if needs_id {
144 let id = self
145 .data_service
146 .metadata
147 .context
148 .next_id(&command.entity)?;
149 command
150 .values
151 .insert(id_property.name.clone(), Value::U64(id));
152 }
153 }
154 ensure_initial_version(&mut command.values, entity);
155 mark_record_status(&mut command.values, CheckObjectStatus::Create);
156 let check_result = self
157 .data_service
158 .metadata
159 .context
160 .check_and_fix_record(&command.entity, &mut command.values);
161 clear_record_status(&mut command.values);
162 check_result?;
163
164 Ok(command)
165 }
166
167 pub fn update_command(&self, id: impl Into<Value>) -> UpdateCommand {
168 UpdateCommand::new(self.entity.clone(), id)
169 }
170
171 pub fn prepare_update_command(
172 &self,
173 command: &UpdateCommand,
174 ) -> Result<UpdateCommand, RuntimeError> {
175 let mut command = command.clone();
176 if let Some(behavior) = self.behavior() {
177 behavior.before_update(self.data_service.metadata.context, &mut command)?;
178 }
179 self.enforce_update_policy(&mut command)?;
180
181 Ok(command)
182 }
183
184 pub fn delete_command(&self, id: impl Into<Value>) -> DeleteCommand {
185 DeleteCommand::new(self.entity.clone(), id)
186 }
187
188 pub fn recover_command(&self, id: impl Into<Value>, expected_version: i64) -> RecoverCommand {
189 RecoverCommand::new(self.entity.clone(), id, expected_version)
190 }
191
192 pub(crate) async fn fetch_all_internal(
193 &self,
194 query: &SelectQuery,
195 ) -> Result<Vec<Record>, DataServiceError<E::Error>> {
196 let query = self
197 .prepare_select_query(query)
198 .map_err(DataServiceError::Runtime)?;
199 let query = query
200 .prepare_for_list()
201 .map_err(|message| DataServiceError::Runtime(RuntimeError::Graph(message)))?;
202 self.fetch_prepared_all(&query).await
203 }
204
205 async fn prepare_continuous_page(
206 &self,
207 query: SelectQuery,
208 ) -> (SelectQuery, Option<ContinuousPageExecution>) {
209 let Some(options) = query.continuous_page_fetch.as_ref() else {
210 self.data_service
211 .metadata
212 .context
213 .observe_continuous_page("DISABLED", None);
214 return (query, None);
215 };
216 let Some(slice) = query.slice.as_ref() else {
217 self.data_service
218 .metadata
219 .context
220 .observe_continuous_page("OFFSET_FALLBACK:INVALID_SLICE", None);
221 return (query, None);
222 };
223 let Some(page_size) = slice.limit else {
224 self.data_service
225 .metadata
226 .context
227 .observe_continuous_page("OFFSET_FALLBACK:INVALID_SLICE", None);
228 return (query, None);
229 };
230 if query.partition_by.is_some()
231 || !query.aggregates.is_empty()
232 || !query.group_by.is_empty()
233 {
234 self.data_service
235 .metadata
236 .context
237 .observe_continuous_page("OFFSET_FALLBACK:UNSUPPORTED_QUERY_SHAPE", None);
238 return (query, None);
239 }
240 if query.order_by.len() != 1
241 || query.order_by[0].field != "id"
242 || query.order_by[0].expr.is_some()
243 {
244 self.data_service
245 .metadata
246 .context
247 .observe_continuous_page("OFFSET_FALLBACK:ORDER_NOT_SEEKABLE_ID", None);
248 return (query, None);
249 }
250 let direction = query.order_by[0].direction;
251 let query_key = self.continuous_page_query_key(&query, &options.namespace);
252 let execution = ContinuousPageExecution {
253 query_key: query_key.clone(),
254 direction,
255 page_size,
256 original_offset: slice.offset,
257 ttl_seconds: options.ttl_seconds,
258 optimized: false,
259 seek_cursor_id: None,
260 };
261 if slice.offset == 0 {
262 self.data_service
263 .metadata
264 .context
265 .observe_continuous_page("OFFSET_FALLBACK:FIRST_PAGE", None);
266 return (query, Some(execution));
267 }
268 let cursor = match self
269 .data_service
270 .metadata
271 .context
272 .continuous_page_cursor_store()
273 .get(&query_key, slice.offset)
274 .await
275 {
276 Ok(Some(cursor)) => cursor,
277 Ok(None) => {
278 self.data_service
279 .metadata
280 .context
281 .observe_continuous_page("OFFSET_FALLBACK:CACHE_MISS", None);
282 return (query, Some(execution));
283 }
284 Err(_) => {
285 self.data_service
286 .metadata
287 .context
288 .observe_continuous_page("OFFSET_FALLBACK:STORE_UNAVAILABLE", None);
289 return (query, Some(execution));
290 }
291 };
292 if cursor.entity != query.entity
293 || cursor.direction != direction
294 || cursor.page_size != page_size
295 || cursor.next_offset != slice.offset
296 || cursor.expires_at <= SystemTime::now()
297 {
298 self.data_service
299 .metadata
300 .context
301 .observe_continuous_page("OFFSET_FALLBACK:CURSOR_INVALID", None);
302 return (query, Some(execution));
303 }
304 let mut optimized = query;
305 optimized.slice.as_mut().expect("validated slice").offset = 0;
306 optimized = optimized.and_filter(match direction {
307 SortDirection::Asc => Expr::gt("id", cursor.boundary.clone()),
308 SortDirection::Desc => Expr::lt("id", cursor.boundary.clone()),
309 });
310 let seek_cursor_id = cursor.cursor_id;
311 self.data_service
312 .metadata
313 .context
314 .observe_continuous_page("CURSOR_SEEK", Some(seek_cursor_id.clone()));
315 (
316 optimized,
317 Some(ContinuousPageExecution {
318 optimized: true,
319 seek_cursor_id: Some(seek_cursor_id),
320 ..execution
321 }),
322 )
323 }
324
325 async fn register_continuous_page(
326 &self,
327 execution: &Option<ContinuousPageExecution>,
328 rows: &[Record],
329 ) {
330 let Some(execution) = execution else { return };
331 if rows.len() as u64 != execution.page_size {
332 return;
333 }
334 let Some(boundary) = rows.last().and_then(|row| row.get("id")).cloned() else {
335 return;
336 };
337 let cursor_id = format!(
338 "cpg_{:x}",
339 SystemTime::now()
340 .duration_since(SystemTime::UNIX_EPOCH)
341 .unwrap_or_default()
342 .as_nanos()
343 );
344 let cursor = ContinuousPageCursor {
345 cursor_id,
346 query_key: execution.query_key.clone(),
347 entity: self.entity.clone(),
348 direction: execution.direction,
349 boundary,
350 page_size: execution.page_size,
351 next_offset: execution.original_offset + rows.len() as u64,
352 expires_at: SystemTime::now() + Duration::from_secs(execution.ttl_seconds),
353 };
354 if self
355 .data_service
356 .metadata
357 .context
358 .continuous_page_cursor_store()
359 .put(cursor)
360 .await
361 .is_err()
362 {
363 self.data_service
364 .metadata
365 .context
366 .observe_continuous_page("OFFSET_FALLBACK:STORE_UNAVAILABLE", None);
367 } else if execution.optimized {
368 self.data_service
369 .metadata
370 .context
371 .observe_continuous_page("CURSOR_SEEK", execution.seek_cursor_id.clone());
372 } else {
373 self.data_service
374 .metadata
375 .context
376 .observe_continuous_page("OFFSET_FALLBACK:FIRST_PAGE", None);
377 }
378 }
379
380 fn continuous_page_query_key(&self, query: &SelectQuery, namespace: &str) -> String {
381 let mut normalized = query.clone();
382 if let Some(slice) = normalized.slice.as_mut() {
383 slice.offset = 0;
384 }
385 normalized.comment = None;
386 normalized.trace_chain.clear();
387 let mut hasher = std::collections::hash_map::DefaultHasher::new();
388 namespace.hash(&mut hasher);
389 format!("{normalized:?}").hash(&mut hasher);
390 self.data_service
391 .metadata
392 .context
393 .user_identifier()
394 .hash(&mut hasher);
395 format!("teaql:continuous-page:v1:{:016x}", hasher.finish())
396 }
397
398 pub(crate) async fn fetch_stream_internal(
402 &self,
403 query: &SelectQuery,
404 ) -> Result<
405 std::pin::Pin<
406 Box<
407 dyn futures_core::Stream<
408 Item = Result<teaql_data_service::StreamChunk, DataServiceError<E::Error>>,
409 > + '_,
410 >,
411 >,
412 DataServiceError<E::Error>,
413 >
414 where
415 E: teaql_data_service::StreamQueryExecutor,
416 {
417 let query = self
418 .prepare_select_query(query)
419 .map_err(DataServiceError::Runtime)?;
420 let query = query
421 .prepare_for_list()
422 .map_err(|message| DataServiceError::Runtime(RuntimeError::Graph(message)))?;
423
424 if !query.relations.is_empty()
425 || !query.child_enhancements.is_empty()
426 || !query.object_group_bys.is_empty()
427 {
428 return Err(DataServiceError::Runtime(RuntimeError::Graph(
429 "streaming relation or aggregate enhancement is not supported; stream a root query or use execute_for_list"
430 .to_owned(),
431 )));
432 }
433
434 let chunk_size = query
435 .stream_config
436 .as_ref()
437 .map(|c| c.chunk_size)
438 .unwrap_or(1000);
439
440 let final_comment = self
441 .data_service
442 .resolve_final_comment(&query.trace_chain, query.comment.clone());
443 let mut query = query.clone();
444 query.comment = final_comment;
445
446 let request = teaql_data_service::QueryRequest {
447 query: query.clone(),
448 trace_chain: query.trace_chain.clone(),
449 comment: query.comment.clone(),
450 };
451
452 let chunks = self.data_service.executor.query_stream(request, chunk_size);
453 use futures_util::StreamExt;
454 Ok(Box::pin(
455 chunks.map(|item| item.map_err(DataServiceError::Executor)),
456 ))
457 }
458
459 async fn fetch_prepared_all(
460 &self,
461 query: &SelectQuery,
462 ) -> Result<Vec<Record>, DataServiceError<E::Error>> {
463 let query = query
464 .clone()
465 .prepare_for_list()
466 .map_err(|message| DataServiceError::Runtime(RuntimeError::Graph(message)))?;
467 let (execution_query, continuous) = self.prepare_continuous_page(query).await;
468 let mut rows = self.fetch_prepared_query(&execution_query).await?;
469 self.enhance_object_group_bys_internal(
470 &mut rows,
471 &execution_query.object_group_bys,
472 &execution_query.trace_chain,
473 )
474 .await?;
475 self.enhance_child_queries_internal(
476 &mut rows,
477 &execution_query.child_enhancements,
478 &execution_query.trace_chain,
479 )
480 .await?;
481 self.enhance_query_relations_internal(&mut rows, &execution_query)
482 .await?;
483 self.register_continuous_page(&continuous, &rows).await;
484 Ok(rows)
485 }
486
487 async fn fetch_prepared_query(
488 &self,
489 query: &SelectQuery,
490 ) -> Result<Vec<Record>, DataServiceError<E::Error>> {
491 let final_comment = self
492 .data_service
493 .resolve_final_comment(&query.trace_chain, query.comment.clone());
494 let mut query = query.clone();
495 query.comment = final_comment;
496 if let Some(options) = query.aggregation_cache.filter(|options| options.enabled) {
497 if let Some(cache) = self
498 .data_service
499 .metadata
500 .context
501 .get_resource::<Arc<dyn AggregationCacheBackend>>()
502 {
503 return self
504 .fetch_prepared_query_with_cache(&query, options, cache.as_ref())
505 .await;
506 }
507 if let Some(cache) = self
508 .data_service
509 .metadata
510 .context
511 .get_resource::<InMemoryAggregationCache>()
512 {
513 return self
514 .fetch_prepared_query_with_cache(&query, options, cache)
515 .await;
516 }
517 }
518 let request = teaql_data_service::QueryRequest {
519 query: query.clone(),
520 trace_chain: query.trace_chain.clone(),
521 comment: query.comment.clone(),
522 };
523 let res = self
524 .data_service
525 .executor
526 .query(request)
527 .await
528 .map_err(DataServiceError::Executor)?;
529 self.data_service
530 .metadata
531 .context
532 .record_metadata_log(&res.metadata);
533 Ok(res.rows)
534 }
535
536 async fn fetch_prepared_query_with_cache(
537 &self,
538 query: &SelectQuery,
539 options: AggregationCacheOptions,
540 cache: &dyn AggregationCacheBackend,
541 ) -> Result<Vec<Record>, DataServiceError<E::Error>> {
542 let key = aggregation_cache_key(
543 cache.namespace(),
544 &aggregation_cache_namespace(&query.entity),
545 query,
546 );
547 let scope = self.data_service.metadata.context.start_runtime_operation(
548 crate::RuntimeOperation::new("cache", format!("{}.aggregation.get", query.entity))
549 .attribute("teaql.cache.operation", "get"),
550 );
551 let result = scope
552 .run(async {
553 if let Some(rows) = cache.get(&key, options.cache_expired_millis) {
554 return Ok((rows, "hit"));
555 }
556 let request = teaql_data_service::QueryRequest {
557 query: query.clone(),
558 trace_chain: query.trace_chain.clone(),
559 comment: query.comment.clone(),
560 };
561 let provider_kind = std::any::type_name::<E>().to_owned();
562 let provider_scope = self.data_service.metadata.context.start_runtime_operation(
563 crate::RuntimeOperation::new("provider", format!("{provider_kind}.query"))
564 .attribute("teaql.provider.kind", provider_kind)
565 .attribute("teaql.provider.operation", "query"),
566 );
567 let provider_result = provider_scope
568 .run(self.data_service.executor.query(request))
569 .await;
570 let res = match provider_result {
571 Ok(value) => {
572 provider_scope.success(std::collections::BTreeMap::new());
573 value
574 }
575 Err(error) => {
576 provider_scope.failure("data_service_error");
577 return Err(DataServiceError::Executor(error));
578 }
579 };
580 self.data_service
581 .metadata
582 .context
583 .record_metadata_log(&res.metadata);
584 let rows = res.rows;
585 cache.put(key, rows.clone());
586 Ok((rows, "miss"))
587 })
588 .await;
589 match result {
590 Ok((rows, cache_result)) => {
591 scope.success(std::collections::BTreeMap::from([(
592 "teaql.cache.result".to_owned(),
593 crate::RuntimeAttributeValue::from(cache_result),
594 )]));
595 Ok(rows)
596 }
597 Err(error) => {
598 scope.failure("cache_load_error");
599 Err(error)
600 }
601 }
602 }
603
604 pub(crate) async fn fetch_all_with_relation_aggregates_internal(
605 &self,
606 query: &SelectQuery,
607 relation_aggregates: &[RelationAggregate],
608 ) -> Result<Vec<Record>, DataServiceError<E::Error>> {
609 let query = self
610 .prepare_select_query(query)
611 .map_err(DataServiceError::Runtime)?;
612
613 let mut rows = self.fetch_prepared_all(&query).await?;
614 self.enhance_relation_aggregates_internal(
615 &mut rows,
616 relation_aggregates,
617 query.aggregation_cache,
618 &query.trace_chain,
619 )
620 .await?;
621 Ok(rows)
622 }
623
624 pub(crate) async fn fetch_smart_list_internal(
625 &self,
626 query: &SelectQuery,
627 ) -> Result<SmartList<Record>, DataServiceError<E::Error>> {
628 let query = self
629 .prepare_select_query(query)
630 .map_err(DataServiceError::Runtime)?;
631
632 self.data_service.fetch_smart_list(&query).await
633 }
634
635 pub(crate) async fn fetch_smart_list_with_relation_aggregates_internal(
636 &self,
637 query: &SelectQuery,
638 relation_aggregates: &[RelationAggregate],
639 ) -> Result<SmartList<Record>, DataServiceError<E::Error>> {
640 self.fetch_all_with_relation_aggregates_internal(query, relation_aggregates)
641 .await
642 .map(SmartList::from)
643 }
644
645 pub(crate) async fn fetch_entities_internal<T>(
646 &self,
647 query: &SelectQuery,
648 ) -> Result<SmartList<T>, DataServiceError<E::Error>>
649 where
650 T: Entity,
651 {
652 let query = self
653 .prepare_select_query(query)
654 .map_err(DataServiceError::Runtime)?;
655
656 self.data_service.fetch_entities(&query).await
657 }
658
659 pub(crate) async fn fetch_entities_with_relation_aggregates_internal<T>(
660 &self,
661 query: &SelectQuery,
662 relation_aggregates: &[RelationAggregate],
663 ) -> Result<SmartList<T>, DataServiceError<E::Error>>
664 where
665 T: Entity,
666 {
667 self.fetch_all_with_relation_aggregates_internal(query, relation_aggregates)
668 .await?
669 .into_iter()
670 .map(|record| {
671 let mut entity = T::from_record(record)?;
672 let root = crate::EntityRoot::default();
673 entity.on_loaded(&root as &dyn std::any::Any);
674 Ok(entity)
675 })
676 .collect::<Result<Vec<_>, _>>()
677 .map(SmartList::from)
678 .map_err(DataServiceError::Entity)
679 }
680
681 pub(crate) async fn fetch_enhanced_entities_with_relation_aggregates_internal<T>(
682 &self,
683 query: &SelectQuery,
684 relation_aggregates: &[RelationAggregate],
685 ) -> Result<SmartList<T>, DataServiceError<E::Error>>
686 where
687 T: Entity,
688 {
689 let query = self
690 .prepare_select_query(query)
691 .map_err(DataServiceError::Runtime)?;
692
693 let mut rows = self.fetch_prepared_all(&query).await?;
694 self.enhance_relation_aggregates_internal(
695 &mut rows,
696 relation_aggregates,
697 query.aggregation_cache,
698 &query.trace_chain,
699 )
700 .await?;
701 self.enhance_relations_internal(&mut rows).await?;
702 rows.into_iter()
703 .map(|record| {
704 let mut entity = T::from_record(record)?;
705 let root = crate::EntityRoot::default();
706 entity.on_loaded(&root as &dyn std::any::Any);
707 Ok(entity)
708 })
709 .collect::<Result<Vec<_>, _>>()
710 .map(SmartList::from)
711 .map_err(DataServiceError::Entity)
712 }
713
714 pub(crate) async fn fetch_enhanced_entities_internal<T>(
715 &self,
716 query: &SelectQuery,
717 ) -> Result<SmartList<T>, DataServiceError<E::Error>>
718 where
719 T: Entity,
720 {
721 let query = self
722 .prepare_select_query(query)
723 .map_err(DataServiceError::Runtime)?;
724
725 let mut rows = self.fetch_prepared_all(&query).await?;
726 self.enhance_relations_internal(&mut rows).await?;
727 let root = self
728 .data_service
729 .metadata
730 .context
731 .get_resource::<crate::EntityRoot>()
732 .cloned();
733 rows.into_iter()
734 .map(|record| {
735 let mut entity = T::from_record(record)?;
736 if let Some(ref root) = root {
737 entity.on_loaded(root as &dyn std::any::Any);
738 }
739 Ok(entity)
740 })
741 .collect::<Result<Vec<_>, _>>()
742 .map(SmartList::from)
743 .map_err(DataServiceError::Entity)
744 }
745
746 #[doc(hidden)]
747 pub async fn fetch_all(
748 &self,
749 query: &PurposedSelectQuery,
750 ) -> Result<Vec<Record>, DataServiceError<E::Error>> {
751 self.fetch_all_internal(query.as_query()).await
752 }
753
754 #[doc(hidden)]
755 pub async fn fetch_stream(
756 &self,
757 query: &PurposedSelectQuery,
758 ) -> Result<
759 std::pin::Pin<
760 Box<
761 dyn futures_core::Stream<
762 Item = Result<teaql_data_service::StreamChunk, DataServiceError<E::Error>>,
763 > + '_,
764 >,
765 >,
766 DataServiceError<E::Error>,
767 >
768 where
769 E: teaql_data_service::StreamQueryExecutor,
770 {
771 self.fetch_stream_internal(query.as_query()).await
772 }
773
774 #[doc(hidden)]
775 pub async fn fetch_smart_list(
776 &self,
777 query: &PurposedSelectQuery,
778 ) -> Result<SmartList<Record>, DataServiceError<E::Error>> {
779 self.fetch_smart_list_internal(query.as_query()).await
780 }
781
782 #[doc(hidden)]
783 pub async fn fetch_smart_list_with_relation_aggregates(
784 &self,
785 query: &PurposedSelectQuery,
786 relation_aggregates: &[RelationAggregate],
787 ) -> Result<SmartList<Record>, DataServiceError<E::Error>> {
788 self.fetch_smart_list_with_relation_aggregates_internal(
789 query.as_query(),
790 relation_aggregates,
791 )
792 .await
793 }
794
795 #[doc(hidden)]
796 pub async fn fetch_entities<T>(
797 &self,
798 query: &PurposedSelectQuery,
799 ) -> Result<SmartList<T>, DataServiceError<E::Error>>
800 where
801 T: Entity,
802 {
803 self.fetch_entities_internal(query.as_query()).await
804 }
805
806 #[doc(hidden)]
807 pub async fn fetch_enhanced_entities<T>(
808 &self,
809 query: &PurposedSelectQuery,
810 ) -> Result<SmartList<T>, DataServiceError<E::Error>>
811 where
812 T: Entity,
813 {
814 self.fetch_enhanced_entities_internal(query.as_query())
815 .await
816 }
817
818 #[doc(hidden)]
819 pub async fn fetch_enhanced_entities_with_relation_aggregates<T>(
820 &self,
821 query: &PurposedSelectQuery,
822 relation_aggregates: &[RelationAggregate],
823 ) -> Result<SmartList<T>, DataServiceError<E::Error>>
824 where
825 T: Entity,
826 {
827 self.fetch_enhanced_entities_with_relation_aggregates_internal(
828 query.as_query(),
829 relation_aggregates,
830 )
831 .await
832 }
833
834 pub(crate) async fn insert_internal(
835 &self,
836 command: &InsertCommand,
837 ) -> Result<u64, DataServiceError<E::Error>> {
838 let command = self
839 .prepare_insert_command(command)
840 .map_err(DataServiceError::Runtime)?;
841 self.execute_prepared_insert_with_comment(command, self.trace_context.clone())
842 .await
843 }
844
845 pub(crate) async fn update_internal(
846 &self,
847 command: &UpdateCommand,
848 ) -> Result<u64, DataServiceError<E::Error>> {
849 let command = self
850 .prepare_update_command(command)
851 .map_err(DataServiceError::Runtime)?;
852 self.execute_prepared_update_with_comment(command, self.trace_context.clone())
853 .await
854 }
855
856 pub(crate) async fn delete_internal(
857 &self,
858 command: &DeleteCommand,
859 ) -> Result<u64, DataServiceError<E::Error>> {
860 self.delete_scoped_internal(command, self.trace_context.clone())
861 .await
862 }
863
864 pub(crate) async fn delete_scoped_internal(
865 &self,
866 command: &DeleteCommand,
867 trace_chain: Vec<teaql_core::TraceNode>,
868 ) -> Result<u64, DataServiceError<E::Error>> {
869 let mut command = command.clone();
870 command.trace_chain = trace_chain.clone();
871 if let Some(behavior) = self.behavior() {
872 behavior
873 .before_delete(self.data_service.metadata.context, &mut command)
874 .map_err(DataServiceError::Runtime)?;
875 }
876 self.enforce_delete_policy(&mut command)
877 .map_err(DataServiceError::Runtime)?;
878
879 let old_values =
880 self.fetch_current_event_row(&command.entity, &command.id, trace_chain.clone())?;
881 let affected = self.data_service.delete(&command).await?;
882
883 let mut event = RawAuditEvent::deleted_with_old_values(
884 command.entity,
885 command.id,
886 command.expected_version,
887 old_values,
888 );
889 event.trace_chain = trace_chain;
890 self.emit_event(event).map_err(DataServiceError::Runtime)?;
891 Ok(affected)
892 }
893
894 pub(crate) async fn recover_internal(
895 &self,
896 command: &RecoverCommand,
897 ) -> Result<u64, DataServiceError<E::Error>> {
898 let mut command = command.clone();
899 command.trace_chain = self.trace_context.clone();
900 if let Some(behavior) = self.behavior() {
901 behavior
902 .before_recover(self.data_service.metadata.context, &mut command)
903 .map_err(DataServiceError::Runtime)?;
904 }
905 self.enforce_recover_policy(&mut command)
906 .map_err(DataServiceError::Runtime)?;
907 let old_values = self.fetch_current_event_row(
908 &command.entity,
909 &command.id,
910 command.trace_chain.clone(),
911 )?;
912 let affected = self.data_service.recover(&command).await?;
913 let event = RawAuditEvent::recovered_with_old_values(
914 command.entity,
915 command.id,
916 command.expected_version,
917 old_values,
918 );
919 self.emit_event(event).map_err(DataServiceError::Runtime)?;
920 Ok(affected)
921 }
922
923 fn emit_event(&self, event: RawAuditEvent) -> Result<(), RuntimeError> {
924 self.data_service.metadata.context.send_event(event)
925 }
926
927 #[allow(dead_code)]
928 pub(super) async fn execute_prepared_insert(
929 &self,
930 command: InsertCommand,
931 ) -> Result<u64, DataServiceError<E::Error>> {
932 self.execute_prepared_insert_with_comment(command, Vec::new())
933 .await
934 }
935
936 pub(super) async fn execute_prepared_insert_with_comment(
937 &self,
938 mut command: InsertCommand,
939 trace_chain: Vec<teaql_core::TraceNode>,
940 ) -> Result<u64, DataServiceError<E::Error>> {
941 command.trace_chain = trace_chain.clone();
942 let affected = self.data_service.insert(&command).await?;
943 let mut event = RawAuditEvent::created(command.entity, command.values);
944 event.trace_chain = trace_chain;
945 self.emit_event(event).map_err(DataServiceError::Runtime)?;
946 Ok(affected)
947 }
948
949 pub(super) async fn execute_prepared_batch_insert(
950 &self,
951 command: teaql_core::BatchInsertCommand,
952 ) -> Result<u64, DataServiceError<E::Error>> {
953 if command.batch_values.is_empty() {
954 return Ok(0);
955 }
956 let affected = self.data_service.batch_insert(&command).await?;
957
958 let entity = command.entity.clone();
959 for (i, values) in command.batch_values.into_iter().enumerate() {
960 let mut event = RawAuditEvent::created(entity.clone(), values);
961 if i < command.trace_chains.len() {
962 event.trace_chain = command.trace_chains[i].clone();
963 }
964 self.emit_event(event).map_err(DataServiceError::Runtime)?;
965 }
966 Ok(affected)
967 }
968
969 #[allow(dead_code)]
970 pub(super) async fn execute_prepared_update(
971 &self,
972 command: UpdateCommand,
973 ) -> Result<u64, DataServiceError<E::Error>> {
974 self.execute_prepared_update_with_comment(command, Vec::new())
975 .await
976 }
977
978 pub(super) async fn execute_prepared_update_with_comment(
979 &self,
980 mut command: UpdateCommand,
981 trace_chain: Vec<teaql_core::TraceNode>,
982 ) -> Result<u64, DataServiceError<E::Error>> {
983 command.trace_chain = trace_chain.clone();
984
985 let mut old_values = command.old_values.clone();
986 let needs_fetch = match &old_values {
987 Some(snapshot) => !command.values.keys().all(|k| snapshot.contains_key(k)),
988 None => true,
989 };
990 if needs_fetch {
991 old_values =
992 self.fetch_current_event_row(&command.entity, &command.id, trace_chain.clone())?;
993 }
994
995 let affected = self.data_service.update(&command).await?;
996 let updated_fields = command.values.keys().cloned().collect();
997 let mut values = command.values.clone();
998 values.insert("id".to_owned(), command.id.clone());
999 if let Some(version) = command.expected_version {
1000 values.insert("version".to_owned(), Value::I64(version + 1));
1001 }
1002 let mut new_values = old_values.clone().unwrap_or_default();
1003 for (field, value) in &values {
1004 new_values.insert(field.clone(), value.clone());
1005 }
1006 let mut event = RawAuditEvent::updated_with_old_values(
1007 command.entity,
1008 values,
1009 old_values,
1010 new_values,
1011 updated_fields,
1012 );
1013 event.trace_chain = trace_chain;
1014 self.emit_event(event).map_err(DataServiceError::Runtime)?;
1015 Ok(affected)
1016 }
1017
1018 pub(super) async fn execute_prepared_batch_update(
1019 &self,
1020 command: teaql_core::BatchUpdateCommand,
1021 ) -> Result<u64, DataServiceError<E::Error>> {
1022 if command.batch_values.is_empty() {
1023 return Ok(0);
1024 }
1025 let affected = self.data_service.batch_update(&command).await?;
1026
1027 let entity = command.entity.clone();
1028 for (i, values) in command.batch_values.into_iter().enumerate() {
1029 let mut full_values = values.clone();
1030 full_values.insert("id".to_owned(), command.batch_ids[i].clone());
1031 if let Some(Some(version)) = command.batch_expected_versions.get(i) {
1032 full_values.insert("version".to_owned(), teaql_core::Value::I64(*version + 1));
1033 }
1034
1035 let old_values = command.batch_old_values.get(i).cloned().unwrap_or(None);
1036 let mut new_values = old_values.clone().unwrap_or_default();
1037 for (field, value) in &full_values {
1038 new_values.insert(field.clone(), value.clone());
1039 }
1040
1041 let mut event = RawAuditEvent::updated_with_old_values(
1042 entity.clone(),
1043 full_values,
1044 old_values,
1045 new_values,
1046 command.update_fields.clone(),
1047 );
1048 if i < command.trace_chains.len() {
1049 event.trace_chain = command.trace_chains[i].clone();
1050 }
1051 self.emit_event(event).map_err(DataServiceError::Runtime)?;
1052 }
1053 Ok(affected)
1054 }
1055
1056 fn fetch_current_event_row(
1057 &self,
1058 _entity: &str,
1059 _id: &Value,
1060 _trace_chain: Vec<teaql_core::TraceNode>,
1061 ) -> Result<Option<Record>, DataServiceError<E::Error>> {
1062 Ok(None)
1065 }
1066
1067 pub(crate) fn scoped_data_service_internal(&self, entity: String) -> EntityDataService<'a, E> {
1068 EntityDataService {
1069 entity,
1070 data_service: ContextDataService {
1071 metadata: UserContextMetadata {
1072 context: self.data_service.metadata.context,
1073 },
1074 executor: self.data_service.executor,
1075 },
1076 trace_context: Vec::new(),
1077 }
1078 }
1079}