Skip to main content

kmp_application/queries/
get_context.rs

1use kmp_domain::{
2    GraphNeighborhoodReader, KmpBundle, NeighborhoodRequest, NodeDetailReader, SnapshotSaveOptions,
3    SnapshotStore,
4};
5
6use crate::ApplicationError;
7pub use crate::queries::render_graph_bundle::RenderedContext;
8use crate::queries::{
9    ContextRenderOptions, QueryApplicationService, QueryTimingBreakdown, RehydrateSessionUseCase,
10    clamp_native_graph_traversal_depth, render_graph_bundle_with_options,
11};
12
13#[derive(Debug, Clone, PartialEq, Eq)]
14pub struct GetContextQuery {
15    pub root_node_id: String,
16    pub role: String,
17    pub depth: u32,
18    pub requested_scopes: Vec<String>,
19    pub render_options: ContextRenderOptions,
20}
21
22#[derive(Debug, Clone, PartialEq)]
23pub struct GetContextResult {
24    pub bundle: KmpBundle,
25    pub rendered: RenderedContext,
26    pub requested_scopes: Vec<String>,
27    pub served_at: std::time::SystemTime,
28    pub timing: Option<QueryTimingBreakdown>,
29}
30
31#[derive(Debug)]
32pub struct GetContextUseCase<G, D, S> {
33    rehydrate_session: RehydrateSessionUseCase<G, D, S>,
34}
35
36impl<G, D, S> GetContextUseCase<G, D, S>
37where
38    G: GraphNeighborhoodReader + Send + Sync,
39    D: NodeDetailReader + Send + Sync,
40    S: SnapshotStore + Send + Sync,
41{
42    pub fn new(rehydrate_session: RehydrateSessionUseCase<G, D, S>) -> Self {
43        Self { rehydrate_session }
44    }
45
46    pub async fn execute(
47        &self,
48        root_node_id: &str,
49        role: &str,
50        depth: u32,
51        requested_scopes: &[String],
52        render_options: &ContextRenderOptions,
53    ) -> Result<GetContextResult, ApplicationError> {
54        // The scopes were resolved before this query was built. Handing them to
55        // the reader is what lets a store narrow on the axis rather than the
56        // caller discarding what it should never have loaded.
57        let (bundle, timing) = self
58            .rehydrate_session
59            .execute_for(
60                &NeighborhoodRequest::new(root_node_id, clamp_native_graph_traversal_depth(depth))
61                    .with_scopes(requested_scopes.to_vec()),
62                role,
63                false,
64                SnapshotSaveOptions::default(),
65            )
66            .await?;
67        let rendered = render_graph_bundle_with_options(&bundle, render_options);
68
69        Ok(GetContextResult {
70            bundle,
71            rendered,
72            requested_scopes: requested_scopes.to_vec(),
73            served_at: std::time::SystemTime::now(),
74            timing: Some(timing),
75        })
76    }
77}
78
79impl<G, D, S> QueryApplicationService<G, D, S>
80where
81    G: GraphNeighborhoodReader + Send + Sync,
82    D: NodeDetailReader + Send + Sync,
83    S: SnapshotStore + Send + Sync,
84{
85    pub async fn get_context(
86        &self,
87        query: GetContextQuery,
88    ) -> Result<GetContextResult, ApplicationError> {
89        let rehydrate = RehydrateSessionUseCase::new(
90            std::sync::Arc::clone(&self.graph_reader),
91            std::sync::Arc::clone(&self.detail_reader),
92            std::sync::Arc::clone(&self.snapshot_store),
93            self.generator_version,
94        );
95
96        GetContextUseCase::new(rehydrate)
97            .execute(
98                &query.root_node_id,
99                &query.role,
100                query.depth,
101                &query.requested_scopes,
102                &query.render_options,
103            )
104            .await
105    }
106}