Skip to main content

ahri_tre_app/service/
governance.rs

1use super::*;
2use crate::StudyDuoRestrictionInput;
3
4/// Resolved governance scope and restriction values. Internal restriction-row
5/// keys do not become separately addressable public resources.
6pub struct GovernedAssetDuo {
7    pub study: StudyRecord,
8    pub catalog: AssetVersionCatalog,
9    pub version: ahri_tre_types::AssetVersionRecord,
10    pub effective: bool,
11    pub restrictions: Vec<EffectiveAssetVersionDuoRestriction>,
12}
13
14impl AppService {
15    async fn asset_duo_scope(
16        &self,
17        selector: ahri_tre_protocol::asset::AssetSelector,
18        version: Option<&str>,
19    ) -> Result<GovernedAssetDuo, AppError> {
20        let (study, catalog, pinned) = self.resolve_catalogue_asset(selector).await?;
21        let version = self.catalogue_version(&catalog, pinned, version)?;
22        Ok(GovernedAssetDuo {
23            study,
24            catalog,
25            version,
26            effective: false,
27            restrictions: Vec::new(),
28        })
29    }
30
31    pub async fn list_governed_asset_duo(
32        &self,
33        request: ahri_tre_protocol::asset::AssetDuoListRequest,
34    ) -> Result<GovernedAssetDuo, AppError> {
35        let mut result = self
36            .asset_duo_scope(request.asset, request.version.as_deref())
37            .await?;
38        result.effective = request.effective;
39        result.restrictions = if request.effective {
40            self.list_effective_asset_version_duo_restrictions(
41                ListEffectiveAssetVersionDuoRestrictionsRequest {
42                    study_id: result.study.study_id,
43                    version_id: result.version.version_id,
44                },
45            )
46            .await?
47            .restrictions
48        } else {
49            self.list_asset_version_duo_restrictions(ListAssetVersionDuoRestrictionsRequest {
50                version_id: result.version.version_id,
51            })
52            .await?
53            .restrictions
54            .into_iter()
55            .map(explicit_duo_value)
56            .collect()
57        };
58        Ok(result)
59    }
60
61    pub async fn clear_governed_asset_duo(
62        &self,
63        request: ahri_tre_protocol::asset::AssetDuoClearRequest,
64    ) -> Result<GovernedAssetDuo, AppError> {
65        let mut result = self
66            .asset_duo_scope(request.asset, request.version.as_deref())
67            .await?;
68        result.restrictions = self
69            .clear_asset_version_duo_restrictions(ClearAssetVersionDuoRestrictionsRequest {
70                version_id: result.version.version_id,
71            })
72            .await?
73            .restrictions
74            .into_iter()
75            .map(explicit_duo_value)
76            .collect();
77        Ok(result)
78    }
79
80    pub async fn replace_governed_asset_duo(
81        &self,
82        request: ahri_tre_protocol::asset::AssetDuoReplaceRequest,
83    ) -> Result<GovernedAssetDuo, AppError> {
84        let mut result = self
85            .asset_duo_scope(request.asset, request.version.as_deref())
86            .await?;
87        let restrictions = request
88            .restrictions
89            .into_iter()
90            .map(|input| crate::AssetVersionDuoRestrictionInput {
91                code: input.code,
92                duo_id: input.duo_id,
93                qualifier_ontology_namespace: input.qualifier_ontology_namespace,
94                qualifier_ontology_id: input.qualifier_ontology_id,
95                qualifier_text: input.qualifier_text,
96                qualifier_date: input.qualifier_date,
97                qualifier_integer: input.qualifier_integer,
98            })
99            .collect();
100        result.restrictions = self
101            .replace_asset_version_duo_restrictions(ReplaceAssetVersionDuoRestrictionsRequest {
102                version_id: result.version.version_id,
103                restrictions,
104            })
105            .await?
106            .restrictions
107            .into_iter()
108            .map(explicit_duo_value)
109            .collect();
110        Ok(result)
111    }
112}
113
114fn explicit_duo_value(
115    restriction: ahri_tre_types::AssetVersionDuoRestrictionRecord,
116) -> EffectiveAssetVersionDuoRestriction {
117    EffectiveAssetVersionDuoRestriction {
118        source: AssetDuoRestrictionSource::AssetVersion,
119        duo_id: restriction.duo_id,
120        qualifier_ontology_namespace: restriction.qualifier_ontology_namespace,
121        qualifier_ontology_id: restriction.qualifier_ontology_id,
122        qualifier_text: restriction.qualifier_text,
123        qualifier_date: restriction.qualifier_date,
124        qualifier_integer: restriction.qualifier_integer,
125    }
126}
127
128impl AppService {
129    async fn governance_study(
130        &self,
131        selector: ahri_tre_protocol::study::StudySelector,
132    ) -> Result<StudyRecord, AppError> {
133        self.resolve_study_selector(self.catalogue_study_selector(selector)?, "governance Study")
134            .await
135    }
136
137    pub async fn grant_governed_study_access(
138        &self,
139        request: ahri_tre_protocol::study::StudyAccessGrantRequest,
140    ) -> Result<(StudyRecord, StudyAccessGrant), AppError> {
141        let study = self.governance_study(request.study).await?;
142        let grant = self
143            .grant_study_access(GrantStudyAccessRequest {
144                study_id: study.study_id,
145                target_user_id: request.target_user,
146            })
147            .await?;
148        Ok((study, grant))
149    }
150
151    /// Resolve every supplied constraint against the retained Datastore before
152    /// changing the Study's Domain memberships.
153    pub async fn add_governed_study_domain(
154        &self,
155        request: ahri_tre_protocol::study::AddStudyDomainRequest,
156    ) -> Result<StudyRegistration, AppError> {
157        self.add_study_domain(AddStudyDomainRequest {
158            study: self.catalogue_study_selector(request.study)?,
159            domain: self.catalogue_domain_selector(request.domain)?,
160        })
161        .await
162    }
163}
164
165impl AppService {
166    pub async fn list_governed_study_access(
167        &self,
168        request: ahri_tre_protocol::study::StudyAccessListRequest,
169    ) -> Result<(StudyRecord, crate::StudyAccessGrantList), AppError> {
170        let study = self.governance_study(request.study).await?;
171        let result = self
172            .list_study_access_grants(ListStudyAccessGrantsRequest {
173                study_id: study.study_id,
174            })
175            .await?;
176        Ok((study, result))
177    }
178}
179
180impl AppService {
181    pub async fn revoke_governed_study_access(
182        &self,
183        request: ahri_tre_protocol::study::StudyAccessRevokeRequest,
184    ) -> Result<(StudyRecord, crate::StudyAccessRevocation), AppError> {
185        let study = self.governance_study(request.study).await?;
186        let result = self
187            .revoke_study_access(RevokeStudyAccessRequest {
188                study_id: study.study_id,
189                target_user_id: request.target_user,
190            })
191            .await?;
192        Ok((study, result))
193    }
194}
195
196impl AppService {
197    pub async fn list_governed_study_custodians(
198        &self,
199        request: ahri_tre_protocol::study::StudyCustodiansListRequest,
200    ) -> Result<(StudyRecord, crate::StudyCustodianList), AppError> {
201        let study = self.governance_study(request.study).await?;
202        let result = self
203            .list_study_custodians(ListStudyCustodiansRequest {
204                study_id: study.study_id,
205            })
206            .await?;
207        Ok((study, result))
208    }
209}
210
211impl AppService {
212    pub async fn add_governed_delegate_custodian(
213        &self,
214        request: ahri_tre_protocol::study::StudyCustodianMutationRequest,
215    ) -> Result<(StudyRecord, crate::StudyCustodian), AppError> {
216        let study = self.governance_study(request.study).await?;
217        let result = self
218            .add_delegate_custodian(AddDelegateCustodianRequest {
219                study_id: study.study_id,
220                target_user_id: request.target_user,
221            })
222            .await?;
223        Ok((study, result))
224    }
225}
226
227impl AppService {
228    pub async fn transfer_governed_primary_custodian(
229        &self,
230        request: ahri_tre_protocol::study::StudyCustodianMutationRequest,
231    ) -> Result<(StudyRecord, crate::StudyPrimaryCustodianshipTransfer), AppError> {
232        let study = self.governance_study(request.study).await?;
233        let result = self
234            .transfer_primary_custodianship(TransferPrimaryCustodianshipRequest {
235                study_id: study.study_id,
236                target_user_id: request.target_user,
237            })
238            .await?;
239        Ok((study, result))
240    }
241}
242
243impl AppService {
244    pub async fn remove_governed_delegate_custodian(
245        &self,
246        request: ahri_tre_protocol::study::StudyCustodianMutationRequest,
247    ) -> Result<(StudyRecord, crate::StudyDelegateCustodianRemoval), AppError> {
248        let study = self.governance_study(request.study).await?;
249        let result = self
250            .remove_delegate_custodian(RemoveDelegateCustodianRequest {
251                study_id: study.study_id,
252                target_user_id: request.target_user,
253            })
254            .await?;
255        Ok((study, result))
256    }
257}
258
259impl AppService {
260    pub async fn list_governed_study_duo(
261        &self,
262        request: ahri_tre_protocol::study::StudyDuoListRequest,
263    ) -> Result<(StudyRecord, crate::StudyDuoRestrictionList), AppError> {
264        let study = self.governance_study(request.study).await?;
265        let result = self
266            .list_study_duo_restrictions(ListStudyDuoRestrictionsRequest {
267                study_id: study.study_id,
268            })
269            .await?;
270        Ok((study, result))
271    }
272}
273
274impl AppService {
275    pub async fn replace_governed_study_duo(
276        &self,
277        request: ahri_tre_protocol::study::StudyDuoReplaceRequest,
278    ) -> Result<(StudyRecord, crate::StudyDuoRestrictionReplacement), AppError> {
279        let study = self.governance_study(request.study).await?;
280        let result = self
281            .replace_study_duo_restrictions(ReplaceStudyDuoRestrictionsRequest {
282                study_id: study.study_id,
283                restrictions: request
284                    .restrictions
285                    .into_iter()
286                    .map(study_duo_input)
287                    .collect(),
288            })
289            .await?;
290        Ok((study, result))
291    }
292}
293
294fn study_duo_input(
295    input: ahri_tre_protocol::study::StudyDuoRestrictionInput,
296) -> StudyDuoRestrictionInput {
297    StudyDuoRestrictionInput {
298        code: input.code,
299        duo_id: input.duo_id,
300        qualifier_ontology_namespace: input.qualifier_ontology_namespace,
301        qualifier_ontology_id: input.qualifier_ontology_id,
302        qualifier_text: input.qualifier_text,
303        qualifier_date: input.qualifier_date,
304        qualifier_integer: input.qualifier_integer,
305    }
306}