Skip to main content

ahri_tre_app/
requests.rs

1use ahri_tre_core::{
2    ArchiveMode, DataFileSearchCursorAnchor, DataFileSearchPredicate, DatasetSearchCursorAnchor,
3    DatasetSearchPredicate, DeleteTarget, EntitySearchCursorAnchor, EntitySearchPredicate,
4    PredicateSetKind, RelationSearchCursorAnchor, RelationSearchPredicate, StudySearchCursorAnchor,
5    StudySearchPredicate, TagAttachmentTarget, VariableSearchCursorAnchor, VariableSearchPredicate,
6};
7use ahri_tre_types::{
8    AssetId, AssetRecord, AssetVersionDuoRestrictionRecord, AssetVersionEntityRecord,
9    AssetVersionRecord, AssetVersionRelationInstanceRecord, DataFileRecord, DatasetRecord,
10    DatasetVariableLinkRecord, DatasetVersionEntityRecord, DatasetVersionRelationInstanceRecord,
11    DatasetVersionWithdrawalRecord, DomainId, DomainRecord, EntityId, EntityInstanceRecord,
12    EntityRecord, EntityRelationId, EntityRelationRecord, KeyRole, NcName, NewAssetRecord,
13    NewAssetVersionDuoRestrictionRecord, NewAssetVersionRecord, NewDomainRecord, NewEntityRecord,
14    NewEntityRelationRecord, NewStudyDuoRestrictionRecord, NewStudyRecord, NewTransformationRecord,
15    RelationInstanceRecord, StudyAccessGrantRecord, StudyCustodianLinkRecord,
16    StudyDuoRestrictionRecord, StudyEntityInstanceRecord, StudyId, StudyRecord,
17    StudyRelationInstanceRecord, TagRecord, TransformationId, TransformationLineageRecord,
18    TransformationRecord, ValueTypeId, ValueTypeRecord, VariableId, VariableRecord, VersionId,
19    VocabularyId, VocabularyItemId, VocabularyItemRecord, VocabularyMappingRecord,
20    VocabularyRecord,
21};
22use chrono::{DateTime, NaiveDate, Utc};
23use serde::{Deserialize, Serialize};
24use std::io::Read;
25use std::path::PathBuf;
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
28pub enum DatastoreContentCountStatus {
29    Available,
30    Unsupported,
31    Unavailable,
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
35pub struct DatastoreContentCount {
36    pub status: DatastoreContentCountStatus,
37    pub count: Option<u64>,
38}
39
40impl DatastoreContentCount {
41    pub const fn available(count: u64) -> Self {
42        Self {
43            status: DatastoreContentCountStatus::Available,
44            count: Some(count),
45        }
46    }
47
48    pub const fn unsupported() -> Self {
49        Self {
50            status: DatastoreContentCountStatus::Unsupported,
51            count: None,
52        }
53    }
54
55    pub const fn unavailable() -> Self {
56        Self {
57            status: DatastoreContentCountStatus::Unavailable,
58            count: None,
59        }
60    }
61}
62
63#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
64pub struct DatastoreContentCounts {
65    pub studies: DatastoreContentCount,
66    pub assets: DatastoreContentCount,
67    pub datasets: DatastoreContentCount,
68    pub datafiles: DatastoreContentCount,
69    pub entities: DatastoreContentCount,
70    pub variables: DatastoreContentCount,
71    pub vocabularies: DatastoreContentCount,
72    pub transformations: DatastoreContentCount,
73}
74
75#[derive(Debug, Clone, Serialize, Deserialize)]
76pub struct FileToDatasetRequest {
77    pub classification: ahri_tre_types::IngestClassification,
78    pub study_id: StudyId,
79    pub dataset_asset_id: Option<AssetId>,
80    pub dataset_name: NcName,
81    pub dataset_version_id: Option<VersionId>,
82    pub input_path: String,
83    pub input_format: Option<String>,
84    pub header: Option<bool>,
85    pub delimiter: Option<char>,
86    #[serde(default)]
87    pub null_strings: Vec<String>,
88    pub json_format: Option<String>,
89    pub sheet: Option<String>,
90    pub description: Option<String>,
91    pub agent_instructions: Option<String>,
92    pub version_note: Option<String>,
93    pub transformation: NewTransformationRecord,
94    #[serde(default)]
95    pub variable_registrations: Vec<DatasetVariableRegistrationRequest>,
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize)]
99pub struct IngestFileRequest {
100    pub classification: ahri_tre_types::IngestClassification,
101    pub study_id: StudyId,
102    pub asset_id: Option<AssetId>,
103    pub asset_name: NcName,
104    pub version_id: Option<VersionId>,
105    pub source: IngestFileSource,
106    pub lake_root: String,
107    pub edam_format: Option<String>,
108    pub compress: bool,
109    pub encrypt: Option<bool>,
110    pub description: Option<String>,
111    pub agent_instructions: Option<String>,
112    pub version_note: Option<String>,
113    pub transformation: NewTransformationRecord,
114}
115
116#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
117#[serde(tag = "type", rename_all = "snake_case")]
118pub enum IngestFileSource {
119    LocalPath { path: String },
120    HttpsUri { uri: String },
121}
122
123/// Safe source facts and readable bytes for one Datafile ingest.
124pub struct IngestFileContent {
125    pub(crate) acquisition: Option<ahri_tre_types::AcquisitionPath>,
126    pub(crate) upload_validation: Option<ahri_tre_lake::UploadValidation>,
127    source_file_name: String,
128    media_type: Option<String>,
129    content_length: Option<u64>,
130    declared_digest: Option<String>,
131    reader: Box<dyn Read + Send>,
132}
133
134impl std::fmt::Debug for IngestFileContent {
135    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
136        formatter
137            .debug_struct("IngestFileContent")
138            .field("source_file_name", &self.source_file_name)
139            .field("media_type", &self.media_type)
140            .field("content_length", &self.content_length)
141            .field("declared_digest", &self.declared_digest)
142            .field("reader", &"readable content")
143            .finish()
144    }
145}
146
147impl IngestFileContent {
148    pub fn new(
149        source_file_name: impl Into<String>,
150        media_type: Option<String>,
151        content_length: Option<u64>,
152        declared_digest: Option<String>,
153        reader: impl Read + Send + 'static,
154    ) -> Self {
155        Self {
156            upload_validation: None,
157            acquisition: None,
158            source_file_name: source_file_name.into(),
159            media_type,
160            content_length,
161            declared_digest,
162            reader: Box::new(reader),
163        }
164    }
165
166    pub fn source_file_name(&self) -> &str {
167        &self.source_file_name
168    }
169
170    pub fn media_type(&self) -> Option<&str> {
171        self.media_type.as_deref()
172    }
173
174    pub fn content_length(&self) -> Option<u64> {
175        self.content_length
176    }
177
178    pub fn declared_digest(&self) -> Option<&str> {
179        self.declared_digest.as_deref()
180    }
181
182    pub fn reader_mut(&mut self) -> &mut (dyn Read + Send) {
183        self.reader.as_mut()
184    }
185
186    pub fn into_reader(self) -> Box<dyn Read + Send> {
187        self.reader
188    }
189}
190
191#[derive(Debug)]
192pub struct IngestDataFileRequest {
193    pub classification: ahri_tre_types::IngestClassification,
194    pub study: StudySelector,
195    pub asset_name: NcName,
196    pub content: IngestFileContent,
197    pub format: String,
198    pub description: Option<String>,
199    pub new_version: bool,
200    pub bump_major: bool,
201    pub bump_minor: bool,
202    pub compress: bool,
203    pub encrypt: Option<bool>,
204}
205
206#[derive(Debug, Clone, PartialEq, Eq)]
207pub struct DataFileIngestVersion {
208    pub datafile_id: VersionId,
209    pub edam_format: String,
210    pub digest: String,
211}
212
213#[derive(Debug, Clone, PartialEq, Eq)]
214pub struct DataFileIngestResult {
215    pub study_id: StudyId,
216    pub study_name: NcName,
217    pub catalog: DataFileListAssetCatalog,
218    pub datafile: DataFileIngestVersion,
219    pub transformation_id: TransformationId,
220    pub source_size_bytes: u64,
221    pub stored_size_bytes: u64,
222}
223
224impl DataFileIngestResult {
225    pub fn from_governed_result(study: StudyRecord, ingested: IngestedFileAsset) -> Self {
226        Self {
227            study_id: study.study_id,
228            study_name: study.name,
229            catalog: DataFileListAssetCatalog::from_catalogue(ingested.catalog),
230            datafile: DataFileIngestVersion {
231                datafile_id: ingested.datafile.datafile_id,
232                edam_format: ingested.datafile.edam_format,
233                digest: ingested.datafile.digest.as_str().to_string(),
234            },
235            transformation_id: ingested.lineage.transformation.transformation_id,
236            source_size_bytes: ingested.source_size_bytes,
237            stored_size_bytes: ingested.stored_size_bytes,
238        }
239    }
240}
241
242#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
243pub struct IngestedFileAsset {
244    pub catalog: AssetVersionCatalog,
245    pub datafile: DataFileRecord,
246    pub lineage: TransformationLineageRecord,
247    pub source_size_bytes: u64,
248    pub stored_size_bytes: u64,
249}
250
251#[derive(Debug, Clone, Serialize, Deserialize)]
252pub struct IngestDatasetFileRequest {
253    pub classification: ahri_tre_types::IngestClassification,
254    pub study_id: StudyId,
255    pub source_asset_name: Option<NcName>,
256    pub dataset_asset_id: Option<AssetId>,
257    pub dataset_name: NcName,
258    pub dataset_version_id: Option<VersionId>,
259    pub source: IngestFileSource,
260    pub input_format: Option<String>,
261    pub header: Option<bool>,
262    pub delimiter: Option<char>,
263    #[serde(default)]
264    pub null_strings: Vec<String>,
265    pub json_format: Option<String>,
266    pub sheet: Option<String>,
267    pub description: Option<String>,
268    pub agent_instructions: Option<String>,
269    pub version_note: Option<String>,
270    pub compress_source: bool,
271    pub encrypt_source: Option<bool>,
272    pub ingest_transformation: NewTransformationRecord,
273    pub materialize_transformation: NewTransformationRecord,
274    #[serde(default)]
275    pub strict_metadata: bool,
276    pub sample_rows: Option<usize>,
277    pub vocabulary_threshold: Option<usize>,
278    #[serde(default)]
279    pub force_metadata_updates: bool,
280    #[serde(default)]
281    pub variable_registrations: Vec<DatasetVariableRegistrationRequest>,
282}
283
284#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
285pub struct IngestedDatasetFile {
286    pub source_file: IngestedFileAsset,
287    pub dataset: DatasetMaterialization,
288}
289
290#[derive(Debug, Clone, Serialize, Deserialize)]
291pub struct DataFileToDatasetRequest {
292    pub study_id: StudyId,
293    #[serde(default, skip_serializing_if = "Option::is_none")]
294    pub metadata_domain_id: Option<DomainId>,
295    pub dataset_asset_id: Option<AssetId>,
296    pub dataset_name: NcName,
297    pub dataset_version_id: Option<VersionId>,
298    pub source_datafile_id: VersionId,
299    pub input_format: Option<String>,
300    pub header: Option<bool>,
301    pub delimiter: Option<char>,
302    #[serde(default)]
303    pub null_strings: Vec<String>,
304    pub json_format: Option<String>,
305    pub sheet: Option<String>,
306    pub description: Option<String>,
307    pub agent_instructions: Option<String>,
308    pub version_note: Option<String>,
309    pub transformation: NewTransformationRecord,
310    #[serde(default)]
311    pub strict_metadata: bool,
312    pub sample_rows: Option<usize>,
313    pub vocabulary_threshold: Option<usize>,
314    #[serde(default)]
315    pub force_metadata_updates: bool,
316    #[serde(default)]
317    pub variable_registrations: Vec<DatasetVariableRegistrationRequest>,
318}
319
320#[derive(Debug, Clone, Serialize, Deserialize)]
321pub struct ManagedDataFileToDatasetRequest {
322    pub study_id: StudyId,
323    #[serde(default, skip_serializing_if = "Option::is_none")]
324    pub metadata_domain_id: Option<DomainId>,
325    pub dataset_asset_id: Option<AssetId>,
326    pub dataset_name: NcName,
327    pub dataset_version_id: Option<VersionId>,
328    pub source_asset_name: NcName,
329    pub source_version: Option<String>,
330    pub input_format: Option<String>,
331    pub header: Option<bool>,
332    pub delimiter: Option<char>,
333    #[serde(default)]
334    pub null_strings: Vec<String>,
335    pub json_format: Option<String>,
336    pub sheet: Option<String>,
337    pub description: Option<String>,
338    pub agent_instructions: Option<String>,
339    pub version_note: Option<String>,
340    pub transformation: NewTransformationRecord,
341    #[serde(default)]
342    pub strict_metadata: bool,
343    pub sample_rows: Option<usize>,
344    pub vocabulary_threshold: Option<usize>,
345    #[serde(default)]
346    pub force_metadata_updates: bool,
347    #[serde(default)]
348    pub variable_registrations: Vec<DatasetVariableRegistrationRequest>,
349}
350
351#[derive(Debug, Clone, Serialize, Deserialize)]
352pub struct DeriveDatasetFromManagedDataFileRequest {
353    pub study: StudySelector,
354    pub metadata_domain: DomainSelector,
355    pub dataset_name: NcName,
356    pub source: ahri_tre_protocol::ingest::ManagedDataFileSource,
357    pub input_format: Option<String>,
358    pub header: Option<bool>,
359    pub delimiter: Option<char>,
360    #[serde(default)]
361    pub null_strings: Vec<String>,
362    pub json_format: Option<String>,
363    pub sheet: Option<String>,
364    pub description: Option<String>,
365    pub version_note: Option<String>,
366    #[serde(default)]
367    pub replace: bool,
368    pub new_version: Option<String>,
369    pub transformation: NewTransformationRecord,
370    pub sample_rows: Option<usize>,
371    pub vocabulary_threshold: Option<usize>,
372    #[serde(default)]
373    pub force_metadata_updates: bool,
374}
375
376#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
377pub struct DatasetMaterialization {
378    pub catalog: AssetVersionCatalog,
379    pub dataset: DatasetRecord,
380    pub lineage: TransformationLineageRecord,
381    pub lake_schema: String,
382    pub lake_table: String,
383    pub lake_relation: String,
384    pub row_count: usize,
385    pub column_count: usize,
386    pub variables: Vec<RegisteredDatasetVariable>,
387    #[serde(default)]
388    pub metadata_warnings: Vec<String>,
389}
390
391#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
392pub struct ManagedDataFileDatasetMaterialization {
393    pub source: DataFileMetadata,
394    pub dataset: DatasetMaterialization,
395}
396
397#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
398pub struct DerivedManagedDataFileDataset {
399    pub study: StudyRecord,
400    pub materialization: ManagedDataFileDatasetMaterialization,
401}
402
403#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
404#[serde(rename_all = "snake_case")]
405pub enum DatasetExportFormat {
406    Csv,
407    Ndjson,
408    Parquet,
409    Arrow,
410}
411
412impl DatasetExportFormat {
413    pub const fn extension(self) -> &'static str {
414        match self {
415            Self::Csv => "csv",
416            Self::Ndjson => "ndjson",
417            Self::Parquet => "parquet",
418            Self::Arrow => "arrow",
419        }
420    }
421}
422
423#[derive(Debug, Clone, Serialize, Deserialize)]
424pub struct ExportDatasetRequest {
425    pub dataset_id: VersionId,
426    pub target_dir: PathBuf,
427    pub format: DatasetExportFormat,
428    pub limit: Option<usize>,
429    pub compress: bool,
430    pub overwrite: bool,
431    pub transformation: NewTransformationRecord,
432}
433
434#[derive(Debug, Clone, Serialize, Deserialize)]
435pub struct ExportDatasetToPathRequest {
436    pub dataset_id: VersionId,
437    pub target_path: PathBuf,
438    pub format: DatasetExportFormat,
439    pub limit: Option<usize>,
440    pub compress: bool,
441    pub overwrite: bool,
442    pub transformation: NewTransformationRecord,
443}
444
445#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
446pub struct ExportedDataset {
447    pub catalog: AssetVersionCatalog,
448    pub dataset: DatasetRecord,
449    pub lineage: TransformationLineageRecord,
450    pub format: DatasetExportFormat,
451    pub target_path: PathBuf,
452    pub compressed: bool,
453    pub row_count: usize,
454    pub column_count: usize,
455    pub exported_size_bytes: u64,
456    pub warnings: Vec<String>,
457}
458
459#[derive(Debug, Clone, Serialize, Deserialize)]
460pub struct ExportDataFileRequest {
461    pub datafile_id: VersionId,
462    pub target_dir: PathBuf,
463    pub compress: bool,
464    pub overwrite: bool,
465    pub transformation: NewTransformationRecord,
466}
467
468#[derive(Debug, Clone, Serialize, Deserialize)]
469pub struct ListStudyDataFilesRequest {
470    pub study_id: StudyId,
471    pub include_versions: bool,
472}
473
474/// Transport-neutral request for the Session-scoped Datafile list workflow.
475#[derive(Debug, Clone, Serialize, Deserialize)]
476pub struct ListDataFilesRequest {
477    pub study: StudySelector,
478    pub include_versions: bool,
479}
480
481#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
482pub struct StudyDataFileEntry {
483    pub catalog: AssetVersionCatalog,
484    pub datafiles: Vec<DataFileRecord>,
485}
486
487/// Transport-neutral result from the Session-scoped Datafile list workflow.
488#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
489pub struct DataFileListResult {
490    pub study_id: StudyId,
491    pub study_name: NcName,
492    pub datafiles: Vec<DataFileListEntry>,
493}
494
495impl DataFileListResult {
496    /// Removes storage and instruction fields from a governed catalogue result.
497    pub fn from_catalogue(study: StudyRecord, entries: Vec<StudyDataFileEntry>) -> Self {
498        Self {
499            study_id: study.study_id,
500            study_name: study.name,
501            datafiles: entries
502                .into_iter()
503                .map(|entry| DataFileListEntry {
504                    catalog: DataFileListAssetCatalog::from_catalogue(entry.catalog),
505                    datafiles: entry
506                        .datafiles
507                        .into_iter()
508                        .map(|datafile| DataFileListVersionSummary {
509                            datafile_id: datafile.datafile_id,
510                            compressed: datafile.compressed,
511                            encrypted: datafile.encrypted,
512                            compression_algorithm: datafile.compression_algorithm,
513                            encryption_algorithm: datafile.encryption_algorithm,
514                            edam_format: datafile.edam_format,
515                        })
516                        .collect(),
517                })
518                .collect(),
519        }
520    }
521}
522
523/// One safe Datafile asset and its selected versions.
524#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
525pub struct DataFileListEntry {
526    pub catalog: DataFileListAssetCatalog,
527    pub datafiles: Vec<DataFileListVersionSummary>,
528}
529
530/// Safe asset and version metadata for a Datafile list result.
531#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
532pub struct DataFileListAssetCatalog {
533    pub asset: DataFileListAssetSummary,
534    pub versions: Vec<AssetVersionRecord>,
535    pub latest_version: Option<AssetVersionRecord>,
536}
537
538impl DataFileListAssetCatalog {
539    /// Removes private asset instructions from a governed asset catalogue.
540    pub fn from_catalogue(catalog: AssetVersionCatalog) -> Self {
541        Self {
542            asset: DataFileListAssetSummary {
543                asset_id: catalog.asset.asset_id,
544                study_id: catalog.asset.study_id,
545                name: catalog.asset.name,
546                description: catalog.asset.description,
547                asset_type: catalog.asset.asset_type,
548                date_created: catalog.asset.date_created,
549                created_by: catalog.asset.created_by,
550            },
551            versions: catalog.versions,
552            latest_version: catalog.latest_version,
553        }
554    }
555}
556
557/// Safe asset metadata for a Datafile list result.
558#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
559pub struct DataFileListAssetSummary {
560    pub asset_id: AssetId,
561    pub study_id: StudyId,
562    pub name: NcName,
563    pub description: Option<String>,
564    pub asset_type: ahri_tre_types::AssetType,
565    pub date_created: Option<DateTime<Utc>>,
566    pub created_by: Option<String>,
567}
568
569/// Safe Datafile-version facts for a Datafile list result.
570#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
571pub struct DataFileListVersionSummary {
572    pub datafile_id: VersionId,
573    pub compressed: Option<bool>,
574    pub encrypted: Option<bool>,
575    pub compression_algorithm: Option<String>,
576    pub encryption_algorithm: Option<String>,
577    pub edam_format: String,
578}
579
580#[derive(Debug, Clone, Serialize, Deserialize)]
581pub struct DataFileMetadataRequest {
582    pub study_id: StudyId,
583    pub asset_name: NcName,
584    pub version: Option<String>,
585}
586
587#[derive(Debug, Clone, Serialize, Deserialize)]
588pub struct DataFileMetadataByIdRequest {
589    pub datafile_id: VersionId,
590}
591
592#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
593pub struct DataFileMetadata {
594    pub catalog: AssetVersionCatalog,
595    pub version: AssetVersionRecord,
596    pub datafile: DataFileRecord,
597}
598
599#[derive(Debug, Clone, Serialize, Deserialize)]
600pub struct ExportStudyDataFileRequest {
601    pub study_id: StudyId,
602    pub asset_name: NcName,
603    pub version: Option<String>,
604    pub target_dir: PathBuf,
605    pub compress: bool,
606    pub overwrite: bool,
607    pub transformation: NewTransformationRecord,
608}
609
610#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
611pub struct ExportedDataFile {
612    pub catalog: AssetVersionCatalog,
613    pub datafile: DataFileRecord,
614    pub lineage: TransformationLineageRecord,
615    pub target_path: PathBuf,
616    pub compressed: bool,
617    pub stored_size_bytes: u64,
618    pub logical_size_bytes: u64,
619    pub exported_size_bytes: u64,
620}
621
622#[derive(Debug, Clone, Serialize, Deserialize)]
623pub struct RegisterDatasetVariablesRequest {
624    pub dataset_id: VersionId,
625    pub domain_id: DomainId,
626    pub variables: Vec<DatasetVariableRegistrationRequest>,
627}
628
629#[derive(Debug, Clone, Serialize, Deserialize)]
630pub struct RegisterVocabularyRequest {
631    pub domain_id: DomainId,
632    pub vocabulary_id: Option<VocabularyId>,
633    pub name: NcName,
634    pub description: Option<String>,
635    #[serde(default)]
636    pub items: Vec<RegisterVocabularyItemRequest>,
637    pub force_metadata_updates: bool,
638}
639
640#[derive(Debug, Clone, Serialize, Deserialize)]
641pub struct RegisterVocabularyItemRequest {
642    pub vocabulary_item_id: Option<VocabularyItemId>,
643    pub value: i32,
644    pub code: String,
645    pub description: Option<String>,
646}
647
648#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
649pub struct RegisteredVocabulary {
650    pub vocabulary: VocabularyRecord,
651    pub items: Vec<VocabularyItemRecord>,
652}
653
654#[derive(Debug, Clone, Serialize, Deserialize)]
655pub struct VocabularyItemReference {
656    pub vocabulary_item_id: Option<VocabularyItemId>,
657    pub domain_id: Option<DomainId>,
658    pub vocabulary_id: Option<VocabularyId>,
659    pub vocabulary_name: Option<NcName>,
660    pub value: Option<i32>,
661    pub code: Option<String>,
662}
663
664#[derive(Debug, Clone, Serialize, Deserialize)]
665pub struct RegisterVocabularyMappingRequest {
666    pub from: VocabularyItemReference,
667    pub to: VocabularyItemReference,
668}
669
670#[derive(Debug, Clone, Serialize, Deserialize)]
671pub struct ListVocabularyMappingsRequest {
672    pub from_vocabulary_id: Option<VocabularyId>,
673    pub to_vocabulary_id: Option<VocabularyId>,
674    pub involving_vocabulary_id: Option<VocabularyId>,
675    pub item_id: Option<VocabularyItemId>,
676}
677
678#[derive(Debug, Clone, Serialize, Deserialize)]
679pub struct VocabularyMappingSummaryRequest {
680    pub vocabulary_id: VocabularyId,
681}
682
683#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
684pub struct VocabularyMappingDetail {
685    pub mapping: VocabularyMappingRecord,
686    pub from_vocabulary: VocabularyRecord,
687    pub from_item: VocabularyItemRecord,
688    pub to_vocabulary: VocabularyRecord,
689    pub to_item: VocabularyItemRecord,
690}
691
692#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
693pub struct VocabularyMappingSummaryPeer {
694    pub vocabulary: VocabularyRecord,
695    pub outgoing_count: usize,
696    pub incoming_count: usize,
697}
698
699#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
700pub struct VocabularyMappingSummary {
701    pub vocabulary: VocabularyRecord,
702    pub has_mappings: bool,
703    pub outgoing_count: usize,
704    pub incoming_count: usize,
705    pub mapped_vocabularies: Vec<VocabularyMappingSummaryPeer>,
706}
707
708#[derive(Debug, Clone, Serialize, Deserialize)]
709pub struct RegisterVariableRequest {
710    pub domain_id: DomainId,
711    pub variable_id: Option<VariableId>,
712    pub name: NcName,
713    pub value_type_id: Option<ValueTypeId>,
714    pub value_type: Option<String>,
715    pub value_format: Option<String>,
716    pub vocabulary_id: Option<VocabularyId>,
717    pub key_role: KeyRole,
718    pub description: Option<String>,
719    pub note: Option<String>,
720    pub ontology_namespace: Option<String>,
721    pub ontology_class: Option<String>,
722    pub force_metadata_updates: bool,
723}
724
725#[derive(Debug, Clone, Serialize, Deserialize)]
726pub struct ListVariablesRequest {
727    pub domain: Option<DomainSelector>,
728    pub study: Option<StudySelector>,
729}
730
731#[derive(Debug, Clone, Serialize, Deserialize)]
732pub struct GetVariableRequest {
733    pub variable: VariableSelector,
734}
735
736#[derive(Debug, Clone, Serialize, Deserialize)]
737pub struct AddVariableRequest {
738    pub domain: DomainSelector,
739    pub name: String,
740    pub value_type: String,
741    pub value_format: Option<String>,
742    pub key_role: Option<String>,
743    pub description: Option<String>,
744    pub ontology_namespace: Option<String>,
745    pub ontology_class: Option<String>,
746    pub vocabulary: Option<String>,
747    pub vocabulary_items: Option<String>,
748}
749
750#[derive(Debug, Clone, Serialize, Deserialize)]
751pub struct UpdateVariableRequest {
752    pub variable: VariableSelector,
753    pub value_type: Option<String>,
754    pub value_format: Option<String>,
755    pub key_role: Option<String>,
756    pub description: Option<String>,
757    pub ontology_namespace: Option<String>,
758    pub ontology_class: Option<String>,
759    pub vocabulary: Option<String>,
760}
761
762#[derive(Debug, Clone, Serialize, Deserialize)]
763pub struct SearchVariablesRequest {
764    pub kind: PredicateSetKind,
765    pub predicates: Vec<VariableSearchPredicate>,
766    pub limit: u16,
767    pub after: Option<VariableSearchCursorAnchor>,
768}
769
770#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
771#[serde(tag = "kind", rename_all = "snake_case")]
772pub enum SemanticDeleteTarget {
773    Domain { domain: DomainSelector },
774    Variable { variable: VariableSelector },
775    Vocabulary { vocabulary: VocabularySelector },
776    Entity { entity: EntitySelector },
777    EntityRelation { relation: EntityRelationSelector },
778}
779
780#[derive(Debug, Clone, Serialize, Deserialize)]
781pub struct PlanSemanticDeleteRequest {
782    pub target: SemanticDeleteTarget,
783    pub reason: String,
784    pub actor: Option<String>,
785    #[serde(default = "default_semantic_delete_dry_run")]
786    pub dry_run: bool,
787}
788
789const fn default_semantic_delete_dry_run() -> bool {
790    true
791}
792
793#[derive(Debug, Clone, Serialize, Deserialize)]
794pub struct ExecuteSemanticDeleteRequest {
795    pub plan: SemanticDeletePlan,
796    #[serde(default)]
797    pub confirmed: bool,
798}
799
800#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
801pub struct SemanticDeletePlanningResult {
802    pub accepted: bool,
803    pub plan: Option<SemanticDeletePlan>,
804    #[serde(default)]
805    pub validation_errors: Vec<SemanticDeleteValidationError>,
806}
807
808impl SemanticDeletePlanningResult {
809    pub fn accepted(plan: SemanticDeletePlan) -> Self {
810        Self {
811            accepted: true,
812            plan: Some(plan),
813            validation_errors: Vec::new(),
814        }
815    }
816
817    pub fn rejected(validation_errors: Vec<SemanticDeleteValidationError>) -> Self {
818        Self {
819            accepted: false,
820            plan: None,
821            validation_errors,
822        }
823    }
824}
825
826#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
827pub struct SemanticDeleteValidationError {
828    pub code: String,
829    pub message: String,
830}
831
832#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
833pub struct SemanticDeletePlan {
834    pub target: SemanticDeleteTarget,
835    pub target_identity: SemanticDeleteTargetIdentity,
836    pub authorization: SemanticDeleteAuthorization,
837    pub reason: String,
838    pub actor: String,
839    pub dependency_summary: SemanticDeleteDependencySummary,
840    pub disposition: SemanticDeleteDisposition,
841}
842
843#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
844pub struct SemanticDeleteTargetIdentity {
845    /// Resolved owning Domain identity, retained separately from display names.
846    pub domain_id: ahri_tre_types::DomainId,
847    pub kind: SemanticDeleteTargetKind,
848    pub id: String,
849    pub domain: Option<String>,
850    pub name: String,
851}
852
853#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
854#[serde(rename_all = "snake_case")]
855pub enum SemanticDeleteTargetKind {
856    Domain,
857    Variable,
858    Vocabulary,
859    Entity,
860    EntityRelation,
861}
862
863#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
864pub struct SemanticDeleteAuthorization {
865    pub status: SemanticDeleteAuthorizationStatus,
866    pub principal: String,
867    pub requirement: String,
868}
869
870#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
871#[serde(rename_all = "snake_case")]
872pub enum SemanticDeleteAuthorizationStatus {
873    ActorRecorded,
874}
875
876#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
877pub struct SemanticDeleteDependencySummary {
878    pub status: SemanticDeleteDependencyStatus,
879    #[serde(default)]
880    pub active_blockers: Vec<SemanticDeleteDependency>,
881    #[serde(default)]
882    pub historical_blockers: Vec<SemanticDeleteDependency>,
883    #[serde(default)]
884    pub unsupported: Vec<SemanticDeleteDependency>,
885    #[serde(default)]
886    pub delete_candidates: Vec<SemanticDeleteDependency>,
887}
888
889impl SemanticDeleteDependencySummary {
890    pub fn clear(target: SemanticDeleteDependency) -> Self {
891        Self {
892            status: SemanticDeleteDependencyStatus::Clear,
893            active_blockers: Vec::new(),
894            historical_blockers: Vec::new(),
895            unsupported: Vec::new(),
896            delete_candidates: vec![target],
897        }
898    }
899
900    pub fn has_blockers(&self) -> bool {
901        !self.active_blockers.is_empty()
902            || !self.historical_blockers.is_empty()
903            || !self.unsupported.is_empty()
904    }
905}
906
907#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
908#[serde(rename_all = "snake_case")]
909pub enum SemanticDeleteDependencyStatus {
910    Clear,
911    Blocked,
912}
913
914#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
915pub struct SemanticDeleteDependency {
916    pub kind: String,
917    pub identifier: String,
918    pub description: String,
919}
920
921#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
922#[serde(rename_all = "snake_case")]
923pub enum SemanticDeleteDisposition {
924    PhysicalDeleteAllowed,
925    Blocked,
926}
927
928#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
929pub struct ExecuteSemanticDeleteResult {
930    pub plan: SemanticDeletePlan,
931    pub deleted: bool,
932    pub disposition: SemanticDeleteExecutionDisposition,
933}
934
935#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
936#[serde(rename_all = "snake_case")]
937pub enum SemanticDeleteExecutionDisposition {
938    PhysicallyDeleted,
939    AlreadyAbsent,
940}
941
942#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
943pub struct RegisteredVariable {
944    pub variable: VariableRecord,
945    pub value_type: ValueTypeRecord,
946    pub vocabulary: Option<VocabularyRecord>,
947    pub vocabulary_items: Vec<VocabularyItemRecord>,
948}
949
950#[derive(Debug, Clone, Serialize, Deserialize)]
951pub struct VariableSearchEntry {
952    pub variable: VariableRecord,
953    pub value_type: ValueTypeRecord,
954    pub vocabulary: Option<VocabularyRecord>,
955    pub vocabulary_items: Vec<VocabularyItemRecord>,
956    pub tags: Vec<TagRecord>,
957}
958
959#[derive(Debug, Clone, Serialize, Deserialize)]
960pub struct VariableSearchResult {
961    pub variables: Vec<VariableSearchEntry>,
962    pub next_cursor: Option<VariableSearchCursorAnchor>,
963}
964
965#[derive(Debug, Clone, Serialize, Deserialize)]
966pub struct SearchEntitiesRequest {
967    pub kind: PredicateSetKind,
968    pub predicates: Vec<EntitySearchPredicate>,
969    pub limit: u16,
970    pub after: Option<EntitySearchCursorAnchor>,
971}
972
973#[derive(Debug, Clone, Serialize, Deserialize)]
974pub struct EntitySearchEntry {
975    pub entity: EntityRecord,
976    pub tags: Vec<TagRecord>,
977}
978
979#[derive(Debug, Clone, Serialize, Deserialize)]
980pub struct EntitySearchResult {
981    pub entities: Vec<EntitySearchEntry>,
982    pub next_cursor: Option<EntitySearchCursorAnchor>,
983}
984
985#[derive(Debug, Clone, Serialize, Deserialize)]
986pub struct SearchRelationsRequest {
987    pub kind: PredicateSetKind,
988    pub predicates: Vec<RelationSearchPredicate>,
989    pub limit: u16,
990    pub after: Option<RelationSearchCursorAnchor>,
991}
992
993#[derive(Debug, Clone, Serialize, Deserialize)]
994pub struct RelationSearchEntry {
995    pub relation: EntityRelationRecord,
996    pub source_entity: EntityRecord,
997    pub target_entity: EntityRecord,
998    pub tags: Vec<TagRecord>,
999}
1000
1001#[derive(Debug, Clone, Serialize, Deserialize)]
1002pub struct RelationSearchResult {
1003    pub relations: Vec<RelationSearchEntry>,
1004    pub next_cursor: Option<RelationSearchCursorAnchor>,
1005}
1006
1007#[derive(Debug, Clone, Serialize, Deserialize)]
1008pub struct ListVocabulariesRequest {
1009    pub domain: Option<DomainSelector>,
1010    pub study: Option<StudySelector>,
1011}
1012
1013#[derive(Debug, Clone, Serialize, Deserialize)]
1014pub struct GetVocabularyRequest {
1015    pub vocabulary: VocabularySelector,
1016}
1017
1018#[derive(Debug, Clone, Serialize, Deserialize)]
1019pub struct AddVocabularyRequest {
1020    pub domain: DomainSelector,
1021    pub name: String,
1022    pub description: Option<String>,
1023    pub items: String,
1024}
1025
1026#[derive(Debug, Clone, Serialize, Deserialize)]
1027pub struct DatasetVariableRegistrationRequest {
1028    pub variable: VariableRecord,
1029    pub row_role: Option<KeyRole>,
1030    pub vocabulary: Option<VocabularyRecord>,
1031    #[serde(default)]
1032    pub vocabulary_items: Vec<VocabularyItemRecord>,
1033}
1034
1035#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1036pub struct RegisteredDatasetVariable {
1037    pub variable: VariableRecord,
1038    pub vocabulary: Option<VocabularyRecord>,
1039    pub vocabulary_items: Vec<VocabularyItemRecord>,
1040    pub link: DatasetVariableLinkRecord,
1041}
1042
1043#[derive(Debug, Clone, Serialize, Deserialize)]
1044pub struct GetDatasetVariablesRequest {
1045    pub dataset_id: VersionId,
1046}
1047
1048#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1049pub struct DatasetVariableMetadata {
1050    pub link: DatasetVariableLinkRecord,
1051    pub variable: VariableRecord,
1052    pub value_type: ValueTypeRecord,
1053    pub vocabulary: Option<VocabularyRecord>,
1054    pub vocabulary_items: Vec<VocabularyItemRecord>,
1055}
1056
1057#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1058pub struct DatasetVariablesMetadata {
1059    pub dataset: DatasetRecord,
1060    pub variables: Vec<DatasetVariableMetadata>,
1061}
1062
1063#[derive(Debug, Clone, Serialize, Deserialize)]
1064pub struct ListStudyDatasetsRequest {
1065    pub study_id: StudyId,
1066    #[serde(default)]
1067    pub include_versions: bool,
1068}
1069
1070#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1071pub struct DatasetListItem {
1072    pub catalog: AssetVersionCatalog,
1073    pub latest_dataset: Option<DatasetRecord>,
1074}
1075
1076#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1077pub struct StudyDatasetList {
1078    pub study_id: StudyId,
1079    pub datasets: Vec<DatasetListItem>,
1080}
1081
1082#[derive(Debug, Clone, Serialize, Deserialize)]
1083pub struct GetStudyDatasetRequest {
1084    pub study_id: StudyId,
1085    pub dataset_name: NcName,
1086    pub with_variables: bool,
1087}
1088
1089#[derive(Debug, Clone, Serialize, Deserialize)]
1090pub struct DatasetMetadataByIdRequest {
1091    pub dataset_id: AssetId,
1092    pub with_variables: bool,
1093}
1094
1095#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1096pub enum DatasetMetadataSelector {
1097    Id { dataset_id: AssetId },
1098    Name { study: StudySelector, name: NcName },
1099}
1100
1101#[derive(Debug, Clone, Serialize, Deserialize)]
1102pub struct ReadDatasetMetadataRequest {
1103    pub dataset: DatasetMetadataSelector,
1104    pub with_variables: bool,
1105}
1106
1107#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1108pub struct StudyDatasetMetadata {
1109    pub catalog: AssetVersionCatalog,
1110    pub dataset: DatasetRecord,
1111    pub variables_included: bool,
1112    pub variable_count: usize,
1113    pub variables: Vec<DatasetVariableMetadata>,
1114}
1115
1116#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1117pub struct DatasetMetadataResult {
1118    pub study: StudyRecord,
1119    pub metadata: StudyDatasetMetadata,
1120}
1121
1122#[derive(Debug, Clone, Serialize, Deserialize)]
1123pub struct PreviewStudyDatasetRequest {
1124    pub study_id: StudyId,
1125    pub dataset_name: NcName,
1126    pub limit: usize,
1127}
1128
1129#[derive(Debug, Clone, Serialize, Deserialize)]
1130pub struct PreviewDatasetByIdRequest {
1131    pub dataset_id: AssetId,
1132    pub limit: usize,
1133}
1134
1135#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1136pub struct DatasetPreviewColumn {
1137    pub name: String,
1138    pub value_type: String,
1139}
1140
1141#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1142pub struct DatasetPreview {
1143    pub catalog: AssetVersionCatalog,
1144    pub dataset: DatasetRecord,
1145    pub columns: Vec<DatasetPreviewColumn>,
1146    pub rows: Vec<Vec<Option<String>>>,
1147    pub displayed_row_count: usize,
1148    pub total_row_count: usize,
1149    pub truncated: bool,
1150    pub limit: usize,
1151}
1152
1153#[derive(Debug, Clone, Serialize, Deserialize)]
1154pub struct WithdrawDatasetVersionRequest {
1155    pub study_id: StudyId,
1156    pub dataset_name: NcName,
1157    pub version: String,
1158    pub reason: String,
1159    pub actor: Option<String>,
1160    #[serde(default)]
1161    pub force: bool,
1162    #[serde(default)]
1163    pub drop_lake_table: bool,
1164}
1165
1166#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1167#[serde(rename_all = "snake_case")]
1168pub enum LakeTableDropDisposition {
1169    NotRequested,
1170    DroppedIfExisted,
1171    AlreadyWithdrawn,
1172}
1173
1174#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1175pub struct WithdrawDatasetVersionResult {
1176    pub catalog: AssetVersionCatalog,
1177    pub withdrawn_version: AssetVersionRecord,
1178    pub withdrawal: DatasetVersionWithdrawalRecord,
1179    pub promoted_latest_version: Option<AssetVersionRecord>,
1180    pub lake_relation: String,
1181    pub lake_schema: String,
1182    pub lake_table: String,
1183    pub lake_table_drop: LakeTableDropDisposition,
1184    pub already_withdrawn: bool,
1185}
1186
1187#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1188#[serde(tag = "type", rename_all = "snake_case")]
1189pub enum ArchiveDeleteVersionTarget {
1190    Version { version_id: VersionId },
1191    Latest,
1192}
1193
1194#[derive(Debug, Clone, Serialize, Deserialize)]
1195pub struct PlanArchiveDeleteRequest {
1196    pub target: DeleteTarget,
1197    pub archive_mode: ArchiveMode,
1198    #[serde(default)]
1199    pub cascade: bool,
1200    #[serde(default)]
1201    pub force: bool,
1202    pub reason: String,
1203    pub actor: Option<String>,
1204    pub version_target: Option<ArchiveDeleteVersionTarget>,
1205    #[serde(default)]
1206    pub delete_all_versions: bool,
1207    #[serde(default = "default_archive_delete_dry_run")]
1208    pub dry_run: bool,
1209}
1210
1211const fn default_archive_delete_dry_run() -> bool {
1212    true
1213}
1214
1215#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1216pub struct ArchiveDeletePlanningResult {
1217    pub accepted: bool,
1218    pub plan: Option<ArchiveDeletePlan>,
1219    #[serde(default)]
1220    pub validation_errors: Vec<ArchiveDeleteValidationError>,
1221}
1222
1223impl ArchiveDeletePlanningResult {
1224    pub fn accepted(plan: ArchiveDeletePlan) -> Self {
1225        Self {
1226            accepted: true,
1227            plan: Some(plan),
1228            validation_errors: Vec::new(),
1229        }
1230    }
1231
1232    pub fn rejected(validation_errors: Vec<ArchiveDeleteValidationError>) -> Self {
1233        Self {
1234            accepted: false,
1235            plan: None,
1236            validation_errors,
1237        }
1238    }
1239}
1240
1241#[derive(Debug, Clone, Serialize, Deserialize)]
1242pub struct DeleteDatasetAssetVersionRequest {
1243    pub plan: ArchiveDeletePlan,
1244}
1245
1246#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1247pub struct DeleteDatasetAssetVersionResult {
1248    pub plan: ArchiveDeletePlan,
1249    pub deletion: WithdrawDatasetVersionResult,
1250    pub target_version: AssetVersionRecord,
1251    pub tombstone: DatasetVersionWithdrawalRecord,
1252    pub promoted_latest_version: Option<AssetVersionRecord>,
1253    pub lake_cleanup: LakeTableDropDisposition,
1254    pub retry_state: ArchiveDeleteRetryState,
1255}
1256
1257#[derive(Debug, Clone, Serialize, Deserialize)]
1258pub struct DeleteDataFileAssetVersionRequest {
1259    pub plan: ArchiveDeletePlan,
1260}
1261
1262#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1263pub struct DeleteDataFileAssetVersionResult {
1264    pub plan: ArchiveDeletePlan,
1265    pub asset: AssetRecord,
1266    pub target_version: AssetVersionRecord,
1267    pub datafile: DataFileRecord,
1268    pub promoted_latest_version: Option<AssetVersionRecord>,
1269    pub managed_file_cleanup: ArchiveDeleteCleanupDisposition,
1270    pub metadata_cleanup: ArchiveDeleteCleanupDisposition,
1271    pub removed_size_bytes: Option<u64>,
1272    pub retry_state: ArchiveDeleteRetryState,
1273}
1274
1275#[derive(Debug, Clone, Serialize, Deserialize)]
1276pub struct DeleteAssetRequest {
1277    pub plan: ArchiveDeletePlan,
1278}
1279
1280#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1281pub struct DeleteAssetResult {
1282    pub plan: ArchiveDeletePlan,
1283    pub asset: AssetRecord,
1284    pub version_results: Vec<DeleteAssetVersionResult>,
1285    pub deleted_versions: usize,
1286    pub skipped_versions: usize,
1287    pub failed_versions: usize,
1288    pub metadata_cleanup: ArchiveDeleteCleanupDisposition,
1289    pub retry_state: ArchiveDeleteRetryState,
1290}
1291
1292#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1293#[serde(tag = "kind", rename_all = "snake_case")]
1294pub enum DeleteAssetVersionResult {
1295    Dataset(Box<DeleteDatasetAssetVersionResult>),
1296    Datafile(Box<DeleteDataFileAssetVersionResult>),
1297    Skipped {
1298        version: AssetVersionRecord,
1299        reason: String,
1300    },
1301}
1302
1303#[derive(Debug, Clone, Serialize, Deserialize)]
1304pub struct DeleteArchivedStudyRequest {
1305    pub plan: ArchiveDeletePlan,
1306    pub archive: ArchiveStudyForDeletionResult,
1307}
1308
1309#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1310pub struct DeleteArchivedStudyResult {
1311    pub plan: ArchiveDeletePlan,
1312    pub archive: ArchiveStudyForDeletionResult,
1313    pub source_study: StudyRecord,
1314    pub asset_results: Vec<DeleteAssetResult>,
1315    pub deleted_assets: usize,
1316    pub deleted_versions: usize,
1317    pub skipped_versions: usize,
1318    pub failed_versions: usize,
1319    pub metadata_cleanup: ArchiveDeleteCleanupDisposition,
1320    pub retry_state: ArchiveDeleteRetryState,
1321}
1322
1323#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1324pub struct ArchiveDeleteValidationError {
1325    pub code: String,
1326    pub message: String,
1327}
1328
1329#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1330pub struct ArchiveDeletePlan {
1331    pub target: DeleteTarget,
1332    pub archive_mode: ArchiveMode,
1333    pub archive_required: bool,
1334    pub authorization: ArchiveDeleteAuthorization,
1335    pub cascade: bool,
1336    pub force: bool,
1337    pub reason: String,
1338    pub actor: Option<String>,
1339    pub resolved_version: Option<VersionId>,
1340    pub delete_all_versions: bool,
1341    pub dependency_summary: ArchiveDeleteDependencySummary,
1342    pub archive_preview: ArchiveDeleteStagePreview,
1343    pub metadata_cleanup_preview: ArchiveDeleteStagePreview,
1344    pub lake_cleanup_preview: ArchiveDeleteStagePreview,
1345    pub retry_state: ArchiveDeleteRetryState,
1346}
1347
1348#[derive(Debug, Clone, Serialize, Deserialize)]
1349pub struct PrepareStudyArchiveForDeletionRequest {
1350    pub study_id: StudyId,
1351    pub reason: String,
1352    pub actor: Option<String>,
1353}
1354
1355#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1356pub struct PreparedArchiveDatafileIdentity {
1357    pub archive_study_id: StudyId,
1358    pub archive_asset_name: NcName,
1359    pub archive_asset_id: Option<AssetId>,
1360    pub archive_version_id: Option<VersionId>,
1361    pub archive_datafile_id: Option<VersionId>,
1362    pub bundle_file_name: String,
1363    pub mime_type: String,
1364    pub compression: String,
1365    pub dataset_payload_format: DatasetExportFormat,
1366}
1367
1368#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1369pub struct PreparedStudyArchiveForDeletionResult {
1370    pub source_study: StudyRecord,
1371    pub archive_domain: DomainRecord,
1372    pub archive_study: StudyRecord,
1373    pub archive_datafile: PreparedArchiveDatafileIdentity,
1374    pub reason: String,
1375    pub actor: String,
1376    pub archive_stage: ArchiveDeleteStagePreview,
1377    pub metadata_cleanup_stage: ArchiveDeleteStagePreview,
1378    pub lake_cleanup_stage: ArchiveDeleteStagePreview,
1379    pub source_study_delete_started: bool,
1380    pub lake_payload_created: bool,
1381}
1382
1383#[derive(Debug, Clone, Serialize, Deserialize)]
1384pub struct ArchiveStudyForDeletionRequest {
1385    pub study_id: StudyId,
1386    pub reason: String,
1387    pub actor: Option<String>,
1388    #[serde(default)]
1389    pub overwrite_existing_exports: bool,
1390}
1391
1392#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1393pub struct ArchiveStudyForDeletionResult {
1394    pub prepared: PreparedStudyArchiveForDeletionResult,
1395    pub archive_asset: AssetRecord,
1396    pub archive_version: AssetVersionRecord,
1397    pub archive_datafile: DataFileRecord,
1398    pub manifest: StudyArchiveManifest,
1399    pub source_size_bytes: u64,
1400    pub stored_size_bytes: u64,
1401    pub source_study_delete_started: bool,
1402}
1403
1404#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1405pub struct StudyArchiveManifest {
1406    pub target: StudyArchiveTarget,
1407    pub archive: StudyArchiveRegistration,
1408    pub reason: String,
1409    pub actor: String,
1410    pub created_at: DateTime<Utc>,
1411    pub dataset_payload_format: DatasetExportFormat,
1412    #[serde(default)]
1413    pub domains: Vec<DomainRecord>,
1414    #[serde(default)]
1415    pub variables: Vec<VariableRecord>,
1416    #[serde(default)]
1417    pub vocabularies: Vec<VocabularyRecord>,
1418    #[serde(default)]
1419    pub vocabulary_items: Vec<VocabularyItemRecord>,
1420    #[serde(default)]
1421    pub entities: Vec<ahri_tre_types::EntityRecord>,
1422    #[serde(default)]
1423    pub entity_relations: Vec<EntityRelationRecord>,
1424    #[serde(default)]
1425    pub assets: Vec<AssetRecord>,
1426    #[serde(default)]
1427    pub asset_versions: Vec<AssetVersionRecord>,
1428    #[serde(default)]
1429    pub datasets: Vec<ArchivedDatasetFileRecord>,
1430    #[serde(default)]
1431    pub datafiles: Vec<ArchivedDataFileRecord>,
1432    pub summary: StudyArchiveSummary,
1433}
1434
1435#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1436pub struct StudyArchiveTarget {
1437    pub study_id: StudyId,
1438    pub study_name: NcName,
1439}
1440
1441#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1442pub struct StudyArchiveRegistration {
1443    pub archive_study_id: StudyId,
1444    pub archive_asset_name: NcName,
1445    pub bundle_file_name: String,
1446    pub mime_type: String,
1447    pub compression: String,
1448}
1449
1450#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1451pub struct ArchivedDatasetFileRecord {
1452    pub asset_id: AssetId,
1453    pub asset_name: NcName,
1454    pub version_id: VersionId,
1455    pub major: i32,
1456    pub minor: i32,
1457    pub patch: i32,
1458    pub bundle_path: String,
1459    pub file_format: DatasetExportFormat,
1460    pub row_count: usize,
1461    pub column_count: usize,
1462    pub exported_size_bytes: u64,
1463}
1464
1465#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1466pub struct ArchivedDataFileRecord {
1467    pub asset_id: AssetId,
1468    pub asset_name: NcName,
1469    pub version_id: VersionId,
1470    pub major: i32,
1471    pub minor: i32,
1472    pub patch: i32,
1473    pub bundle_path: String,
1474    pub edam_format: String,
1475    pub exported_size_bytes: u64,
1476}
1477
1478#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1479pub struct StudyArchiveSummary {
1480    pub asset_count: usize,
1481    pub asset_version_count: usize,
1482    pub dataset_count: usize,
1483    pub datafile_count: usize,
1484    pub domain_count: usize,
1485    pub variable_count: usize,
1486    pub vocabulary_count: usize,
1487    pub vocabulary_item_count: usize,
1488    pub entity_count: usize,
1489    pub entity_relation_count: usize,
1490}
1491
1492#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1493pub struct ArchiveDeleteAuthorization {
1494    pub status: ArchiveDeleteAuthorizationStatus,
1495    pub principal: String,
1496    pub requirement: String,
1497}
1498
1499#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1500#[serde(rename_all = "snake_case")]
1501pub enum ArchiveDeleteAuthorizationStatus {
1502    Authorized,
1503    ActorRecorded,
1504}
1505
1506#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1507pub struct ArchiveDeleteDependencySummary {
1508    pub status: ArchiveDeleteDependencyStatus,
1509    #[serde(default)]
1510    pub items: Vec<ArchiveDeleteDependencyPreview>,
1511}
1512
1513impl ArchiveDeleteDependencySummary {
1514    pub fn not_analyzed() -> Self {
1515        Self {
1516            status: ArchiveDeleteDependencyStatus::NotAnalyzed,
1517            items: Vec::new(),
1518        }
1519    }
1520}
1521
1522#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1523#[serde(rename_all = "snake_case")]
1524pub enum ArchiveDeleteDependencyStatus {
1525    NotAnalyzed,
1526    Clear,
1527    RequiresCascade,
1528    Blocked,
1529}
1530
1531#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1532pub struct ArchiveDeleteDependencyPreview {
1533    pub kind: String,
1534    pub identifier: String,
1535    pub action: ArchiveDeleteDependencyAction,
1536}
1537
1538#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1539#[serde(rename_all = "snake_case")]
1540pub enum ArchiveDeleteDependencyAction {
1541    Archive,
1542    Delete,
1543    MarkInactive,
1544    Retain,
1545    Block,
1546}
1547
1548#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1549pub struct ArchiveDeleteStagePreview {
1550    pub required: bool,
1551    pub disposition: ArchiveDeleteCleanupDisposition,
1552    pub retryable: bool,
1553    pub summary: String,
1554}
1555
1556impl ArchiveDeleteStagePreview {
1557    pub fn pending(required: bool, summary: impl Into<String>) -> Self {
1558        Self {
1559            required,
1560            disposition: ArchiveDeleteCleanupDisposition::Pending,
1561            retryable: false,
1562            summary: summary.into(),
1563        }
1564    }
1565}
1566
1567#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1568#[serde(rename_all = "snake_case")]
1569pub enum ArchiveDeleteCleanupDisposition {
1570    NotRequired,
1571    Pending,
1572    Completed,
1573    Skipped,
1574    FailedRetryable,
1575    FailedTerminal,
1576}
1577
1578#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1579#[serde(rename_all = "snake_case")]
1580pub enum ArchiveDeleteRetryState {
1581    NotStarted,
1582    NotRetryable,
1583    Retryable,
1584    Completed,
1585}
1586
1587#[derive(Debug, Clone, Serialize, Deserialize)]
1588pub struct GetTagsRequest {
1589    pub target: TagAttachmentTarget,
1590}
1591
1592#[derive(Debug, Clone, Serialize, Deserialize)]
1593pub struct SetTagsRequest {
1594    pub target: TagAttachmentTarget,
1595    #[serde(default)]
1596    pub tags: Vec<String>,
1597}
1598
1599#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1600pub struct TargetTags {
1601    pub target: TagAttachmentTarget,
1602    pub tags: Vec<TagRecord>,
1603}
1604
1605#[derive(Debug, Clone, Serialize, Deserialize)]
1606pub struct RegisterEntityRequest {
1607    pub entity: NewEntityRecord,
1608}
1609
1610#[derive(Debug, Clone, Serialize, Deserialize)]
1611pub struct RegisterEntityRelationRequest {
1612    pub relation: NewEntityRelationRecord,
1613}
1614
1615#[derive(Debug, Clone, Serialize, Deserialize)]
1616pub struct ListEntitiesRequest {
1617    pub domain: DomainSelector,
1618}
1619
1620#[derive(Debug, Clone, Serialize, Deserialize)]
1621pub struct GetEntityRequest {
1622    pub entity: EntitySelector,
1623}
1624
1625#[derive(Debug, Clone, Serialize, Deserialize)]
1626pub struct AddEntityRequest {
1627    pub domain: DomainSelector,
1628    pub name: String,
1629    pub description: Option<String>,
1630    pub agent_instructions: Option<String>,
1631    pub ontology_namespace: Option<String>,
1632    pub ontology_class: Option<String>,
1633}
1634
1635#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1636pub enum EntitySelector {
1637    Id {
1638        entity_id: EntityId,
1639    },
1640    Name {
1641        domain: DomainSelector,
1642        name: String,
1643    },
1644}
1645
1646impl From<(String, String)> for EntitySelector {
1647    fn from((domain, name): (String, String)) -> Self {
1648        Self::Name {
1649            domain: domain.into(),
1650            name,
1651        }
1652    }
1653}
1654
1655impl From<(&str, &str)> for EntitySelector {
1656    fn from((domain, name): (&str, &str)) -> Self {
1657        Self::Name {
1658            domain: domain.into(),
1659            name: name.to_string(),
1660        }
1661    }
1662}
1663
1664impl From<EntityId> for EntitySelector {
1665    fn from(entity_id: EntityId) -> Self {
1666        Self::Id { entity_id }
1667    }
1668}
1669
1670#[derive(Debug, Clone, Serialize, Deserialize)]
1671pub struct ListEntityRelationsRequest {
1672    pub domain: DomainSelector,
1673}
1674
1675#[derive(Debug, Clone, Serialize, Deserialize)]
1676pub struct GetEntityRelationRequest {
1677    pub relation: EntityRelationSelector,
1678}
1679
1680#[derive(Debug, Clone, Serialize, Deserialize)]
1681pub struct AddEntityRelationRequest {
1682    pub domain: DomainSelector,
1683    pub name: String,
1684    pub subject: String,
1685    pub object: String,
1686    pub description: Option<String>,
1687    pub agent_instructions: Option<String>,
1688    pub ontology_namespace: Option<String>,
1689    pub ontology_class: Option<String>,
1690}
1691
1692#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1693pub enum EntityRelationSelector {
1694    Id {
1695        entity_relation_id: EntityRelationId,
1696    },
1697    Name {
1698        domain: DomainSelector,
1699        name: String,
1700    },
1701}
1702
1703impl From<(String, String)> for EntityRelationSelector {
1704    fn from((domain, name): (String, String)) -> Self {
1705        Self::Name {
1706            domain: domain.into(),
1707            name,
1708        }
1709    }
1710}
1711
1712impl From<(&str, &str)> for EntityRelationSelector {
1713    fn from((domain, name): (&str, &str)) -> Self {
1714        Self::Name {
1715            domain: domain.into(),
1716            name: name.to_string(),
1717        }
1718    }
1719}
1720
1721impl From<EntityRelationId> for EntityRelationSelector {
1722    fn from(entity_relation_id: EntityRelationId) -> Self {
1723        Self::Id { entity_relation_id }
1724    }
1725}
1726
1727#[derive(Debug, Clone, Serialize, Deserialize)]
1728pub struct GetEntityInstanceRequest {
1729    pub instance_id: i64,
1730}
1731
1732#[derive(Debug, Clone, Serialize, Deserialize)]
1733pub struct ListEntityInstancesRequest {
1734    pub domain: String,
1735    pub entity: String,
1736}
1737
1738#[derive(Debug, Clone, Serialize, Deserialize)]
1739pub struct AddEntityInstanceRequest {
1740    pub domain: String,
1741    pub entity: String,
1742    pub transformation_id: TransformationId,
1743    pub label: Option<String>,
1744    pub note: Option<String>,
1745}
1746
1747#[derive(Debug, Clone, Serialize, Deserialize)]
1748pub struct AddStudyEntityInstanceMappingRequest {
1749    pub study: String,
1750    pub instance_id: i64,
1751    pub external_id: String,
1752    pub transformation_id: TransformationId,
1753}
1754
1755#[derive(Debug, Clone, Serialize, Deserialize)]
1756pub struct GetStudyEntityInstanceMappingRequest {
1757    pub study: String,
1758    pub domain: String,
1759    pub entity: String,
1760    pub external_id: String,
1761}
1762
1763#[derive(Debug, Clone, Serialize, Deserialize)]
1764pub struct EntityInstanceMapping {
1765    pub instance: EntityInstanceRecord,
1766    pub mapping: StudyEntityInstanceRecord,
1767}
1768
1769#[derive(Debug, Clone, Serialize, Deserialize)]
1770pub struct ListStudyEntityInstanceMappingsRequest {
1771    pub study: String,
1772    pub domain: Option<String>,
1773    pub entity: Option<String>,
1774}
1775
1776#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1777pub struct EntityInstanceMappingReadback {
1778    pub study: StudyRecord,
1779    pub domain: DomainRecord,
1780    pub entity: EntityRecord,
1781    pub instance: EntityInstanceRecord,
1782    pub mapping: StudyEntityInstanceRecord,
1783}
1784
1785#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1786pub struct EntityInstanceMappingList {
1787    pub study: StudyRecord,
1788    pub mappings: Vec<EntityInstanceMappingReadback>,
1789}
1790
1791#[derive(Debug, Clone, Serialize, Deserialize)]
1792pub struct AddEntityAssetLinkRequest {
1793    pub study: String,
1794    pub instance_id: i64,
1795    pub asset: String,
1796    pub version: Option<String>,
1797    pub transformation_id: TransformationId,
1798}
1799
1800#[derive(Debug, Clone, Serialize, Deserialize)]
1801pub struct ListEntityAssetLinksRequest {
1802    pub instance_id: Option<i64>,
1803    pub study: Option<String>,
1804    pub asset: Option<String>,
1805    pub version: Option<String>,
1806}
1807
1808#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1809pub struct EntityAssetLinkReadback {
1810    pub study: StudyRecord,
1811    pub asset: AssetRecord,
1812    pub version: AssetVersionRecord,
1813    pub domain: DomainRecord,
1814    pub entity: EntityRecord,
1815    pub instance: EntityInstanceRecord,
1816    pub study_mapping: Option<StudyEntityInstanceRecord>,
1817    pub link: AssetVersionEntityRecord,
1818}
1819
1820#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1821pub struct EntityAssetLinkList {
1822    pub resolved_asset: Option<AssetRecord>,
1823    pub resolved_version: Option<AssetVersionRecord>,
1824    pub links: Vec<EntityAssetLinkReadback>,
1825}
1826
1827#[derive(Debug, Clone, Serialize, Deserialize)]
1828pub struct AddEntityDatasetLinkRequest {
1829    pub study: String,
1830    pub instance_id: i64,
1831    pub dataset: String,
1832    pub variable: String,
1833    pub version: Option<String>,
1834    pub transformation_id: TransformationId,
1835}
1836
1837#[derive(Debug, Clone, Serialize, Deserialize)]
1838pub struct ListEntityInstanceDatasetsRequest {
1839    pub instance_id: i64,
1840}
1841
1842#[derive(Debug, Clone, Serialize, Deserialize)]
1843pub struct GetEntityDatasetLinkRequest {
1844    pub instance_id: i64,
1845    pub study: Option<String>,
1846    pub dataset: String,
1847    pub version: Option<String>,
1848}
1849
1850#[derive(Debug, Clone, Serialize, Deserialize)]
1851pub struct ListEntityDatasetLinksRequest {
1852    pub study: Option<String>,
1853    pub dataset: String,
1854    pub version: Option<String>,
1855}
1856
1857#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1858pub struct EntityDatasetLinkReadback {
1859    pub study: StudyRecord,
1860    pub dataset: AssetRecord,
1861    pub version: AssetVersionRecord,
1862    pub domain: DomainRecord,
1863    pub entity: EntityRecord,
1864    pub instance: EntityInstanceRecord,
1865    pub study_mapping: Option<StudyEntityInstanceRecord>,
1866    pub variable: VariableRecord,
1867    pub link: DatasetVersionEntityRecord,
1868}
1869
1870#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1871pub struct EntityDatasetLinkList {
1872    pub resolved_dataset: Option<AssetRecord>,
1873    pub resolved_version: Option<AssetVersionRecord>,
1874    pub links: Vec<EntityDatasetLinkReadback>,
1875}
1876
1877#[derive(Debug, Clone, Serialize, Deserialize)]
1878pub struct GetRelationInstanceRequest {
1879    pub relation_instance_id: i64,
1880}
1881
1882#[derive(Debug, Clone, Serialize, Deserialize)]
1883pub struct ListRelationInstancesRequest {
1884    pub domain: String,
1885    pub name: String,
1886}
1887
1888#[derive(Debug, Clone, Serialize, Deserialize)]
1889pub struct AddRelationInstanceRequest {
1890    pub domain: String,
1891    pub name: String,
1892    pub subject_instance_id: i64,
1893    pub object_instance_id: i64,
1894    pub transformation_id: TransformationId,
1895    pub valid_from: Option<NaiveDate>,
1896    pub valid_to: Option<NaiveDate>,
1897    pub note: Option<String>,
1898}
1899
1900#[derive(Debug, Clone, Serialize, Deserialize)]
1901pub struct AddStudyRelationInstanceMappingRequest {
1902    pub study: String,
1903    pub relation_instance_id: i64,
1904    pub external_id: String,
1905    pub transformation_id: TransformationId,
1906}
1907
1908#[derive(Debug, Clone, Serialize, Deserialize)]
1909pub struct GetStudyRelationInstanceMappingRequest {
1910    pub study: String,
1911    pub domain: String,
1912    pub name: String,
1913    pub external_id: String,
1914}
1915
1916#[derive(Debug, Clone, Serialize, Deserialize)]
1917pub struct RelationInstanceMapping {
1918    pub instance: RelationInstanceRecord,
1919    pub mapping: StudyRelationInstanceRecord,
1920}
1921
1922#[derive(Debug, Clone, Serialize, Deserialize)]
1923pub struct ListStudyRelationInstanceMappingsRequest {
1924    pub study: String,
1925    pub domain: Option<String>,
1926    pub relation: Option<String>,
1927}
1928
1929#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1930pub struct RelationInstanceMappingReadback {
1931    pub study: StudyRecord,
1932    pub domain: DomainRecord,
1933    pub relation: EntityRelationRecord,
1934    pub subject_entity: EntityRecord,
1935    pub object_entity: EntityRecord,
1936    pub subject_instance: EntityInstanceRecord,
1937    pub object_instance: EntityInstanceRecord,
1938    pub instance: RelationInstanceRecord,
1939    pub mapping: StudyRelationInstanceRecord,
1940}
1941
1942#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1943pub struct RelationInstanceMappingList {
1944    pub study: StudyRecord,
1945    pub mappings: Vec<RelationInstanceMappingReadback>,
1946}
1947
1948#[derive(Debug, Clone, Serialize, Deserialize)]
1949pub struct AddRelationAssetLinkRequest {
1950    pub study: String,
1951    pub relation_instance_id: i64,
1952    pub asset: String,
1953    pub version: Option<String>,
1954    pub transformation_id: TransformationId,
1955}
1956
1957#[derive(Debug, Clone, Serialize, Deserialize)]
1958pub struct ListRelationAssetLinksRequest {
1959    pub relation_instance_id: Option<i64>,
1960    pub study: Option<String>,
1961    pub asset: Option<String>,
1962    pub version: Option<String>,
1963}
1964
1965#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1966pub struct RelationAssetLinkReadback {
1967    pub study: StudyRecord,
1968    pub asset: AssetRecord,
1969    pub version: AssetVersionRecord,
1970    pub domain: DomainRecord,
1971    pub relation: EntityRelationRecord,
1972    pub subject_entity: EntityRecord,
1973    pub object_entity: EntityRecord,
1974    pub subject_instance: EntityInstanceRecord,
1975    pub object_instance: EntityInstanceRecord,
1976    pub instance: RelationInstanceRecord,
1977    pub study_mapping: Option<StudyRelationInstanceRecord>,
1978    pub link: AssetVersionRelationInstanceRecord,
1979}
1980
1981#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1982pub struct RelationAssetLinkList {
1983    pub resolved_asset: Option<AssetRecord>,
1984    pub resolved_version: Option<AssetVersionRecord>,
1985    pub links: Vec<RelationAssetLinkReadback>,
1986}
1987
1988#[derive(Debug, Clone, Serialize, Deserialize)]
1989pub struct AddRelationDatasetLinkRequest {
1990    pub study: String,
1991    pub relation_instance_id: i64,
1992    pub dataset: String,
1993    pub subject_variable: String,
1994    pub object_variable: String,
1995    pub relation_variable: Option<String>,
1996    pub version: Option<String>,
1997    pub transformation_id: TransformationId,
1998}
1999
2000#[derive(Debug, Clone, Serialize, Deserialize)]
2001pub struct GetRelationDatasetLinkRequest {
2002    pub relation_instance_id: i64,
2003    pub study: Option<String>,
2004    pub dataset: String,
2005    pub version: Option<String>,
2006}
2007
2008#[derive(Debug, Clone, Serialize, Deserialize)]
2009pub struct ListRelationDatasetLinksRequest {
2010    pub study: Option<String>,
2011    pub dataset: String,
2012    pub version: Option<String>,
2013}
2014
2015#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2016pub struct RelationDatasetLinkReadback {
2017    pub study: StudyRecord,
2018    pub dataset: AssetRecord,
2019    pub version: AssetVersionRecord,
2020    pub domain: DomainRecord,
2021    pub relation: EntityRelationRecord,
2022    pub subject_entity: EntityRecord,
2023    pub object_entity: EntityRecord,
2024    pub subject_instance: EntityInstanceRecord,
2025    pub object_instance: EntityInstanceRecord,
2026    pub instance: RelationInstanceRecord,
2027    pub study_mapping: Option<StudyRelationInstanceRecord>,
2028    pub subject_variable: VariableRecord,
2029    pub object_variable: VariableRecord,
2030    pub relation_variable: Option<VariableRecord>,
2031    pub link: DatasetVersionRelationInstanceRecord,
2032}
2033
2034#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2035pub struct RelationDatasetLinkList {
2036    pub resolved_dataset: Option<AssetRecord>,
2037    pub resolved_version: Option<AssetVersionRecord>,
2038    pub links: Vec<RelationDatasetLinkReadback>,
2039}
2040
2041#[derive(Debug, Clone, Serialize, Deserialize)]
2042pub struct EnsureStudyEntityInstanceRequest {
2043    pub study_id: StudyId,
2044    pub entity_id: EntityId,
2045    pub external_id: String,
2046    pub canonical_uuid: Option<uuid::Uuid>,
2047    pub label: Option<String>,
2048    pub note: Option<String>,
2049    pub transformation_id: TransformationId,
2050}
2051
2052#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2053pub struct StudyEntityInstanceRegistration {
2054    pub instance: EntityInstanceRecord,
2055    pub mapping: StudyEntityInstanceRecord,
2056}
2057
2058#[derive(Debug, Clone, Serialize, Deserialize)]
2059pub struct EnsureEntityInstancesFromDatasetRequest {
2060    pub study: String,
2061    pub domain: String,
2062    pub entity: String,
2063    pub dataset: String,
2064    pub version: Option<String>,
2065    pub external_id_variable: String,
2066    pub label_template: Option<String>,
2067    pub dry_run: bool,
2068}
2069
2070#[derive(Debug, Clone, Serialize, Deserialize)]
2071pub struct EnsureRelationInstancesFromDatasetRequest {
2072    pub study: String,
2073    pub domain: String,
2074    pub relation: String,
2075    pub dataset: String,
2076    pub version: Option<String>,
2077    pub relation_external_id_variable: String,
2078    pub subject_external_id_variable: String,
2079    pub object_external_id_variable: String,
2080    pub valid_from_variable: Option<String>,
2081    pub valid_to_variable: Option<String>,
2082    pub dry_run: bool,
2083}
2084
2085#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2086pub struct EntityInstanceDatasetBatchCounts {
2087    pub created_instances: usize,
2088    pub reused_instances: usize,
2089    pub created_study_mappings: usize,
2090    pub reused_study_mappings: usize,
2091    pub created_dataset_links: usize,
2092    pub reused_dataset_links: usize,
2093    pub rejected_rows: usize,
2094    pub conflicts: usize,
2095}
2096
2097#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2098pub struct EntityInstanceDatasetBatchConflict {
2099    pub external_id: String,
2100    pub message: String,
2101}
2102
2103#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2104pub struct EntityInstanceDatasetBatchRejectedRow {
2105    pub row_number: usize,
2106    pub reason: String,
2107}
2108
2109#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2110pub struct EntityInstanceDatasetBatchResult {
2111    pub schema_version: String,
2112    pub operation: String,
2113    pub dry_run: bool,
2114    pub study: String,
2115    pub domain: String,
2116    pub entity: String,
2117    pub dataset: String,
2118    pub requested_version: Option<String>,
2119    pub resolved_version: String,
2120    pub external_id_variable: String,
2121    pub label_template: Option<String>,
2122    pub transformation_id: Option<TransformationId>,
2123    pub transformation: Option<TransformationLineageRecord>,
2124    pub counts: EntityInstanceDatasetBatchCounts,
2125    pub conflicts: Vec<EntityInstanceDatasetBatchConflict>,
2126    pub rejected_rows: Vec<EntityInstanceDatasetBatchRejectedRow>,
2127}
2128
2129#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2130pub struct RelationInstanceDatasetBatchCounts {
2131    pub created_instances: usize,
2132    pub reused_instances: usize,
2133    pub created_study_mappings: usize,
2134    pub reused_study_mappings: usize,
2135    pub created_dataset_links: usize,
2136    pub reused_dataset_links: usize,
2137    pub missing_endpoints: usize,
2138    pub rejected_rows: usize,
2139    pub conflicts: usize,
2140}
2141
2142#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2143pub struct RelationInstanceDatasetBatchConflict {
2144    pub external_id: String,
2145    pub message: String,
2146}
2147
2148#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2149pub struct RelationInstanceDatasetBatchMissingEndpoint {
2150    pub relation_external_id: String,
2151    pub endpoint_role: String,
2152    pub endpoint_external_id: String,
2153    pub entity_id: EntityId,
2154}
2155
2156#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2157pub struct RelationInstanceDatasetBatchRejectedRow {
2158    pub row_number: usize,
2159    pub reason: String,
2160}
2161
2162#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2163pub struct RelationInstanceDatasetBatchResult {
2164    pub schema_version: String,
2165    pub operation: String,
2166    pub dry_run: bool,
2167    pub study: String,
2168    pub domain: String,
2169    pub relation: String,
2170    pub dataset: String,
2171    pub requested_version: Option<String>,
2172    pub resolved_version: String,
2173    pub relation_external_id_variable: String,
2174    pub subject_external_id_variable: String,
2175    pub object_external_id_variable: String,
2176    pub valid_from_variable: Option<String>,
2177    pub valid_to_variable: Option<String>,
2178    pub transformation_id: Option<TransformationId>,
2179    pub transformation: Option<TransformationLineageRecord>,
2180    pub counts: RelationInstanceDatasetBatchCounts,
2181    pub conflicts: Vec<RelationInstanceDatasetBatchConflict>,
2182    pub missing_endpoints: Vec<RelationInstanceDatasetBatchMissingEndpoint>,
2183    pub rejected_rows: Vec<RelationInstanceDatasetBatchRejectedRow>,
2184}
2185
2186#[derive(Debug, Clone, Serialize, Deserialize)]
2187pub struct EnsureStudyRelationInstanceRequest {
2188    pub study_id: StudyId,
2189    pub entity_relation_id: EntityRelationId,
2190    pub external_id: String,
2191    pub subject_entity_instance_id: i64,
2192    pub object_entity_instance_id: i64,
2193    pub valid_from: Option<NaiveDate>,
2194    pub valid_to: Option<NaiveDate>,
2195    pub note: Option<String>,
2196    pub transformation_id: TransformationId,
2197}
2198
2199#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2200pub struct StudyRelationInstanceRegistration {
2201    pub instance: RelationInstanceRecord,
2202    pub mapping: StudyRelationInstanceRecord,
2203}
2204
2205#[derive(Debug, Clone, Serialize, Deserialize)]
2206pub struct LinkDatasetVersionEntitiesRequest {
2207    #[serde(default)]
2208    pub links: Vec<DatasetVersionEntityRecord>,
2209}
2210
2211#[derive(Debug, Clone, Serialize, Deserialize)]
2212pub struct LinkDatasetVersionRelationInstancesRequest {
2213    #[serde(default)]
2214    pub links: Vec<DatasetVersionRelationInstanceRecord>,
2215}
2216
2217#[derive(Debug, Clone, Serialize, Deserialize)]
2218pub struct ListStudyTransformationsRequest {
2219    pub study: String,
2220}
2221
2222#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2223pub struct TransformationSummary {
2224    pub transformation: TransformationRecord,
2225    pub inputs: Vec<ahri_tre_types::TransformationInputLinkRecord>,
2226    pub outputs: Vec<ahri_tre_types::TransformationOutputLinkRecord>,
2227}
2228
2229#[derive(Debug, Clone, Serialize, Deserialize)]
2230pub struct SqlToDatasetRequest {
2231    pub classification: ahri_tre_types::IngestClassification,
2232    pub study_id: StudyId,
2233    pub domain_id: DomainId,
2234    pub dataset_asset_id: Option<AssetId>,
2235    pub dataset_name: NcName,
2236    pub dataset_version_id: Option<VersionId>,
2237    pub sql: String,
2238    pub description: Option<String>,
2239    pub agent_instructions: Option<String>,
2240    pub version_note: Option<String>,
2241    pub transformation: NewTransformationRecord,
2242    pub force_metadata_updates: bool,
2243}
2244
2245#[derive(Debug, Clone, Serialize, Deserialize)]
2246pub struct DatasetSqlTransformRequest {
2247    pub study_id: StudyId,
2248    pub dataset_asset_id: Option<AssetId>,
2249    pub dataset_name: NcName,
2250    pub dataset_version_id: Option<VersionId>,
2251    pub inputs: std::collections::BTreeMap<String, VersionId>,
2252    pub views: std::collections::BTreeMap<String, String>,
2253    pub budgets: ahri_tre_types::DisclosureBudgetOverrides,
2254    pub sql: String,
2255    pub description: Option<String>,
2256    pub agent_instructions: Option<String>,
2257    pub version_note: Option<String>,
2258    pub transformation: NewTransformationRecord,
2259}
2260
2261#[derive(Debug, Clone, Serialize, Deserialize)]
2262pub struct OfflineRedcapIngestRequest {
2263    #[serde(default)]
2264    pub no_register_dictionary: bool,
2265    pub classification: ahri_tre_types::IngestClassification,
2266    pub study_id: Option<StudyId>,
2267    pub domain_id: Option<DomainId>,
2268    pub domain_name: Option<NcName>,
2269    pub study_name: Option<NcName>,
2270    pub project_info_path: String,
2271    pub metadata_path: String,
2272    pub records_eav_path: String,
2273    #[serde(default)]
2274    pub forms: Vec<String>,
2275    #[serde(default)]
2276    pub skip_forms: Vec<String>,
2277    pub dataset_prefix: Option<NcName>,
2278    pub description: Option<String>,
2279    pub agent_instructions: Option<String>,
2280    pub version_note: Option<String>,
2281    pub ingest_transformation: NewTransformationRecord,
2282    pub transform_transformation: NewTransformationRecord,
2283    pub force_metadata_updates: bool,
2284    pub compress: bool,
2285    pub encrypt: Option<bool>,
2286}
2287
2288#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2289pub struct RedcapIngestResult {
2290    pub form_outcomes: Vec<ahri_tre_protocol::ingest::RedcapFormOutcome>,
2291    pub study: StudyRecord,
2292    pub datafiles: Vec<IngestedFileAsset>,
2293    pub ingest_lineage: TransformationLineageRecord,
2294    pub datasets: Vec<DatasetMaterialization>,
2295    pub warnings: Vec<String>,
2296}
2297
2298#[derive(Debug, Clone, Serialize, Deserialize)]
2299pub struct AnalyzeSqlSourceRequest {
2300    pub sql: String,
2301}
2302
2303#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2304pub struct SqlSourceAnalysis {
2305    pub source_kind: String,
2306    pub source_description: String,
2307    pub columns: Vec<SqlSourceColumnAnalysis>,
2308    pub warnings: Vec<String>,
2309}
2310
2311#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2312pub struct SqlSourceColumnAnalysis {
2313    pub name: String,
2314    pub sql_type: String,
2315    pub value_type_id: ahri_tre_types::ValueTypeId,
2316    pub value_type: String,
2317    pub description: Option<String>,
2318    pub source_table: Option<String>,
2319    pub source_column: Option<String>,
2320    pub vocabulary: Option<SqlSourceVocabularyAnalysis>,
2321}
2322
2323#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2324pub struct SqlSourceVocabularyAnalysis {
2325    pub name: String,
2326    pub description: Option<String>,
2327    pub items: Vec<SqlSourceVocabularyItemAnalysis>,
2328}
2329
2330#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2331pub struct SqlSourceVocabularyItemAnalysis {
2332    pub value: i32,
2333    pub code: String,
2334    pub description: Option<String>,
2335}
2336
2337#[derive(Debug, Clone)]
2338pub struct OpenDuckDbSourceRequest {
2339    pub path: PathBuf,
2340}
2341
2342#[derive(Debug, Clone)]
2343pub struct OpenSqliteSourceRequest {
2344    pub path: PathBuf,
2345}
2346
2347#[derive(Debug, Clone)]
2348pub struct OpenPostgresSourceRequest {
2349    pub config: ahri_tre_pgmeta::DirectPostgresConnectConfig,
2350    pub schema: Option<String>,
2351}
2352
2353#[derive(Debug, Clone)]
2354pub struct OpenMsSqlSourceRequest {
2355    pub connection_string: String,
2356    pub safe_description: Option<String>,
2357    pub schema: Option<String>,
2358}
2359
2360#[derive(Debug, Clone, Serialize, Deserialize)]
2361pub struct RegisterDomainRequest {
2362    pub domain: DomainRecord,
2363}
2364
2365#[derive(Debug, Clone, Serialize, Deserialize)]
2366pub struct RegisterNewDomainRequest {
2367    pub domain: NewDomainRecord,
2368}
2369
2370#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2371pub struct ListDomainsRequest {}
2372
2373/// Transport-neutral result from the Session-scoped Domain list workflow.
2374#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2375pub struct DomainListResult {
2376    pub domains: Vec<DomainRecord>,
2377}
2378
2379#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2380pub enum DomainSelector {
2381    Id { domain_id: DomainId },
2382    Name { name: String },
2383}
2384
2385impl From<String> for DomainSelector {
2386    fn from(name: String) -> Self {
2387        Self::Name { name }
2388    }
2389}
2390
2391impl From<&str> for DomainSelector {
2392    fn from(name: &str) -> Self {
2393        Self::Name {
2394            name: name.to_string(),
2395        }
2396    }
2397}
2398
2399impl From<&String> for DomainSelector {
2400    fn from(name: &String) -> Self {
2401        Self::from(name.as_str())
2402    }
2403}
2404
2405impl From<DomainId> for DomainSelector {
2406    fn from(domain_id: DomainId) -> Self {
2407        Self::Id { domain_id }
2408    }
2409}
2410
2411#[derive(Debug, Clone, Serialize, Deserialize)]
2412pub struct GetDomainRequest {
2413    pub domain: DomainSelector,
2414}
2415
2416#[derive(Debug, Clone, Serialize, Deserialize)]
2417pub struct AddDomainRequest {
2418    pub name: String,
2419    pub description: Option<String>,
2420    pub uri: Option<String>,
2421}
2422
2423/// Transport-neutral result from the Session-scoped Domain add workflow.
2424#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2425pub struct DomainAddResult {
2426    pub domain: DomainRecord,
2427}
2428
2429#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2430pub struct ListStudiesRequest {}
2431
2432/// Transport-neutral result from the Session-scoped Study list workflow.
2433#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2434pub struct StudyListResult {
2435    pub studies: Vec<StudyRegistration>,
2436}
2437
2438#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2439pub struct SearchStudiesRequest {
2440    pub kind: PredicateSetKind,
2441    pub predicates: Vec<StudySearchPredicate>,
2442    pub limit: u16,
2443    pub after: Option<StudySearchCursorAnchor>,
2444}
2445
2446#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2447pub struct StudySearchEntry {
2448    pub study: StudyRecord,
2449    pub domains: Vec<DomainRecord>,
2450    pub tags: Vec<TagRecord>,
2451}
2452
2453#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2454pub struct StudySearchResult {
2455    pub studies: Vec<StudySearchEntry>,
2456    pub next_cursor: Option<StudySearchCursorAnchor>,
2457}
2458
2459#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2460pub struct SearchDatasetsRequest {
2461    pub kind: PredicateSetKind,
2462    pub predicates: Vec<DatasetSearchPredicate>,
2463    pub limit: u16,
2464    pub after: Option<DatasetSearchCursorAnchor>,
2465}
2466
2467#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2468pub struct DatasetSearchEntry {
2469    pub study: StudyRecord,
2470    pub asset: AssetRecord,
2471    pub latest_version: AssetVersionRecord,
2472    pub variable_count: Option<u64>,
2473    pub tags: Vec<TagRecord>,
2474}
2475
2476#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2477pub struct DatasetSearchResult {
2478    pub datasets: Vec<DatasetSearchEntry>,
2479    pub next_cursor: Option<DatasetSearchCursorAnchor>,
2480}
2481
2482#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2483pub struct SearchDataFilesRequest {
2484    pub kind: PredicateSetKind,
2485    pub predicates: Vec<DataFileSearchPredicate>,
2486    pub limit: u16,
2487    pub after: Option<DataFileSearchCursorAnchor>,
2488}
2489
2490#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2491pub struct DataFileSearchEntry {
2492    pub study: StudyRecord,
2493    pub asset: AssetRecord,
2494    pub latest_version: AssetVersionRecord,
2495    pub datafile: DataFileRecord,
2496    pub tags: Vec<TagRecord>,
2497}
2498
2499#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2500pub struct DataFileSearchResult {
2501    pub datafiles: Vec<DataFileSearchEntry>,
2502    pub next_cursor: Option<DataFileSearchCursorAnchor>,
2503}
2504
2505#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2506pub enum StudySelector {
2507    Id {
2508        study_id: StudyId,
2509    },
2510    Name {
2511        domain: Option<DomainSelector>,
2512        name: String,
2513    },
2514}
2515
2516impl From<String> for StudySelector {
2517    fn from(name: String) -> Self {
2518        Self::Name { domain: None, name }
2519    }
2520}
2521
2522impl From<&str> for StudySelector {
2523    fn from(name: &str) -> Self {
2524        Self::Name {
2525            domain: None,
2526            name: name.to_string(),
2527        }
2528    }
2529}
2530
2531impl From<&String> for StudySelector {
2532    fn from(name: &String) -> Self {
2533        Self::from(name.as_str())
2534    }
2535}
2536
2537impl From<StudyId> for StudySelector {
2538    fn from(study_id: StudyId) -> Self {
2539        Self::Id { study_id }
2540    }
2541}
2542
2543#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2544pub enum VariableSelector {
2545    Id {
2546        variable_id: VariableId,
2547    },
2548    Name {
2549        domain: DomainSelector,
2550        name: String,
2551    },
2552}
2553
2554impl From<(String, String)> for VariableSelector {
2555    fn from((domain, name): (String, String)) -> Self {
2556        Self::Name {
2557            domain: domain.into(),
2558            name,
2559        }
2560    }
2561}
2562
2563impl From<(&str, &str)> for VariableSelector {
2564    fn from((domain, name): (&str, &str)) -> Self {
2565        Self::Name {
2566            domain: domain.into(),
2567            name: name.to_string(),
2568        }
2569    }
2570}
2571
2572impl From<VariableId> for VariableSelector {
2573    fn from(variable_id: VariableId) -> Self {
2574        Self::Id { variable_id }
2575    }
2576}
2577
2578#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2579pub enum VocabularySelector {
2580    Id {
2581        vocabulary_id: VocabularyId,
2582    },
2583    Name {
2584        domain: DomainSelector,
2585        name: String,
2586    },
2587}
2588
2589impl From<(String, String)> for VocabularySelector {
2590    fn from((domain, name): (String, String)) -> Self {
2591        Self::Name {
2592            domain: domain.into(),
2593            name,
2594        }
2595    }
2596}
2597
2598impl From<(&str, &str)> for VocabularySelector {
2599    fn from((domain, name): (&str, &str)) -> Self {
2600        Self::Name {
2601            domain: domain.into(),
2602            name: name.to_string(),
2603        }
2604    }
2605}
2606
2607impl From<VocabularyId> for VocabularySelector {
2608    fn from(vocabulary_id: VocabularyId) -> Self {
2609        Self::Id { vocabulary_id }
2610    }
2611}
2612
2613#[derive(Debug, Clone, Serialize, Deserialize)]
2614pub struct GetStudyRequest {
2615    pub study: StudySelector,
2616}
2617
2618#[derive(Debug, Clone, Serialize, Deserialize)]
2619pub struct AddStudyRequest {
2620    pub name: String,
2621    pub external_id: String,
2622    pub study_type: String,
2623    pub domain: DomainSelector,
2624    pub description: Option<String>,
2625}
2626
2627/// Transport-neutral result from the Session-scoped Study add workflow.
2628#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2629pub struct StudyAddResult {
2630    pub registration: StudyRegistration,
2631}
2632
2633#[derive(Debug, Clone, Serialize, Deserialize)]
2634pub struct AddStudyDomainRequest {
2635    pub study: StudySelector,
2636    pub domain: DomainSelector,
2637}
2638
2639#[derive(Debug, Clone, Serialize, Deserialize)]
2640pub struct RegisterStudyRequest {
2641    pub study: StudyRecord,
2642    pub domain_ids: Vec<DomainId>,
2643}
2644
2645#[derive(Debug, Clone, Serialize, Deserialize)]
2646pub struct RegisterNewStudyRequest {
2647    pub study: NewStudyRecord,
2648    pub domain_ids: Vec<DomainId>,
2649}
2650
2651#[derive(Debug, Clone, Serialize, Deserialize)]
2652pub struct RegisterDomainStudyRequest {
2653    pub domain: DomainRecord,
2654    pub study: StudyRecord,
2655}
2656
2657#[derive(Debug, Clone, Serialize, Deserialize)]
2658pub struct GetStudyRegistrationRequest {
2659    pub study_id: Option<StudyId>,
2660    pub study_name: Option<NcName>,
2661}
2662
2663#[derive(Debug, Clone, Serialize, Deserialize)]
2664pub struct ListStudyAccessGrantsRequest {
2665    pub study_id: StudyId,
2666}
2667
2668#[derive(Debug, Clone, Serialize, Deserialize)]
2669pub struct GrantStudyAccessRequest {
2670    pub study_id: StudyId,
2671    pub target_user_id: String,
2672}
2673
2674#[derive(Debug, Clone, Serialize, Deserialize)]
2675pub struct RevokeStudyAccessRequest {
2676    pub study_id: StudyId,
2677    pub target_user_id: String,
2678}
2679
2680#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2681pub struct StudyAccessGrant {
2682    pub grant: StudyAccessGrantRecord,
2683}
2684
2685#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2686pub struct StudyAccessRevocation {
2687    pub study_id: StudyId,
2688    pub target_user_id: String,
2689}
2690
2691#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2692pub struct StudyAccessGrantList {
2693    pub study_id: StudyId,
2694    pub grants: Vec<StudyAccessGrantRecord>,
2695}
2696
2697#[derive(Debug, Clone, Serialize, Deserialize)]
2698pub struct ListStudyCustodiansRequest {
2699    pub study_id: StudyId,
2700}
2701
2702#[derive(Debug, Clone, Serialize, Deserialize)]
2703pub struct AddDelegateCustodianRequest {
2704    pub study_id: StudyId,
2705    pub target_user_id: String,
2706}
2707
2708#[derive(Debug, Clone, Serialize, Deserialize)]
2709pub struct TransferPrimaryCustodianshipRequest {
2710    pub study_id: StudyId,
2711    pub target_user_id: String,
2712}
2713
2714#[derive(Debug, Clone, Serialize, Deserialize)]
2715pub struct RemoveDelegateCustodianRequest {
2716    pub study_id: StudyId,
2717    pub target_user_id: String,
2718}
2719
2720#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2721pub struct StudyCustodian {
2722    pub custodian: StudyCustodianLinkRecord,
2723}
2724
2725#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2726pub struct StudyPrimaryCustodianshipTransfer {
2727    pub primary_custodian: StudyCustodianLinkRecord,
2728}
2729
2730#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2731pub struct StudyDelegateCustodianRemoval {
2732    pub study_id: StudyId,
2733    pub target_user_id: String,
2734}
2735
2736#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2737pub struct StudyCustodianList {
2738    pub study_id: StudyId,
2739    pub custodians: Vec<StudyCustodianLinkRecord>,
2740}
2741
2742#[derive(Debug, Clone, Serialize, Deserialize)]
2743pub struct ListStudyDuoRestrictionsRequest {
2744    pub study_id: StudyId,
2745}
2746
2747#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2748pub struct StudyDuoRestrictionInput {
2749    pub code: Option<String>,
2750    pub duo_id: Option<String>,
2751    pub qualifier_ontology_namespace: Option<String>,
2752    pub qualifier_ontology_id: Option<String>,
2753    pub qualifier_text: Option<String>,
2754    pub qualifier_date: Option<NaiveDate>,
2755    pub qualifier_integer: Option<i32>,
2756}
2757
2758impl StudyDuoRestrictionInput {
2759    pub fn into_new_restriction(
2760        self,
2761        study_id: StudyId,
2762    ) -> Result<NewStudyDuoRestrictionRecord, String> {
2763        let code = self
2764            .code
2765            .as_deref()
2766            .map(str::trim)
2767            .filter(|v| !v.is_empty());
2768        let duo_id = self
2769            .duo_id
2770            .as_deref()
2771            .map(str::trim)
2772            .filter(|v| !v.is_empty());
2773        let resolved_duo_id = match (code, duo_id) {
2774            (None, None) => return Err("DUO restriction requires code or duo_id".to_string()),
2775            (Some(_), Some(_)) => {
2776                return Err("DUO restriction accepts code or duo_id, not both".to_string());
2777            }
2778            (Some(value), None) | (None, Some(value)) => value.to_string(),
2779        };
2780
2781        Ok(NewStudyDuoRestrictionRecord {
2782            study_id,
2783            duo_id: resolved_duo_id,
2784            qualifier_ontology_namespace: self.qualifier_ontology_namespace,
2785            qualifier_ontology_id: self.qualifier_ontology_id,
2786            qualifier_text: self.qualifier_text,
2787            qualifier_date: self.qualifier_date,
2788            qualifier_integer: self.qualifier_integer,
2789        })
2790    }
2791}
2792
2793#[derive(Debug, Clone, Serialize, Deserialize)]
2794pub struct ReplaceStudyDuoRestrictionsRequest {
2795    pub study_id: StudyId,
2796    pub restrictions: Vec<StudyDuoRestrictionInput>,
2797}
2798
2799#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2800pub struct AssetVersionDuoRestrictionInput {
2801    pub code: Option<String>,
2802    pub duo_id: Option<String>,
2803    pub qualifier_ontology_namespace: Option<String>,
2804    pub qualifier_ontology_id: Option<String>,
2805    pub qualifier_text: Option<String>,
2806    pub qualifier_date: Option<NaiveDate>,
2807    pub qualifier_integer: Option<i32>,
2808}
2809
2810impl AssetVersionDuoRestrictionInput {
2811    pub fn into_new_restriction(
2812        self,
2813        version_id: VersionId,
2814    ) -> Result<NewAssetVersionDuoRestrictionRecord, String> {
2815        let code = self
2816            .code
2817            .as_deref()
2818            .map(str::trim)
2819            .filter(|v| !v.is_empty());
2820        let duo_id = self
2821            .duo_id
2822            .as_deref()
2823            .map(str::trim)
2824            .filter(|v| !v.is_empty());
2825        let resolved_duo_id = match (code, duo_id) {
2826            (None, None) => return Err("DUO restriction requires code or duo_id".to_string()),
2827            (Some(_), Some(_)) => {
2828                return Err("DUO restriction accepts code or duo_id, not both".to_string());
2829            }
2830            (Some(value), None) | (None, Some(value)) => value.to_string(),
2831        };
2832
2833        Ok(NewAssetVersionDuoRestrictionRecord {
2834            version_id,
2835            duo_id: resolved_duo_id,
2836            qualifier_ontology_namespace: self.qualifier_ontology_namespace,
2837            qualifier_ontology_id: self.qualifier_ontology_id,
2838            qualifier_text: self.qualifier_text,
2839            qualifier_date: self.qualifier_date,
2840            qualifier_integer: self.qualifier_integer,
2841        })
2842    }
2843}
2844
2845#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2846pub struct StudyDuoRestrictionList {
2847    pub study_id: StudyId,
2848    pub restrictions: Vec<StudyDuoRestrictionRecord>,
2849}
2850
2851#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2852pub struct StudyDuoRestrictionReplacement {
2853    pub study_id: StudyId,
2854    pub restrictions: Vec<StudyDuoRestrictionRecord>,
2855}
2856
2857#[derive(Debug, Clone, Serialize, Deserialize)]
2858pub struct ListAssetVersionDuoRestrictionsRequest {
2859    pub version_id: VersionId,
2860}
2861
2862#[derive(Debug, Clone, Serialize, Deserialize)]
2863pub struct ListEffectiveAssetVersionDuoRestrictionsRequest {
2864    pub study_id: StudyId,
2865    pub version_id: VersionId,
2866}
2867
2868#[derive(Debug, Clone, Serialize, Deserialize)]
2869pub struct ReplaceAssetVersionDuoRestrictionsRequest {
2870    pub version_id: VersionId,
2871    pub restrictions: Vec<AssetVersionDuoRestrictionInput>,
2872}
2873
2874#[derive(Debug, Clone, Serialize, Deserialize)]
2875pub struct ClearAssetVersionDuoRestrictionsRequest {
2876    pub version_id: VersionId,
2877}
2878
2879#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2880pub struct AssetVersionDuoRestrictionList {
2881    pub version_id: VersionId,
2882    pub restrictions: Vec<AssetVersionDuoRestrictionRecord>,
2883}
2884
2885#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2886#[serde(rename_all = "kebab-case")]
2887pub enum AssetDuoRestrictionSource {
2888    AssetVersion,
2889    StudyDefault,
2890}
2891
2892#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2893pub struct EffectiveAssetVersionDuoRestriction {
2894    pub source: AssetDuoRestrictionSource,
2895    pub duo_id: String,
2896    pub qualifier_ontology_namespace: Option<String>,
2897    pub qualifier_ontology_id: Option<String>,
2898    pub qualifier_text: Option<String>,
2899    pub qualifier_date: Option<NaiveDate>,
2900    pub qualifier_integer: Option<i32>,
2901}
2902
2903#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2904pub struct EffectiveAssetVersionDuoRestrictionList {
2905    pub study_id: StudyId,
2906    pub version_id: VersionId,
2907    pub restrictions: Vec<EffectiveAssetVersionDuoRestriction>,
2908}
2909
2910#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2911pub struct AssetVersionDuoRestrictionReplacement {
2912    pub version_id: VersionId,
2913    pub restrictions: Vec<AssetVersionDuoRestrictionRecord>,
2914}
2915
2916#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2917pub struct AssetVersionDuoRestrictionClear {
2918    pub version_id: VersionId,
2919    pub restrictions: Vec<AssetVersionDuoRestrictionRecord>,
2920}
2921
2922#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2923pub struct StudyRegistration {
2924    pub study: StudyRecord,
2925    pub domains: Vec<DomainRecord>,
2926    pub tags: Vec<TagRecord>,
2927}
2928
2929#[derive(Debug, Clone, Serialize, Deserialize)]
2930pub struct CreateAssetRequest {
2931    pub asset: AssetRecord,
2932}
2933
2934#[derive(Debug, Clone, Serialize, Deserialize)]
2935pub struct CreateNewAssetRequest {
2936    pub asset: NewAssetRecord,
2937}
2938
2939#[derive(Debug, Clone, Serialize, Deserialize)]
2940pub struct CreateAssetVersionRequest {
2941    pub classification: ahri_tre_types::IngestClassification,
2942    pub version: AssetVersionRecord,
2943}
2944
2945#[derive(Debug, Clone, Serialize, Deserialize)]
2946pub struct CreateNewAssetVersionRequest {
2947    pub version: NewAssetVersionRecord,
2948}
2949
2950#[derive(Debug, Clone, Serialize, Deserialize)]
2951pub struct GetAssetRequest {
2952    pub asset_id: Option<AssetId>,
2953    pub study_id: Option<StudyId>,
2954    pub asset_name: Option<NcName>,
2955}
2956
2957#[derive(Debug, Clone, Serialize, Deserialize)]
2958pub struct ListStudyAssetsRequest {
2959    pub study_id: StudyId,
2960}
2961
2962#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2963pub struct AssetVersionCatalog {
2964    pub asset: AssetRecord,
2965    pub versions: Vec<AssetVersionRecord>,
2966    pub latest_version: Option<AssetVersionRecord>,
2967}
2968
2969#[derive(Debug, Clone, Serialize, Deserialize)]
2970pub struct RecordTransformationRequest {
2971    pub transformation: NewTransformationRecord,
2972    pub input_version_ids: Vec<VersionId>,
2973    pub output_version_ids: Vec<VersionId>,
2974}
2975
2976#[derive(Debug, Clone, Serialize, Deserialize)]
2977pub struct TransformAssetsRequest {
2978    pub transformation: NewTransformationRecord,
2979    pub input_version_ids: Vec<VersionId>,
2980    pub output_version_ids: Vec<VersionId>,
2981}
2982
2983#[derive(Debug, Clone, Serialize, Deserialize)]
2984pub struct RecordExportRequest {
2985    pub transformation: NewTransformationRecord,
2986    pub input_version_ids: Vec<VersionId>,
2987}