relay_knowledge/application/code_repository/
business_knowledge.rs1use crate::{
4 api::{ApiError, ApiMetadata, BusinessKnowledgeQueryResponse, RequestContext},
5 application::service::RelayKnowledgeService,
6 domain::{
7 BusinessKnowledgeQueryRequest, BusinessKnowledgeResolution, BusinessKnowledgeStatus,
8 FreshnessPolicy, GraphVersion,
9 },
10};
11
12use super::{
13 errors::storage_api_error,
14 repository::{ensure_worktree_overlay_matches_current_worktree, required_code_repository},
15 scope::{
16 active_index_matches_request, indexed_commit_for_selector,
17 latest_compatible_code_scope_status, resolved_code_scope_status,
18 },
19};
20
21impl RelayKnowledgeService {
22 pub async fn business_knowledge_query(
24 &self,
25 request: BusinessKnowledgeQueryRequest,
26 context: RequestContext,
27 ) -> Result<BusinessKnowledgeQueryResponse, ApiError> {
28 let store = self.store().await.map_err(storage_api_error)?;
29 let repository_status =
30 required_code_repository(store.as_ref(), &request.repository.repository).await?;
31 if request.freshness_policy == FreshnessPolicy::GraphOnly {
32 let graph_version = store
33 .current_graph_version()
34 .await
35 .map_err(storage_api_error)?;
36 let scope = crate::api::CodeRepositoryScopeMetadata::from_status(
37 &repository_status,
38 &request.repository,
39 request.repository.ref_selector.clone(),
40 );
41 return Ok(BusinessKnowledgeQueryResponse {
42 metadata: ApiMetadata::graph_only(&context, graph_version),
43 status: BusinessKnowledgeStatus {
44 repository_id: repository_status.repository_id,
45 source_scope: scope.scope_id.clone(),
46 resolved_commit_sha: scope.resolved_commit_sha.clone(),
47 projected_graph_version: GraphVersion::ZERO,
48 stale: true,
49 source_count: 0,
50 domain_count: 0,
51 term_count: 0,
52 mapping_count: 0,
53 last_error: Some("graph_only freshness policy selected".to_owned()),
54 },
55 scope,
56 request,
57 resolution: BusinessKnowledgeResolution::NotFound,
58 domains: Vec::new(),
59 terms: Vec::new(),
60 });
61 }
62
63 let requested_ref = request.repository.ref_selector.clone();
64 let mut request = request_at_indexed_ref(request, &repository_status).await?;
65 if requested_ref == "worktree" {
66 ensure_worktree_overlay_matches_current_worktree(
67 &store,
68 &repository_status,
69 &request.repository,
70 )
71 .await?;
72 }
73 let mut served_stale_scope = false;
74 let scoped_status =
75 match resolved_code_scope_status(&store, &repository_status, &request.repository).await
76 {
77 Ok(status) => status,
78 Err(error) if request.freshness_policy == FreshnessPolicy::AllowStale => {
79 if !active_index_matches_request(
80 &store,
81 &repository_status,
82 &request.repository,
83 )
84 .await?
85 {
86 return Err(error);
87 }
88 let Some(stale) =
89 latest_compatible_code_scope_status(&store, &request.repository).await?
90 else {
91 return Err(error);
92 };
93 let Some(commit) = stale.last_indexed_commit.clone() else {
94 return Err(error);
95 };
96 request.repository.ref_selector = commit;
97 served_stale_scope = true;
98 stale
99 }
100 Err(error) => return Err(error),
101 };
102 let source_scope = scoped_status.last_indexed_scope_id.clone().ok_or_else(|| {
103 ApiError::invalid_argument(format!(
104 "code repository '{}' does not have an indexed source scope",
105 scoped_status.alias
106 ))
107 })?;
108 request.repository.repository = repository_status.repository_id.clone();
109 if let Some(commit) = scoped_status.last_indexed_commit.clone() {
110 request.repository.ref_selector = commit;
111 }
112 let projection = store
113 .business_knowledge_projection_for_scope(source_scope, request.clone())
114 .await
115 .map_err(storage_api_error)?;
116 if request.freshness_policy == FreshnessPolicy::WaitUntilFresh
117 && (projection.status.stale || scoped_status.stale)
118 {
119 return Err(ApiError::invalid_argument(format!(
120 "business knowledge projection for repository '{}' scope '{}' is stale; run repo index before querying with wait_until_fresh",
121 repository_status.alias, projection.status.source_scope
122 )));
123 }
124 let graph_version = store
125 .current_graph_version()
126 .await
127 .map_err(storage_api_error)?;
128 let mut metadata = ApiMetadata::graph_only(&context, graph_version);
129 if projection.status.stale || scoped_status.stale || served_stale_scope {
130 metadata.stale = true;
131 }
132 let mut selector = request.repository.clone();
133 selector.path_filters = scoped_status.path_filters.clone();
134 selector.language_filters = scoped_status.language_filters.clone();
135 let mut scope = crate::api::CodeRepositoryScopeMetadata::from_status(
136 &scoped_status,
137 &selector,
138 requested_ref,
139 );
140 if served_stale_scope {
141 scope.stale = true;
142 }
143 let mut status = projection.status;
144 if scoped_status.stale || served_stale_scope {
145 status.stale = true;
146 }
147 Ok(BusinessKnowledgeQueryResponse {
148 metadata,
149 scope,
150 request,
151 status,
152 resolution: projection.resolution,
153 domains: projection.domains,
154 terms: projection.terms,
155 })
156 }
157}
158
159async fn request_at_indexed_ref(
160 mut request: BusinessKnowledgeQueryRequest,
161 status: &crate::domain::CodeRepositoryStatus,
162) -> Result<BusinessKnowledgeQueryRequest, ApiError> {
163 request.repository.ref_selector = indexed_commit_for_selector(
164 status,
165 &request.repository,
166 request.repository.ref_selector.clone(),
167 )
168 .await?;
169 Ok(request)
170}