1use crate::{MetadataExecutor, PgMetaError, PgMetadataConnection};
7use ahri_tre_types::{
8 AssetId, AssetType, DomainId, EntityId, EntityRelationId, KeyRole, StudyId, StudyTypeId,
9 TransformationId, TransformationType, ValueTypeId, VariableId, VersionId, VocabularyId,
10 VocabularyItemId,
11};
12use chrono::{DateTime, NaiveDate, NaiveDateTime, Utc};
13use postgres::Row;
14use uuid::Uuid;
15
16pub const METADATA_TABLES: &[&str] = &[
18 "datastore_identity",
19 "duo_codes",
20 "study_types",
21 "studies",
22 "study_access",
23 "study_access_requests",
24 "study_custodians",
25 "domains",
26 "study_domains",
27 "study_duo_restrictions",
28 "transformations",
29 "value_types",
30 "vocabularies",
31 "vocabulary_items",
32 "variables",
33 "vocabulary_mapping",
34 "assets",
35 "asset_versions",
36 "datasets",
37 "dataset_admission_receipts",
38 "dataset_executor_generation",
39 "dataset_output_reservations",
40 "dataset_output_attempts",
41 "operations",
42 "operation_idempotency",
43 "dataset_version_withdrawals",
44 "datafiles",
45 "asset_version_duo_restrictions",
46 "transformation_inputs",
47 "transformation_outputs",
48 "dataset_variables",
49 "entities",
50 "entityrelations",
51 "entity_instances",
52 "study_entity_instances",
53 "relation_instances",
54 "study_relation_instances",
55 "asset_version_entities",
56 "dataset_version_entities",
57 "asset_version_relation_instances",
58 "dataset_version_relation_instances",
59 "tags",
60 "study_tags",
61 "domain_tags",
62 "variable_tags",
63 "asset_tags",
64 "asset_version_tags",
65 "entity_tags",
66 "entityrelation_tags",
67];
68
69#[derive(Debug, Clone, PartialEq, Eq)]
70pub struct PgStudyReadModel {
71 pub study_id: StudyId,
72 pub name: String,
73 pub description: Option<String>,
74 pub documentation: Option<String>,
75 pub agent_instructions: Option<String>,
76 pub external_id: Option<String>,
77 pub study_type_id: Option<StudyTypeId>,
78 pub date_created: Option<DateTime<Utc>>,
79 pub created_by: Option<String>,
80}
81
82#[derive(Debug, Clone, PartialEq, Eq)]
83pub struct PgStudyTypeReadModel {
84 pub study_type_id: StudyTypeId,
85 pub name: String,
86 pub namespace: Option<String>,
87 pub ontology_class: Option<String>,
88 pub description: Option<String>,
89}
90
91#[derive(Debug, Clone, PartialEq, Eq)]
92pub struct PgDomainReadModel {
93 pub domain_id: DomainId,
94 pub name: String,
95 pub uri: Option<String>,
96 pub description: Option<String>,
97}
98
99#[derive(Debug, Clone, PartialEq, Eq)]
100pub struct PgStudyDomainReadModel {
101 pub study_id: StudyId,
102 pub domain_id: DomainId,
103}
104
105#[derive(Debug, Clone, PartialEq, Eq)]
106pub struct PgDuoCodeReadModel {
107 pub duo_id: String,
108 pub duo_code: String,
109 pub label: String,
110 pub definition: String,
111 pub category: String,
112 pub qualifier_type: String,
113 pub qualifier_required: bool,
114 pub qualifier_note: Option<String>,
115}
116
117#[derive(Debug, Clone, PartialEq, Eq)]
118pub struct PgStudyAccessReadModel {
119 pub study_id: StudyId,
120 pub user_id: String,
121 pub date_granted: Option<DateTime<Utc>>,
122 pub granted_by: Option<String>,
123}
124
125#[derive(Debug, Clone, PartialEq, Eq)]
126pub struct PgStudyCustodianReadModel {
127 pub study_id: StudyId,
128 pub user_id: String,
129 pub is_primary: bool,
130 pub date_granted: Option<DateTime<Utc>>,
131 pub granted_by: Option<String>,
132}
133
134#[derive(Debug, Clone, PartialEq, Eq)]
135pub struct PgDuoRestrictionReadModel {
136 pub restriction_id: i64,
137 pub study_id: StudyId,
138 pub duo_id: String,
139 pub duo_code: String,
140 pub label: String,
141 pub qualifier_ontology_namespace: Option<String>,
142 pub qualifier_ontology_id: Option<String>,
143 pub qualifier_text: Option<String>,
144 pub qualifier_date: Option<NaiveDate>,
145 pub qualifier_integer: Option<i32>,
146}
147
148#[derive(Debug, Clone, PartialEq, Eq)]
149pub struct PgAssetReadModel {
150 pub asset_id: AssetId,
151 pub study_id: StudyId,
152 pub name: String,
153 pub description: Option<String>,
154 pub agent_instructions: Option<String>,
155 pub asset_type: AssetType,
156 pub date_created: Option<DateTime<Utc>>,
157 pub created_by: Option<String>,
158}
159
160#[derive(Debug, Clone, PartialEq, Eq)]
161pub struct PgAssetVersionReadModel {
162 pub version_id: VersionId,
163 pub asset_id: AssetId,
164 pub major: i32,
165 pub minor: i32,
166 pub patch: i32,
167 pub version_label: Option<String>,
168 pub version_note: Option<String>,
169 pub is_latest: Option<bool>,
170 pub doi: Option<String>,
171 pub created_at: Option<DateTime<Utc>>,
172 pub created_by: Option<String>,
173}
174
175#[derive(Debug, Clone, PartialEq, Eq)]
176pub struct PgAssetVersionDuoRestrictionReadModel {
177 pub restriction_id: i64,
178 pub version_id: VersionId,
179 pub duo_id: String,
180 pub duo_code: String,
181 pub label: String,
182 pub qualifier_ontology_namespace: Option<String>,
183 pub qualifier_ontology_id: Option<String>,
184 pub qualifier_text: Option<String>,
185 pub qualifier_date: Option<NaiveDate>,
186 pub qualifier_integer: Option<i32>,
187}
188
189#[derive(Debug, Clone, PartialEq, Eq)]
190pub struct PgDatasetReadModel {
191 pub dataset_id: VersionId,
192}
193
194#[derive(Debug, Clone, PartialEq, Eq)]
195pub struct PgDataFileReadModel {
196 pub datafile_id: VersionId,
197 pub size_bytes: Option<i64>,
199 pub compressed: Option<bool>,
200 pub encrypted: Option<bool>,
201 pub compression_algorithm: Option<String>,
202 pub encryption_algorithm: Option<String>,
203 pub encryption_key: Option<Vec<u8>>,
204 pub storage_uri: String,
205 pub edam_format: String,
206 pub digest: String,
207}
208
209#[derive(Debug, Clone, PartialEq, Eq)]
210pub struct PgValueTypeReadModel {
211 pub value_type_id: ValueTypeId,
212 pub value_type: String,
213 pub description: Option<String>,
214}
215
216#[derive(Debug, Clone, PartialEq, Eq)]
217pub struct PgVocabularyReadModel {
218 pub vocabulary_id: VocabularyId,
219 pub domain_id: DomainId,
220 pub name: String,
221 pub description: Option<String>,
222}
223
224#[derive(Debug, Clone, PartialEq, Eq)]
225pub struct PgVocabularyMappingReadModel {
226 pub vocabulary_mapping_id: i64,
227 pub from_vocabulary_item: VocabularyItemId,
228 pub to_vocabulary_item: VocabularyItemId,
229}
230
231#[derive(Debug, Clone, PartialEq, Eq)]
232pub struct PgVocabularyMappingWithItemsReadModel {
233 pub mapping: PgVocabularyMappingReadModel,
234 pub from_item: PgVocabularyItemReadModel,
235 pub to_item: PgVocabularyItemReadModel,
236}
237
238#[derive(Debug, Clone, PartialEq, Eq)]
239pub struct PgVocabularyItemReadModel {
240 pub vocabulary_item_id: VocabularyItemId,
241 pub vocabulary_id: VocabularyId,
242 pub value: i32,
243 pub code: String,
244 pub description: Option<String>,
245}
246
247#[derive(Debug, Clone, PartialEq, Eq)]
248pub struct PgVariableReadModel {
249 pub variable_id: VariableId,
250 pub domain_id: DomainId,
251 pub name: String,
252 pub value_type_id: ValueTypeId,
253 pub value_type: String,
254 pub value_format: Option<String>,
255 pub vocabulary_id: Option<VocabularyId>,
256 pub key_role: KeyRole,
257 pub description: Option<String>,
258 pub note: Option<String>,
259 pub ontology_namespace: Option<String>,
260 pub ontology_class: Option<String>,
261}
262
263pub mod provisional {
264 use super::VariableId;
265
266 #[derive(Debug, Clone, PartialEq, Eq)]
269 pub struct PgVariableMappingReadModel {
270 pub mapping_id: i64,
271 pub from_variable_id: VariableId,
272 pub to_variable_id: VariableId,
273 pub operator: String,
274 pub operants: String,
275 pub prerequisite_id: Option<VariableId>,
276 }
277}
278
279#[derive(Debug, Clone, PartialEq, Eq)]
280pub struct PgDatasetVariableReadModel {
281 pub dataset_id: VersionId,
282 pub variable_id: VariableId,
283 pub row_role: KeyRole,
284}
285
286#[derive(Debug, Clone, PartialEq, Eq)]
287pub struct PgEntityReadModel {
288 pub entity_id: EntityId,
289 pub domain_id: DomainId,
290 pub uuid: Option<Uuid>,
291 pub name: String,
292 pub description: Option<String>,
293 pub agent_instructions: Option<String>,
294 pub ontology_namespace: Option<String>,
295 pub ontology_class: Option<String>,
296}
297
298#[derive(Debug, Clone, PartialEq, Eq)]
299pub struct PgEntityInstanceReadModel {
300 pub instance_id: i64,
301 pub uuid: Uuid,
302 pub entity_id: EntityId,
303 pub label: Option<String>,
304 pub note: Option<String>,
305 pub transformation_id: TransformationId,
306}
307
308#[derive(Debug, Clone, PartialEq, Eq)]
309pub struct PgStudyEntityInstanceReadModel {
310 pub study_id: StudyId,
311 pub entity_instance_id: i64,
312 pub entity_id: EntityId,
313 pub external_id: String,
314 pub transformation_id: TransformationId,
315}
316
317#[derive(Debug, Clone, PartialEq, Eq)]
318pub struct PgEntityRelationReadModel {
319 pub entity_relation_id: EntityRelationId,
320 pub subject_entity_id: EntityId,
321 pub object_entity_id: EntityId,
322 pub domain_id: DomainId,
323 pub uuid: Option<Uuid>,
324 pub name: String,
325 pub description: Option<String>,
326 pub agent_instructions: Option<String>,
327 pub ontology_namespace: Option<String>,
328 pub ontology_class: Option<String>,
329}
330
331#[derive(Debug, Clone, PartialEq, Eq)]
332pub struct PgRelationInstanceReadModel {
333 pub relation_instance_id: i64,
334 pub uuid: Uuid,
335 pub entity_relation_id: EntityRelationId,
336 pub entity_instance_id_1: i64,
337 pub entity_instance_id_2: i64,
338 pub valid_from: Option<NaiveDate>,
339 pub valid_to: Option<NaiveDate>,
340 pub note: Option<String>,
341 pub transformation_id: TransformationId,
342}
343
344#[derive(Debug, Clone, PartialEq, Eq)]
345pub struct PgStudyRelationInstanceReadModel {
346 pub study_id: StudyId,
347 pub relation_instance_id: i64,
348 pub entity_relation_id: EntityRelationId,
349 pub external_id: String,
350 pub transformation_id: TransformationId,
351}
352
353#[derive(Debug, Clone, PartialEq, Eq)]
354pub struct PgAssetVersionEntityReadModel {
355 pub version_id: VersionId,
356 pub entity_instance_id: i64,
357 pub transformation_id: TransformationId,
358}
359
360#[derive(Debug, Clone, PartialEq, Eq)]
361pub struct PgDatasetVersionEntityReadModel {
362 pub version_id: VersionId,
363 pub entity_instance_id: i64,
364 pub entity_variable_id: VariableId,
365 pub transformation_id: TransformationId,
366}
367
368#[derive(Debug, Clone, PartialEq, Eq)]
369pub struct PgAssetVersionRelationInstanceReadModel {
370 pub version_id: VersionId,
371 pub relation_instance_id: i64,
372 pub transformation_id: TransformationId,
373}
374
375#[derive(Debug, Clone, PartialEq, Eq)]
376pub struct PgDatasetVersionRelationInstanceReadModel {
377 pub version_id: VersionId,
378 pub relation_instance_id: i64,
379 pub subject_variable_id: VariableId,
380 pub object_variable_id: VariableId,
381 pub relation_variable_id: Option<VariableId>,
382 pub transformation_id: TransformationId,
383}
384
385#[derive(Debug, Clone, PartialEq, Eq)]
386pub struct PgTransformationReadModel {
387 pub transformation_id: TransformationId,
388 pub transformation_type: TransformationType,
389 pub description: String,
390 pub repository_url: Option<String>,
391 pub commit_hash: Option<String>,
392 pub file_path: Option<String>,
393 pub date_created: Option<DateTime<Utc>>,
394 pub created_by: Option<String>,
395}
396
397#[derive(Debug, Clone, PartialEq, Eq)]
398pub struct PgTagReadModel {
399 pub tag_id: i64,
400 pub name: String,
401}
402
403#[derive(Debug, Clone, PartialEq, Eq)]
404pub struct PgStudyTagReadModel {
405 pub study_id: StudyId,
406 pub tag_id: i64,
407}
408
409#[derive(Debug, Clone, PartialEq, Eq)]
410pub struct PgDomainTagReadModel {
411 pub domain_id: DomainId,
412 pub tag_id: i64,
413}
414
415#[derive(Debug, Clone, PartialEq, Eq)]
416pub struct PgVariableTagReadModel {
417 pub variable_id: VariableId,
418 pub tag_id: i64,
419}
420
421#[derive(Debug, Clone, PartialEq, Eq)]
422pub struct PgAssetTagReadModel {
423 pub asset_id: AssetId,
424 pub tag_id: i64,
425}
426
427#[derive(Debug, Clone, PartialEq, Eq)]
428pub struct PgAssetVersionTagReadModel {
429 pub version_id: VersionId,
430 pub tag_id: i64,
431}
432
433#[derive(Debug, Clone, PartialEq, Eq)]
434pub struct PgEntityTagReadModel {
435 pub entity_id: EntityId,
436 pub tag_id: i64,
437}
438
439#[derive(Debug, Clone, PartialEq, Eq)]
440pub struct PgEntityRelationTagReadModel {
441 pub entity_relation_id: EntityRelationId,
442 pub tag_id: i64,
443}
444
445#[derive(Debug, Clone, PartialEq, Eq)]
446pub struct PgTransformationInputReadModel {
447 pub transformation_id: TransformationId,
448 pub version_id: VersionId,
449}
450
451#[derive(Debug, Clone, PartialEq, Eq)]
452pub struct PgTransformationOutputReadModel {
453 pub transformation_id: TransformationId,
454 pub version_id: VersionId,
455}
456
457impl PgMetadataConnection {
458 pub fn missing_metadata_tables(&mut self) -> Result<Vec<&'static str>, PgMetaError> {
459 let mut missing = Vec::new();
460 for table_name in METADATA_TABLES {
461 let exists = MetadataExecutor::query_optional(
462 self,
463 "check metadata table",
464 "
465 SELECT 1
466 FROM information_schema.tables
467 WHERE table_schema = 'public'
468 AND table_name = $1
469 LIMIT 1
470 ",
471 &[table_name],
472 )?
473 .is_some();
474 if !exists {
475 missing.push(*table_name);
476 }
477 }
478 Ok(missing)
479 }
480
481 pub fn list_study_types(&mut self) -> Result<Vec<PgStudyTypeReadModel>, PgMetaError> {
482 let rows = self
483 .client
484 .query(
485 "
486 SELECT study_type_id, name, namespace, ontology_class, description
487 FROM public.study_types
488 ORDER BY study_type_id
489 ",
490 &[],
491 )
492 .map_err(PgMetaError::Query)?;
493 rows.iter().map(study_type_from_row).collect()
494 }
495
496 pub fn list_studies(&mut self) -> Result<Vec<PgStudyReadModel>, PgMetaError> {
497 let rows = self
498 .client
499 .query(
500 "
501 SELECT study_id, name, description, documentation, agent_instructions,
502 external_id, study_type_id, date_created, created_by
503 FROM public.studies
504 ORDER BY name
505 ",
506 &[],
507 )
508 .map_err(PgMetaError::Query)?;
509 rows.iter().map(study_from_row).collect()
510 }
511
512 pub fn list_studies_for_domain(
516 &mut self,
517 domain_id: DomainId,
518 ) -> Result<Vec<PgStudyReadModel>, PgMetaError> {
519 let rows = self
520 .client
521 .query(
522 "
523 SELECT s.study_id, s.name, s.description, s.documentation,
524 s.agent_instructions, s.external_id, s.study_type_id,
525 s.date_created, s.created_by
526 FROM public.studies s
527 JOIN public.study_domains sd ON sd.study_id = s.study_id
528 WHERE sd.domain_id = $1
529 ORDER BY lower(s.name), s.study_id
530 ",
531 &[&id_i32(domain_id.0, "domain_id")?],
532 )
533 .map_err(PgMetaError::Query)?;
534 rows.iter().map(study_from_row).collect()
535 }
536
537 pub fn get_study(
538 &mut self,
539 study_id: StudyId,
540 ) -> Result<Option<PgStudyReadModel>, PgMetaError> {
541 let row = self
542 .client
543 .query_opt(
544 "
545 SELECT study_id, name, description, documentation, agent_instructions,
546 external_id, study_type_id, date_created, created_by
547 FROM public.studies
548 WHERE study_id = $1
549 LIMIT 1
550 ",
551 &[&study_id.0],
552 )
553 .map_err(PgMetaError::Query)?;
554 row.as_ref().map(study_from_row).transpose()
555 }
556
557 pub fn get_study_by_name(
558 &mut self,
559 name: &str,
560 ) -> Result<Option<PgStudyReadModel>, PgMetaError> {
561 let row = self
562 .client
563 .query_opt(
564 "
565 SELECT study_id, name, description, documentation, agent_instructions,
566 external_id, study_type_id, date_created, created_by
567 FROM public.studies
568 WHERE name = $1
569 LIMIT 1
570 ",
571 &[&name],
572 )
573 .map_err(PgMetaError::Query)?;
574 row.as_ref().map(study_from_row).transpose()
575 }
576
577 pub fn list_domains(&mut self) -> Result<Vec<PgDomainReadModel>, PgMetaError> {
578 let rows = self
579 .client
580 .query(
581 "
582 SELECT domain_id, name, uri, description
583 FROM public.domains
584 ORDER BY name
585 ",
586 &[],
587 )
588 .map_err(PgMetaError::Query)?;
589 rows.iter().map(domain_from_row).collect()
590 }
591
592 pub fn list_domains_by_name(
593 &mut self,
594 name: &str,
595 ) -> Result<Vec<PgDomainReadModel>, PgMetaError> {
596 let rows = self.client.query(
597 "SELECT domain_id, name, uri, description FROM public.domains WHERE name = $1 ORDER BY domain_id",
598 &[&name],
599 ).map_err(PgMetaError::Query)?;
600 rows.iter().map(domain_from_row).collect()
601 }
602
603 pub fn get_domain(
604 &mut self,
605 domain_id: DomainId,
606 ) -> Result<Option<PgDomainReadModel>, PgMetaError> {
607 let row = self
608 .client
609 .query_opt(
610 "
611 SELECT domain_id, name, uri, description
612 FROM public.domains
613 WHERE domain_id = $1
614 LIMIT 1
615 ",
616 &[&id_i32(domain_id.0, "domain_id")?],
617 )
618 .map_err(PgMetaError::Query)?;
619 row.as_ref().map(domain_from_row).transpose()
620 }
621
622 pub fn get_domain_by_name(
623 &mut self,
624 name: &str,
625 ) -> Result<Option<PgDomainReadModel>, PgMetaError> {
626 let row = self
627 .client
628 .query_opt(
629 "
630 SELECT domain_id, name, uri, description
631 FROM public.domains
632 WHERE name = $1
633 LIMIT 1
634 ",
635 &[&name],
636 )
637 .map_err(PgMetaError::Query)?;
638 row.as_ref().map(domain_from_row).transpose()
639 }
640
641 pub fn list_duo_codes(&mut self) -> Result<Vec<PgDuoCodeReadModel>, PgMetaError> {
642 let rows = self
643 .client
644 .query(
645 "
646 SELECT duo_id, duo_code, label, definition, category,
647 qualifier_type, qualifier_required, qualifier_note
648 FROM public.duo_codes
649 ORDER BY duo_code
650 ",
651 &[],
652 )
653 .map_err(PgMetaError::Query)?;
654 rows.iter().map(duo_code_from_row).collect()
655 }
656
657 pub fn list_study_domains(
658 &mut self,
659 study_id: StudyId,
660 ) -> Result<Vec<PgDomainReadModel>, PgMetaError> {
661 let rows = self
662 .client
663 .query(
664 "
665 SELECT d.domain_id, d.name, d.uri, d.description
666 FROM public.domains d
667 JOIN public.study_domains sd ON sd.domain_id = d.domain_id
668 WHERE sd.study_id = $1
669 ORDER BY d.name
670 ",
671 &[&study_id.0],
672 )
673 .map_err(PgMetaError::Query)?;
674 rows.iter().map(domain_from_row).collect()
675 }
676
677 pub fn list_study_domain_links(
678 &mut self,
679 study_id: StudyId,
680 ) -> Result<Vec<PgStudyDomainReadModel>, PgMetaError> {
681 let rows = self
682 .client
683 .query(
684 "
685 SELECT study_id, domain_id
686 FROM public.study_domains
687 WHERE study_id = $1
688 ORDER BY domain_id
689 ",
690 &[&study_id.0],
691 )
692 .map_err(PgMetaError::Query)?;
693 rows.iter().map(study_domain_from_row).collect()
694 }
695
696 pub fn list_study_access(
697 &mut self,
698 study_id: StudyId,
699 ) -> Result<Vec<PgStudyAccessReadModel>, PgMetaError> {
700 let rows = self
701 .client
702 .query(
703 "
704 SELECT study_id, user_id, date_granted, granted_by
705 FROM public.study_access
706 WHERE study_id = $1
707 ORDER BY user_id
708 ",
709 &[&study_id.0],
710 )
711 .map_err(PgMetaError::Query)?;
712 rows.iter().map(study_access_from_row).collect()
713 }
714
715 pub fn list_study_custodians(
716 &mut self,
717 study_id: StudyId,
718 ) -> Result<Vec<PgStudyCustodianReadModel>, PgMetaError> {
719 let rows = self
720 .client
721 .query(
722 "
723 SELECT study_id, user_id, is_primary, date_granted, granted_by
724 FROM public.study_custodians
725 WHERE study_id = $1
726 ORDER BY is_primary DESC, user_id
727 ",
728 &[&study_id.0],
729 )
730 .map_err(PgMetaError::Query)?;
731 rows.iter().map(study_custodian_from_row).collect()
732 }
733
734 pub fn list_study_duo_restrictions(
735 &mut self,
736 study_id: StudyId,
737 ) -> Result<Vec<PgDuoRestrictionReadModel>, PgMetaError> {
738 let rows = self
739 .client
740 .query(
741 "
742 SELECT sdr.study_duo_restriction_id, sdr.study_id, sdr.duo_id,
743 dc.duo_code, dc.label,
744 sdr.qualifier_ontology_namespace, sdr.qualifier_ontology_id,
745 sdr.qualifier_text, sdr.qualifier_date, sdr.qualifier_integer
746 FROM public.study_duo_restrictions sdr
747 JOIN public.duo_codes dc ON dc.duo_id = sdr.duo_id
748 WHERE sdr.study_id = $1
749 ORDER BY sdr.study_duo_restriction_id
750 ",
751 &[&study_id.0],
752 )
753 .map_err(PgMetaError::Query)?;
754 rows.iter().map(study_duo_from_row).collect()
755 }
756
757 pub fn list_assets_for_study(
758 &mut self,
759 study_id: StudyId,
760 ) -> Result<Vec<PgAssetReadModel>, PgMetaError> {
761 let rows = self
762 .client
763 .query(
764 "
765 SELECT asset_id, study_id, name, description, agent_instructions,
766 asset_type::text AS asset_type, date_created, created_by
767 FROM public.assets
768 WHERE study_id = $1
769 ORDER BY name
770 ",
771 &[&study_id.0],
772 )
773 .map_err(PgMetaError::Query)?;
774 rows.iter().map(asset_from_row).collect()
775 }
776
777 pub fn get_asset(
778 &mut self,
779 asset_id: AssetId,
780 ) -> Result<Option<PgAssetReadModel>, PgMetaError> {
781 self.client
782 .query_opt(
783 "
784 SELECT asset_id, study_id, name, description, agent_instructions,
785 asset_type::text AS asset_type, date_created, created_by
786 FROM public.assets
787 WHERE asset_id = $1
788 LIMIT 1
789 ",
790 &[&asset_id.0],
791 )
792 .map_err(PgMetaError::Query)?
793 .as_ref()
794 .map(asset_from_row)
795 .transpose()
796 }
797
798 pub fn get_asset_by_name(
799 &mut self,
800 study_id: StudyId,
801 name: &str,
802 ) -> Result<Option<PgAssetReadModel>, PgMetaError> {
803 self.client
804 .query_opt(
805 "
806 SELECT asset_id, study_id, name, description, agent_instructions,
807 asset_type::text AS asset_type, date_created, created_by
808 FROM public.assets
809 WHERE study_id = $1
810 AND name = $2
811 LIMIT 1
812 ",
813 &[&study_id.0, &name],
814 )
815 .map_err(PgMetaError::Query)?
816 .as_ref()
817 .map(asset_from_row)
818 .transpose()
819 }
820
821 pub fn list_asset_versions(
822 &mut self,
823 asset_id: AssetId,
824 ) -> Result<Vec<PgAssetVersionReadModel>, PgMetaError> {
825 let rows = self
826 .client
827 .query(
828 "
829 SELECT version_id, asset_id, major, minor, patch, version_label,
830 version_note, is_latest, doi, created_at, created_by
831 FROM public.asset_versions
832 WHERE asset_id = $1
833 ORDER BY major, minor, patch
834 ",
835 &[&asset_id.0],
836 )
837 .map_err(PgMetaError::Query)?;
838 rows.iter().map(asset_version_from_row).collect()
839 }
840
841 pub fn get_asset_version(
842 &mut self,
843 version_id: VersionId,
844 ) -> Result<Option<PgAssetVersionReadModel>, PgMetaError> {
845 self.client
846 .query_opt(
847 "
848 SELECT version_id, asset_id, major, minor, patch, version_label,
849 version_note, is_latest, doi, created_at, created_by
850 FROM public.asset_versions
851 WHERE version_id = $1
852 LIMIT 1
853 ",
854 &[&version_id.0],
855 )
856 .map_err(PgMetaError::Query)?
857 .as_ref()
858 .map(asset_version_from_row)
859 .transpose()
860 }
861
862 pub fn list_asset_version_duo_restrictions(
863 &mut self,
864 version_id: VersionId,
865 ) -> Result<Vec<PgAssetVersionDuoRestrictionReadModel>, PgMetaError> {
866 let rows = self
867 .client
868 .query(
869 "
870 SELECT avdr.asset_version_duo_restriction_id, avdr.version_id, avdr.duo_id,
871 dc.duo_code, dc.label,
872 avdr.qualifier_ontology_namespace, avdr.qualifier_ontology_id,
873 avdr.qualifier_text, avdr.qualifier_date, avdr.qualifier_integer
874 FROM public.asset_version_duo_restrictions avdr
875 JOIN public.duo_codes dc ON dc.duo_id = avdr.duo_id
876 WHERE avdr.version_id = $1
877 ORDER BY avdr.asset_version_duo_restriction_id
878 ",
879 &[&version_id.0],
880 )
881 .map_err(PgMetaError::Query)?;
882 rows.iter().map(asset_version_duo_from_row).collect()
883 }
884
885 pub fn list_datasets_for_asset(
886 &mut self,
887 asset_id: AssetId,
888 ) -> Result<Vec<PgDatasetReadModel>, PgMetaError> {
889 let rows = self
890 .client
891 .query(
892 "
893 SELECT d.dataset_id
894 FROM public.datasets d
895 JOIN public.asset_versions av ON av.version_id = d.dataset_id
896 WHERE av.asset_id = $1
897 ORDER BY av.major, av.minor, av.patch
898 ",
899 &[&asset_id.0],
900 )
901 .map_err(PgMetaError::Query)?;
902 rows.iter().map(dataset_from_row).collect()
903 }
904
905 pub fn get_dataset(
906 &mut self,
907 dataset_id: VersionId,
908 ) -> Result<Option<PgDatasetReadModel>, PgMetaError> {
909 self.client
910 .query_opt(
911 "
912 SELECT dataset_id
913 FROM public.datasets
914 WHERE dataset_id = $1
915 LIMIT 1
916 ",
917 &[&dataset_id.0],
918 )
919 .map_err(PgMetaError::Query)?
920 .as_ref()
921 .map(dataset_from_row)
922 .transpose()
923 }
924
925 pub fn get_dataset_version_withdrawn_at(
927 &mut self,
928 dataset_id: VersionId,
929 ) -> Result<Option<DateTime<Utc>>, PgMetaError> {
930 self.client
931 .query_opt(
932 "
933 SELECT withdrawn_at
934 FROM public.dataset_version_withdrawals
935 WHERE dataset_id = $1
936 LIMIT 1
937 ",
938 &[&dataset_id.0],
939 )
940 .map_err(PgMetaError::Query)
941 .map(|row| row.map(|row| utc(row.get::<_, NaiveDateTime>("withdrawn_at"))))
942 }
943
944 pub fn list_datafiles_for_asset(
945 &mut self,
946 asset_id: AssetId,
947 ) -> Result<Vec<PgDataFileReadModel>, PgMetaError> {
948 let rows = self
949 .client
950 .query(
951 "
952 SELECT df.datafile_id, df.size_bytes, df.compressed, df.encrypted,
953 df.compression_algorithm, df.encryption_algorithm,
954 df.encryption_key, df.storage_uri, df.edam_format, df.digest
955 FROM public.datafiles df
956 JOIN public.asset_versions av ON av.version_id = df.datafile_id
957 WHERE av.asset_id = $1
958 ORDER BY av.major, av.minor, av.patch
959 ",
960 &[&asset_id.0],
961 )
962 .map_err(PgMetaError::Query)?;
963 rows.iter().map(datafile_from_row).collect()
964 }
965
966 pub fn get_datafile(
967 &mut self,
968 datafile_id: VersionId,
969 ) -> Result<Option<PgDataFileReadModel>, PgMetaError> {
970 self.client
971 .query_opt(
972 "
973 SELECT datafile_id, size_bytes, compressed, encrypted,
974 compression_algorithm, encryption_algorithm,
975 encryption_key, storage_uri, edam_format, digest
976 FROM public.datafiles
977 WHERE datafile_id = $1
978 LIMIT 1
979 ",
980 &[&datafile_id.0],
981 )
982 .map_err(PgMetaError::Query)?
983 .as_ref()
984 .map(datafile_from_row)
985 .transpose()
986 }
987
988 pub fn list_value_types(&mut self) -> Result<Vec<PgValueTypeReadModel>, PgMetaError> {
989 let rows = self
990 .client
991 .query(
992 "
993 SELECT value_type_id, value_type, description
994 FROM public.value_types
995 ORDER BY value_type_id
996 ",
997 &[],
998 )
999 .map_err(PgMetaError::Query)?;
1000 rows.iter().map(value_type_from_row).collect()
1001 }
1002
1003 pub fn list_vocabularies_for_domain(
1004 &mut self,
1005 domain_id: DomainId,
1006 ) -> Result<Vec<PgVocabularyReadModel>, PgMetaError> {
1007 let rows = self
1008 .client
1009 .query(
1010 "
1011 SELECT vocabulary_id, domain_id, name, description
1012 FROM public.vocabularies
1013 WHERE domain_id = $1
1014 ORDER BY name
1015 ",
1016 &[&id_i32(domain_id.0, "domain_id")?],
1017 )
1018 .map_err(PgMetaError::Query)?;
1019 rows.iter().map(vocabulary_from_row).collect()
1020 }
1021
1022 pub fn get_vocabulary(
1023 &mut self,
1024 vocabulary_id: VocabularyId,
1025 ) -> Result<Option<PgVocabularyReadModel>, PgMetaError> {
1026 self.client
1027 .query_opt(
1028 "
1029 SELECT vocabulary_id, domain_id, name, description
1030 FROM public.vocabularies
1031 WHERE vocabulary_id = $1
1032 LIMIT 1
1033 ",
1034 &[&id_i32(vocabulary_id.0, "vocabulary_id")?],
1035 )
1036 .map_err(PgMetaError::Query)?
1037 .as_ref()
1038 .map(vocabulary_from_row)
1039 .transpose()
1040 }
1041
1042 pub fn list_vocabulary_items(
1043 &mut self,
1044 vocabulary_id: VocabularyId,
1045 ) -> Result<Vec<PgVocabularyItemReadModel>, PgMetaError> {
1046 let rows = self
1047 .client
1048 .query(
1049 "
1050 SELECT vocabulary_item_id, vocabulary_id, value, code, description
1051 FROM public.vocabulary_items
1052 WHERE vocabulary_id = $1
1053 ORDER BY value, vocabulary_item_id
1054 ",
1055 &[&id_i32(vocabulary_id.0, "vocabulary_id")?],
1056 )
1057 .map_err(PgMetaError::Query)?;
1058 rows.iter().map(vocabulary_item_from_row).collect()
1059 }
1060
1061 pub fn get_vocabulary_item(
1062 &mut self,
1063 vocabulary_item_id: VocabularyItemId,
1064 ) -> Result<Option<PgVocabularyItemReadModel>, PgMetaError> {
1065 self.client
1066 .query_opt(
1067 "
1068 SELECT vocabulary_item_id, vocabulary_id, value, code, description
1069 FROM public.vocabulary_items
1070 WHERE vocabulary_item_id = $1
1071 LIMIT 1
1072 ",
1073 &[&id_i32(vocabulary_item_id.0, "vocabulary_item_id")?],
1074 )
1075 .map_err(PgMetaError::Query)?
1076 .as_ref()
1077 .map(vocabulary_item_from_row)
1078 .transpose()
1079 }
1080
1081 pub fn list_vocabulary_mappings(
1082 &mut self,
1083 ) -> Result<Vec<PgVocabularyMappingReadModel>, PgMetaError> {
1084 let rows = self
1085 .client
1086 .query(
1087 "
1088 SELECT vocabulary_mapping_id, from_vocabulary_item, to_vocabulary_item
1089 FROM public.vocabulary_mapping
1090 ORDER BY vocabulary_mapping_id
1091 ",
1092 &[],
1093 )
1094 .map_err(PgMetaError::Query)?;
1095 rows.iter().map(vocabulary_mapping_from_row).collect()
1096 }
1097
1098 pub fn list_vocabulary_mappings_for_vocabulary(
1099 &mut self,
1100 vocabulary_id: VocabularyId,
1101 ) -> Result<Vec<PgVocabularyMappingWithItemsReadModel>, PgMetaError> {
1102 let rows = self
1103 .client
1104 .query(
1105 "
1106 SELECT vm.vocabulary_mapping_id,
1107 fi.vocabulary_item_id AS from_vocabulary_item_id,
1108 fi.vocabulary_id AS from_vocabulary_id,
1109 fi.value AS from_value, fi.code AS from_code,
1110 fi.description AS from_description,
1111 ti.vocabulary_item_id AS to_vocabulary_item_id,
1112 ti.vocabulary_id AS to_vocabulary_id,
1113 ti.value AS to_value, ti.code AS to_code,
1114 ti.description AS to_description
1115 FROM public.vocabulary_mapping vm
1116 JOIN public.vocabulary_items fi
1117 ON fi.vocabulary_item_id = vm.from_vocabulary_item
1118 JOIN public.vocabulary_items ti
1119 ON ti.vocabulary_item_id = vm.to_vocabulary_item
1120 WHERE fi.vocabulary_id = $1 OR ti.vocabulary_id = $1
1121 ORDER BY fi.vocabulary_id, fi.value, fi.vocabulary_item_id,
1122 ti.vocabulary_id, ti.value, ti.vocabulary_item_id,
1123 vm.vocabulary_mapping_id
1124 ",
1125 &[&id_i32(vocabulary_id.0, "vocabulary_id")?],
1126 )
1127 .map_err(PgMetaError::Query)?;
1128 rows.iter()
1129 .map(|row| {
1130 let mapping = PgVocabularyMappingReadModel {
1131 vocabulary_mapping_id: i64::from(row.get::<_, i32>("vocabulary_mapping_id")),
1132 from_vocabulary_item: VocabularyItemId(i64::from(
1133 row.get::<_, i32>("from_vocabulary_item_id"),
1134 )),
1135 to_vocabulary_item: VocabularyItemId(i64::from(
1136 row.get::<_, i32>("to_vocabulary_item_id"),
1137 )),
1138 };
1139 let from_item = PgVocabularyItemReadModel {
1140 vocabulary_item_id: VocabularyItemId(i64::from(
1141 row.get::<_, i32>("from_vocabulary_item_id"),
1142 )),
1143 vocabulary_id: VocabularyId(i64::from(row.get::<_, i32>("from_vocabulary_id"))),
1144 value: row.get("from_value"),
1145 code: row.get("from_code"),
1146 description: row.get("from_description"),
1147 };
1148 let to_item = PgVocabularyItemReadModel {
1149 vocabulary_item_id: VocabularyItemId(i64::from(
1150 row.get::<_, i32>("to_vocabulary_item_id"),
1151 )),
1152 vocabulary_id: VocabularyId(i64::from(row.get::<_, i32>("to_vocabulary_id"))),
1153 value: row.get("to_value"),
1154 code: row.get("to_code"),
1155 description: row.get("to_description"),
1156 };
1157 Ok(PgVocabularyMappingWithItemsReadModel {
1158 mapping,
1159 from_item,
1160 to_item,
1161 })
1162 })
1163 .collect()
1164 }
1165
1166 pub fn list_variables_for_domain(
1167 &mut self,
1168 domain_id: DomainId,
1169 ) -> Result<Vec<PgVariableReadModel>, PgMetaError> {
1170 let rows = self
1171 .client
1172 .query(
1173 "
1174 SELECT v.variable_id, v.domain_id, v.name, v.value_type_id,
1175 vt.value_type, v.value_format, v.vocabulary_id,
1176 v.keyrole::text AS keyrole, v.description, v.note,
1177 v.ontology_namespace, v.ontology_class
1178 FROM public.variables v
1179 JOIN public.value_types vt ON vt.value_type_id = v.value_type_id
1180 WHERE v.domain_id = $1
1181 ORDER BY lower(v.name), v.variable_id
1182 ",
1183 &[&id_i32(domain_id.0, "domain_id")?],
1184 )
1185 .map_err(PgMetaError::Query)?;
1186 rows.iter().map(variable_from_row).collect()
1187 }
1188
1189 pub fn get_variable(
1190 &mut self,
1191 variable_id: VariableId,
1192 ) -> Result<Option<PgVariableReadModel>, PgMetaError> {
1193 self.client
1194 .query_opt(
1195 "
1196 SELECT v.variable_id, v.domain_id, v.name, v.value_type_id,
1197 vt.value_type, v.value_format, v.vocabulary_id,
1198 v.keyrole::text AS keyrole, v.description, v.note,
1199 v.ontology_namespace, v.ontology_class
1200 FROM public.variables v
1201 JOIN public.value_types vt ON vt.value_type_id = v.value_type_id
1202 WHERE v.variable_id = $1
1203 LIMIT 1
1204 ",
1205 &[&id_i32(variable_id.0, "variable_id")?],
1206 )
1207 .map_err(PgMetaError::Query)?
1208 .as_ref()
1209 .map(variable_from_row)
1210 .transpose()
1211 }
1212
1213 pub fn list_variable_mappings_provisional(
1216 &mut self,
1217 ) -> Result<Vec<provisional::PgVariableMappingReadModel>, PgMetaError> {
1218 let rows = self
1219 .client
1220 .query(
1221 "
1222 SELECT mapping_id, from_variable_id, to_variable_id,
1223 operator::text AS operator, operants, prerequisite_id
1224 FROM public.variable_mapping
1225 ORDER BY mapping_id
1226 ",
1227 &[],
1228 )
1229 .map_err(PgMetaError::Query)?;
1230 rows.iter().map(variable_mapping_from_row).collect()
1231 }
1232
1233 pub fn list_dataset_variables(
1234 &mut self,
1235 dataset_id: VersionId,
1236 ) -> Result<Vec<PgDatasetVariableReadModel>, PgMetaError> {
1237 let rows = self
1238 .client
1239 .query(
1240 "
1241 SELECT dataset_id, variable_id, row_role::text AS row_role
1242 FROM public.dataset_variables
1243 WHERE dataset_id = $1
1244 ORDER BY variable_id
1245 ",
1246 &[&dataset_id.0],
1247 )
1248 .map_err(PgMetaError::Query)?;
1249 rows.iter().map(dataset_variable_from_row).collect()
1250 }
1251
1252 pub fn list_dataset_variables_for_variable(
1253 &mut self,
1254 variable_id: VariableId,
1255 ) -> Result<Vec<PgDatasetVariableReadModel>, PgMetaError> {
1256 let rows = self
1257 .client
1258 .query(
1259 "
1260 SELECT dataset_id, variable_id, row_role::text AS row_role
1261 FROM public.dataset_variables
1262 WHERE variable_id = $1
1263 ORDER BY dataset_id
1264 ",
1265 &[&id_i32(variable_id.0, "variable_id")?],
1266 )
1267 .map_err(PgMetaError::Query)?;
1268 rows.iter().map(dataset_variable_from_row).collect()
1269 }
1270
1271 pub fn list_entities_for_domain(
1272 &mut self,
1273 domain_id: DomainId,
1274 ) -> Result<Vec<PgEntityReadModel>, PgMetaError> {
1275 let rows = self
1276 .client
1277 .query(
1278 "
1279 SELECT entity_id, domain_id, uuid, name, description,
1280 agent_instructions, ontology_namespace, ontology_class
1281 FROM public.entities
1282 WHERE domain_id = $1
1283 ORDER BY name
1284 ",
1285 &[&id_i32(domain_id.0, "domain_id")?],
1286 )
1287 .map_err(PgMetaError::Query)?;
1288 rows.iter().map(entity_from_row).collect()
1289 }
1290
1291 pub fn list_entity_relations_for_domain(
1292 &mut self,
1293 domain_id: DomainId,
1294 ) -> Result<Vec<PgEntityRelationReadModel>, PgMetaError> {
1295 let rows = self
1296 .client
1297 .query(
1298 "
1299 SELECT entityrelation_id, subject_entity_id, object_entity_id,
1300 domain_id, uuid, name, description, agent_instructions,
1301 ontology_namespace, ontology_class
1302 FROM public.entityrelations
1303 WHERE domain_id = $1
1304 ORDER BY name
1305 ",
1306 &[&id_i32(domain_id.0, "domain_id")?],
1307 )
1308 .map_err(PgMetaError::Query)?;
1309 rows.iter().map(entity_relation_from_row).collect()
1310 }
1311
1312 pub fn list_entity_instances_for_entity(
1313 &mut self,
1314 entity_id: EntityId,
1315 ) -> Result<Vec<PgEntityInstanceReadModel>, PgMetaError> {
1316 let rows = self
1317 .client
1318 .query(
1319 "
1320 SELECT instance_id, uuid, entity_id, label, note, transformation_id
1321 FROM public.entity_instances
1322 WHERE entity_id = $1
1323 ORDER BY instance_id
1324 ",
1325 &[&id_i32(entity_id.0, "entity_id")?],
1326 )
1327 .map_err(PgMetaError::Query)?;
1328 rows.iter().map(entity_instance_from_row).collect()
1329 }
1330
1331 pub fn get_entity_instance(
1332 &mut self,
1333 instance_id: i64,
1334 ) -> Result<Option<PgEntityInstanceReadModel>, PgMetaError> {
1335 self.client
1336 .query_opt(
1337 "
1338 SELECT instance_id, uuid, entity_id, label, note, transformation_id
1339 FROM public.entity_instances
1340 WHERE instance_id = $1
1341 LIMIT 1
1342 ",
1343 &[&instance_id],
1344 )
1345 .map_err(PgMetaError::Query)?
1346 .as_ref()
1347 .map(entity_instance_from_row)
1348 .transpose()
1349 }
1350
1351 pub fn get_entity_instance_by_uuid(
1352 &mut self,
1353 uuid: Uuid,
1354 ) -> Result<Option<PgEntityInstanceReadModel>, PgMetaError> {
1355 self.client
1356 .query_opt(
1357 "
1358 SELECT instance_id, uuid, entity_id, label, note, transformation_id
1359 FROM public.entity_instances
1360 WHERE uuid = $1
1361 LIMIT 1
1362 ",
1363 &[&uuid],
1364 )
1365 .map_err(PgMetaError::Query)?
1366 .as_ref()
1367 .map(entity_instance_from_row)
1368 .transpose()
1369 }
1370
1371 pub fn get_study_entity_instance(
1372 &mut self,
1373 study_id: StudyId,
1374 entity_id: EntityId,
1375 external_id: &str,
1376 ) -> Result<Option<PgStudyEntityInstanceReadModel>, PgMetaError> {
1377 self.client
1378 .query_opt(
1379 "
1380 SELECT study_id, entity_instance_id, entity_id,
1381 external_id, transformation_id
1382 FROM public.study_entity_instances
1383 WHERE study_id = $1 AND entity_id = $2 AND external_id = $3
1384 LIMIT 1
1385 ",
1386 &[
1387 &study_id.0,
1388 &id_i32(entity_id.0, "entity_id")?,
1389 &external_id,
1390 ],
1391 )
1392 .map_err(PgMetaError::Query)?
1393 .as_ref()
1394 .map(study_entity_instance_from_row)
1395 .transpose()
1396 }
1397
1398 pub fn list_study_entity_instances(
1399 &mut self,
1400 study_id: StudyId,
1401 ) -> Result<Vec<PgStudyEntityInstanceReadModel>, PgMetaError> {
1402 let rows = self
1403 .client
1404 .query(
1405 "
1406 SELECT study_id, entity_instance_id, entity_id,
1407 external_id, transformation_id
1408 FROM public.study_entity_instances
1409 WHERE study_id = $1
1410 ORDER BY entity_instance_id
1411 ",
1412 &[&study_id.0],
1413 )
1414 .map_err(PgMetaError::Query)?;
1415 rows.iter().map(study_entity_instance_from_row).collect()
1416 }
1417
1418 pub fn list_relation_instances_for_relation(
1419 &mut self,
1420 entity_relation_id: EntityRelationId,
1421 ) -> Result<Vec<PgRelationInstanceReadModel>, PgMetaError> {
1422 let rows = self
1423 .client
1424 .query(
1425 "
1426 SELECT relation_instance_id, uuid, entityrelation_id,
1427 entity_instance_id_1, entity_instance_id_2,
1428 valid_from, valid_to, note, transformation_id
1429 FROM public.relation_instances
1430 WHERE entityrelation_id = $1
1431 ORDER BY relation_instance_id
1432 ",
1433 &[&id_i32(entity_relation_id.0, "entityrelation_id")?],
1434 )
1435 .map_err(PgMetaError::Query)?;
1436 rows.iter().map(relation_instance_from_row).collect()
1437 }
1438
1439 pub fn get_relation_instance(
1440 &mut self,
1441 relation_instance_id: i64,
1442 ) -> Result<Option<PgRelationInstanceReadModel>, PgMetaError> {
1443 self.client
1444 .query_opt(
1445 "
1446 SELECT relation_instance_id, uuid, entityrelation_id,
1447 entity_instance_id_1, entity_instance_id_2,
1448 valid_from, valid_to, note, transformation_id
1449 FROM public.relation_instances
1450 WHERE relation_instance_id = $1
1451 LIMIT 1
1452 ",
1453 &[&relation_instance_id],
1454 )
1455 .map_err(PgMetaError::Query)?
1456 .as_ref()
1457 .map(relation_instance_from_row)
1458 .transpose()
1459 }
1460
1461 pub fn get_relation_instance_by_uuid(
1462 &mut self,
1463 uuid: Uuid,
1464 ) -> Result<Option<PgRelationInstanceReadModel>, PgMetaError> {
1465 self.client
1466 .query_opt(
1467 "
1468 SELECT relation_instance_id, uuid, entityrelation_id,
1469 entity_instance_id_1, entity_instance_id_2,
1470 valid_from, valid_to, note, transformation_id
1471 FROM public.relation_instances
1472 WHERE uuid = $1
1473 LIMIT 1
1474 ",
1475 &[&uuid],
1476 )
1477 .map_err(PgMetaError::Query)?
1478 .as_ref()
1479 .map(relation_instance_from_row)
1480 .transpose()
1481 }
1482
1483 pub fn get_study_relation_instance(
1484 &mut self,
1485 study_id: StudyId,
1486 entity_relation_id: EntityRelationId,
1487 external_id: &str,
1488 ) -> Result<Option<PgStudyRelationInstanceReadModel>, PgMetaError> {
1489 self.client
1490 .query_opt(
1491 "
1492 SELECT study_id, relation_instance_id, entityrelation_id,
1493 external_id, transformation_id
1494 FROM public.study_relation_instances
1495 WHERE study_id = $1 AND entityrelation_id = $2 AND external_id = $3
1496 LIMIT 1
1497 ",
1498 &[
1499 &study_id.0,
1500 &id_i32(entity_relation_id.0, "entityrelation_id")?,
1501 &external_id,
1502 ],
1503 )
1504 .map_err(PgMetaError::Query)?
1505 .as_ref()
1506 .map(study_relation_instance_from_row)
1507 .transpose()
1508 }
1509
1510 pub fn list_study_relation_instances(
1511 &mut self,
1512 study_id: StudyId,
1513 ) -> Result<Vec<PgStudyRelationInstanceReadModel>, PgMetaError> {
1514 let rows = self
1515 .client
1516 .query(
1517 "
1518 SELECT study_id, relation_instance_id, entityrelation_id,
1519 external_id, transformation_id
1520 FROM public.study_relation_instances
1521 WHERE study_id = $1
1522 ORDER BY relation_instance_id
1523 ",
1524 &[&study_id.0],
1525 )
1526 .map_err(PgMetaError::Query)?;
1527 rows.iter().map(study_relation_instance_from_row).collect()
1528 }
1529
1530 pub fn list_asset_version_entities(
1531 &mut self,
1532 version_id: VersionId,
1533 ) -> Result<Vec<PgAssetVersionEntityReadModel>, PgMetaError> {
1534 let rows = self
1535 .client
1536 .query(
1537 "
1538 SELECT version_id, entity_instance_id, transformation_id
1539 FROM public.asset_version_entities
1540 WHERE version_id = $1
1541 ORDER BY entity_instance_id
1542 ",
1543 &[&version_id.0],
1544 )
1545 .map_err(PgMetaError::Query)?;
1546 rows.iter().map(asset_version_entity_from_row).collect()
1547 }
1548
1549 pub fn list_asset_version_entities_for_instance(
1550 &mut self,
1551 entity_instance_id: i64,
1552 ) -> Result<Vec<PgAssetVersionEntityReadModel>, PgMetaError> {
1553 let rows = self
1554 .client
1555 .query(
1556 "
1557 SELECT version_id, entity_instance_id, transformation_id
1558 FROM public.asset_version_entities
1559 WHERE entity_instance_id = $1
1560 ORDER BY version_id
1561 ",
1562 &[&entity_instance_id],
1563 )
1564 .map_err(PgMetaError::Query)?;
1565 rows.iter().map(asset_version_entity_from_row).collect()
1566 }
1567
1568 pub fn list_dataset_version_entities(
1569 &mut self,
1570 version_id: VersionId,
1571 ) -> Result<Vec<PgDatasetVersionEntityReadModel>, PgMetaError> {
1572 let rows = self
1573 .client
1574 .query(
1575 "
1576 SELECT version_id, entity_instance_id, entity_variable_id,
1577 transformation_id
1578 FROM public.dataset_version_entities
1579 WHERE version_id = $1
1580 ORDER BY entity_instance_id
1581 ",
1582 &[&version_id.0],
1583 )
1584 .map_err(PgMetaError::Query)?;
1585 rows.iter().map(dataset_version_entity_from_row).collect()
1586 }
1587
1588 pub fn list_dataset_version_entities_for_instance(
1589 &mut self,
1590 entity_instance_id: i64,
1591 ) -> Result<Vec<PgDatasetVersionEntityReadModel>, PgMetaError> {
1592 let rows = self
1593 .client
1594 .query(
1595 "
1596 SELECT version_id, entity_instance_id, entity_variable_id,
1597 transformation_id
1598 FROM public.dataset_version_entities
1599 WHERE entity_instance_id = $1
1600 ORDER BY version_id, entity_variable_id
1601 ",
1602 &[&entity_instance_id],
1603 )
1604 .map_err(PgMetaError::Query)?;
1605 rows.iter().map(dataset_version_entity_from_row).collect()
1606 }
1607
1608 pub fn list_asset_version_relation_instances(
1609 &mut self,
1610 version_id: VersionId,
1611 ) -> Result<Vec<PgAssetVersionRelationInstanceReadModel>, PgMetaError> {
1612 let rows = self
1613 .client
1614 .query(
1615 "
1616 SELECT version_id, relation_instance_id, transformation_id
1617 FROM public.asset_version_relation_instances
1618 WHERE version_id = $1
1619 ORDER BY relation_instance_id
1620 ",
1621 &[&version_id.0],
1622 )
1623 .map_err(PgMetaError::Query)?;
1624 rows.iter()
1625 .map(asset_version_relation_instance_from_row)
1626 .collect()
1627 }
1628
1629 pub fn list_asset_version_relation_instances_for_instance(
1630 &mut self,
1631 relation_instance_id: i64,
1632 ) -> Result<Vec<PgAssetVersionRelationInstanceReadModel>, PgMetaError> {
1633 let rows = self
1634 .client
1635 .query(
1636 "
1637 SELECT version_id, relation_instance_id, transformation_id
1638 FROM public.asset_version_relation_instances
1639 WHERE relation_instance_id = $1
1640 ORDER BY version_id
1641 ",
1642 &[&relation_instance_id],
1643 )
1644 .map_err(PgMetaError::Query)?;
1645 rows.iter()
1646 .map(asset_version_relation_instance_from_row)
1647 .collect()
1648 }
1649
1650 pub fn list_dataset_version_relation_instances(
1651 &mut self,
1652 version_id: VersionId,
1653 ) -> Result<Vec<PgDatasetVersionRelationInstanceReadModel>, PgMetaError> {
1654 let rows = self
1655 .client
1656 .query(
1657 "
1658 SELECT version_id, relation_instance_id, subject_variable_id,
1659 object_variable_id, relation_variable_id, transformation_id
1660 FROM public.dataset_version_relation_instances
1661 WHERE version_id = $1
1662 ORDER BY relation_instance_id
1663 ",
1664 &[&version_id.0],
1665 )
1666 .map_err(PgMetaError::Query)?;
1667 rows.iter()
1668 .map(dataset_version_relation_instance_from_row)
1669 .collect()
1670 }
1671
1672 pub fn list_dataset_version_relation_instances_for_instance(
1673 &mut self,
1674 relation_instance_id: i64,
1675 ) -> Result<Vec<PgDatasetVersionRelationInstanceReadModel>, PgMetaError> {
1676 let rows = self
1677 .client
1678 .query(
1679 "
1680 SELECT version_id, relation_instance_id, subject_variable_id,
1681 object_variable_id, relation_variable_id, transformation_id
1682 FROM public.dataset_version_relation_instances
1683 WHERE relation_instance_id = $1
1684 ORDER BY version_id
1685 ",
1686 &[&relation_instance_id],
1687 )
1688 .map_err(PgMetaError::Query)?;
1689 rows.iter()
1690 .map(dataset_version_relation_instance_from_row)
1691 .collect()
1692 }
1693
1694 pub fn list_transformations(&mut self) -> Result<Vec<PgTransformationReadModel>, PgMetaError> {
1695 let rows = self
1696 .client
1697 .query(
1698 "
1699 SELECT transformation_id, transformation_type::text AS transformation_type,
1700 description, repository_url, commit_hash, file_path,
1701 date_created, created_by
1702 FROM public.transformations
1703 ORDER BY transformation_id
1704 ",
1705 &[],
1706 )
1707 .map_err(PgMetaError::Query)?;
1708 rows.iter().map(transformation_from_row).collect()
1709 }
1710
1711 pub fn get_transformation(
1712 &mut self,
1713 transformation_id: TransformationId,
1714 ) -> Result<Option<PgTransformationReadModel>, PgMetaError> {
1715 self.client
1716 .query_opt(
1717 "
1718 SELECT transformation_id, transformation_type::text AS transformation_type,
1719 description, repository_url, commit_hash, file_path,
1720 date_created, created_by
1721 FROM public.transformations
1722 WHERE transformation_id = $1
1723 ",
1724 &[&id_i32(transformation_id.0, "transformation_id")?],
1725 )
1726 .map_err(PgMetaError::Query)?
1727 .as_ref()
1728 .map(transformation_from_row)
1729 .transpose()
1730 }
1731
1732 pub fn list_transformation_inputs(
1733 &mut self,
1734 transformation_id: TransformationId,
1735 ) -> Result<Vec<PgTransformationInputReadModel>, PgMetaError> {
1736 let rows = self
1737 .client
1738 .query(
1739 "
1740 SELECT transformation_id, version_id
1741 FROM public.transformation_inputs
1742 WHERE transformation_id = $1
1743 ORDER BY version_id
1744 ",
1745 &[&id_i32(transformation_id.0, "transformation_id")?],
1746 )
1747 .map_err(PgMetaError::Query)?;
1748 rows.iter().map(transformation_input_from_row).collect()
1749 }
1750
1751 pub fn list_transformation_inputs_for_version(
1752 &mut self,
1753 version_id: VersionId,
1754 ) -> Result<Vec<PgTransformationInputReadModel>, PgMetaError> {
1755 let rows = self
1756 .client
1757 .query(
1758 "
1759 SELECT transformation_id, version_id
1760 FROM public.transformation_inputs
1761 WHERE version_id = $1
1762 ORDER BY transformation_id
1763 ",
1764 &[&version_id.0],
1765 )
1766 .map_err(PgMetaError::Query)?;
1767 rows.iter().map(transformation_input_from_row).collect()
1768 }
1769
1770 pub fn list_transformation_outputs(
1771 &mut self,
1772 transformation_id: TransformationId,
1773 ) -> Result<Vec<PgTransformationOutputReadModel>, PgMetaError> {
1774 let rows = self
1775 .client
1776 .query(
1777 "
1778 SELECT transformation_id, version_id
1779 FROM public.transformation_outputs
1780 WHERE transformation_id = $1
1781 ORDER BY version_id
1782 ",
1783 &[&id_i32(transformation_id.0, "transformation_id")?],
1784 )
1785 .map_err(PgMetaError::Query)?;
1786 rows.iter().map(transformation_output_from_row).collect()
1787 }
1788
1789 pub fn list_transformation_outputs_for_version(
1790 &mut self,
1791 version_id: VersionId,
1792 ) -> Result<Vec<PgTransformationOutputReadModel>, PgMetaError> {
1793 let rows = self
1794 .client
1795 .query(
1796 "
1797 SELECT transformation_id, version_id
1798 FROM public.transformation_outputs
1799 WHERE version_id = $1
1800 ORDER BY transformation_id
1801 ",
1802 &[&version_id.0],
1803 )
1804 .map_err(PgMetaError::Query)?;
1805 rows.iter().map(transformation_output_from_row).collect()
1806 }
1807
1808 pub fn list_transformations_producing_version(
1809 &mut self,
1810 version_id: VersionId,
1811 ) -> Result<Vec<PgTransformationReadModel>, PgMetaError> {
1812 let rows = self
1813 .client
1814 .query(
1815 "
1816 SELECT t.transformation_id,
1817 t.transformation_type::text AS transformation_type,
1818 t.description, t.repository_url, t.commit_hash, t.file_path,
1819 t.date_created, t.created_by
1820 FROM public.transformations t
1821 JOIN public.transformation_outputs o
1822 ON o.transformation_id = t.transformation_id
1823 WHERE o.version_id = $1
1824 ORDER BY t.transformation_id
1825 ",
1826 &[&version_id.0],
1827 )
1828 .map_err(PgMetaError::Query)?;
1829 rows.iter().map(transformation_from_row).collect()
1830 }
1831
1832 pub fn list_tags_for_target(
1833 &mut self,
1834 target: PgTagTarget,
1835 ) -> Result<Vec<String>, PgMetaError> {
1836 let (mapping_table, key_column, key_value) = target.mapping();
1837 let sql = format!(
1838 "
1839 SELECT t.name
1840 FROM public.tags t
1841 JOIN public.{mapping_table} m ON m.tag_id = t.tag_id
1842 WHERE m.{key_column} = $1
1843 ORDER BY t.name
1844 "
1845 );
1846 let rows = match key_value {
1847 PgTagKey::Uuid(value) => self.client.query(&sql, &[&value]),
1848 PgTagKey::Int(value) => self.client.query(&sql, &[&id_i32(value, "tag target id")?]),
1849 }
1850 .map_err(PgMetaError::Query)?;
1851 Ok(rows.iter().map(|row| row.get("name")).collect())
1852 }
1853
1854 pub fn list_tag_records_for_target(
1855 &mut self,
1856 target: PgTagTarget,
1857 ) -> Result<Vec<PgTagReadModel>, PgMetaError> {
1858 let (mapping_table, key_column, key_value) = target.mapping();
1859 let sql = format!(
1860 "
1861 SELECT t.tag_id, t.name
1862 FROM public.tags t
1863 JOIN public.{mapping_table} m ON m.tag_id = t.tag_id
1864 WHERE m.{key_column} = $1
1865 ORDER BY t.name
1866 "
1867 );
1868 let rows = match key_value {
1869 PgTagKey::Uuid(value) => self.client.query(&sql, &[&value]),
1870 PgTagKey::Int(value) => self.client.query(&sql, &[&id_i32(value, "tag target id")?]),
1871 }
1872 .map_err(PgMetaError::Query)?;
1873 rows.iter().map(tag_from_row).collect()
1874 }
1875
1876 pub fn list_study_tag_links(
1877 &mut self,
1878 study_id: StudyId,
1879 ) -> Result<Vec<PgStudyTagReadModel>, PgMetaError> {
1880 let rows = self
1881 .client
1882 .query(
1883 "
1884 SELECT study_id, tag_id
1885 FROM public.study_tags
1886 WHERE study_id = $1
1887 ORDER BY tag_id
1888 ",
1889 &[&study_id.0],
1890 )
1891 .map_err(PgMetaError::Query)?;
1892 rows.iter().map(study_tag_from_row).collect()
1893 }
1894
1895 pub fn list_domain_tag_links(
1896 &mut self,
1897 domain_id: DomainId,
1898 ) -> Result<Vec<PgDomainTagReadModel>, PgMetaError> {
1899 let rows = self
1900 .client
1901 .query(
1902 "
1903 SELECT domain_id, tag_id
1904 FROM public.domain_tags
1905 WHERE domain_id = $1
1906 ORDER BY tag_id
1907 ",
1908 &[&id_i32(domain_id.0, "domain_id")?],
1909 )
1910 .map_err(PgMetaError::Query)?;
1911 rows.iter().map(domain_tag_from_row).collect()
1912 }
1913
1914 pub fn list_variable_tag_links(
1915 &mut self,
1916 variable_id: VariableId,
1917 ) -> Result<Vec<PgVariableTagReadModel>, PgMetaError> {
1918 let rows = self
1919 .client
1920 .query(
1921 "
1922 SELECT variable_id, tag_id
1923 FROM public.variable_tags
1924 WHERE variable_id = $1
1925 ORDER BY tag_id
1926 ",
1927 &[&id_i32(variable_id.0, "variable_id")?],
1928 )
1929 .map_err(PgMetaError::Query)?;
1930 rows.iter().map(variable_tag_from_row).collect()
1931 }
1932
1933 pub fn list_asset_tag_links(
1934 &mut self,
1935 asset_id: AssetId,
1936 ) -> Result<Vec<PgAssetTagReadModel>, PgMetaError> {
1937 let rows = self
1938 .client
1939 .query(
1940 "
1941 SELECT asset_id, tag_id
1942 FROM public.asset_tags
1943 WHERE asset_id = $1
1944 ORDER BY tag_id
1945 ",
1946 &[&asset_id.0],
1947 )
1948 .map_err(PgMetaError::Query)?;
1949 rows.iter().map(asset_tag_from_row).collect()
1950 }
1951
1952 pub fn list_asset_version_tag_links(
1953 &mut self,
1954 version_id: VersionId,
1955 ) -> Result<Vec<PgAssetVersionTagReadModel>, PgMetaError> {
1956 let rows = self
1957 .client
1958 .query(
1959 "
1960 SELECT version_id, tag_id
1961 FROM public.asset_version_tags
1962 WHERE version_id = $1
1963 ORDER BY tag_id
1964 ",
1965 &[&version_id.0],
1966 )
1967 .map_err(PgMetaError::Query)?;
1968 rows.iter().map(asset_version_tag_from_row).collect()
1969 }
1970
1971 pub fn list_entity_tag_links(
1972 &mut self,
1973 entity_id: EntityId,
1974 ) -> Result<Vec<PgEntityTagReadModel>, PgMetaError> {
1975 let rows = self
1976 .client
1977 .query(
1978 "
1979 SELECT entity_id, tag_id
1980 FROM public.entity_tags
1981 WHERE entity_id = $1
1982 ORDER BY tag_id
1983 ",
1984 &[&id_i32(entity_id.0, "entity_id")?],
1985 )
1986 .map_err(PgMetaError::Query)?;
1987 rows.iter().map(entity_tag_from_row).collect()
1988 }
1989
1990 pub fn list_entity_relation_tag_links(
1991 &mut self,
1992 entity_relation_id: EntityRelationId,
1993 ) -> Result<Vec<PgEntityRelationTagReadModel>, PgMetaError> {
1994 let rows = self
1995 .client
1996 .query(
1997 "
1998 SELECT entityrelation_id, tag_id
1999 FROM public.entityrelation_tags
2000 WHERE entityrelation_id = $1
2001 ORDER BY tag_id
2002 ",
2003 &[&id_i32(entity_relation_id.0, "entityrelation_id")?],
2004 )
2005 .map_err(PgMetaError::Query)?;
2006 rows.iter().map(entity_relation_tag_from_row).collect()
2007 }
2008
2009 pub fn list_tags(&mut self) -> Result<Vec<PgTagReadModel>, PgMetaError> {
2010 let rows = self
2011 .client
2012 .query(
2013 "
2014 SELECT tag_id, name
2015 FROM public.tags
2016 ORDER BY name
2017 ",
2018 &[],
2019 )
2020 .map_err(PgMetaError::Query)?;
2021 rows.iter().map(tag_from_row).collect()
2022 }
2023}
2024
2025#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2027pub enum PgTagTarget {
2028 Study(StudyId),
2029 Domain(DomainId),
2030 Variable(VariableId),
2031 Asset(AssetId),
2032 AssetVersion(VersionId),
2033 Entity(EntityId),
2034 EntityRelation(EntityRelationId),
2035}
2036
2037impl PgTagTarget {
2038 fn mapping(self) -> (&'static str, &'static str, PgTagKey) {
2039 match self {
2040 Self::Study(id) => ("study_tags", "study_id", PgTagKey::Uuid(id.0)),
2041 Self::Domain(id) => ("domain_tags", "domain_id", PgTagKey::Int(id.0)),
2042 Self::Variable(id) => ("variable_tags", "variable_id", PgTagKey::Int(id.0)),
2043 Self::Asset(id) => ("asset_tags", "asset_id", PgTagKey::Uuid(id.0)),
2044 Self::AssetVersion(id) => ("asset_version_tags", "version_id", PgTagKey::Uuid(id.0)),
2045 Self::Entity(id) => ("entity_tags", "entity_id", PgTagKey::Int(id.0)),
2046 Self::EntityRelation(id) => (
2047 "entityrelation_tags",
2048 "entityrelation_id",
2049 PgTagKey::Int(id.0),
2050 ),
2051 }
2052 }
2053}
2054
2055#[derive(Debug, Clone, Copy)]
2056enum PgTagKey {
2057 Uuid(Uuid),
2058 Int(i64),
2059}
2060
2061fn study_from_row(row: &Row) -> Result<PgStudyReadModel, PgMetaError> {
2062 Ok(PgStudyReadModel {
2063 study_id: StudyId(row.get("study_id")),
2064 name: row.get("name"),
2065 description: row.get("description"),
2066 documentation: row.get("documentation"),
2067 agent_instructions: row.get("agent_instructions"),
2068 external_id: row.get("external_id"),
2069 study_type_id: optional_int_id(row, "study_type_id", StudyTypeId),
2070 date_created: row.get::<_, Option<NaiveDateTime>>("date_created").map(utc),
2071 created_by: row.get("created_by"),
2072 })
2073}
2074
2075fn study_type_from_row(row: &Row) -> Result<PgStudyTypeReadModel, PgMetaError> {
2076 Ok(PgStudyTypeReadModel {
2077 study_type_id: StudyTypeId(i64::from(row.get::<_, i32>("study_type_id"))),
2078 name: row.get("name"),
2079 namespace: row.get("namespace"),
2080 ontology_class: row.get("ontology_class"),
2081 description: row.get("description"),
2082 })
2083}
2084
2085fn domain_from_row(row: &Row) -> Result<PgDomainReadModel, PgMetaError> {
2086 Ok(PgDomainReadModel {
2087 domain_id: DomainId(i64::from(row.get::<_, i32>("domain_id"))),
2088 name: row.get("name"),
2089 uri: row.get("uri"),
2090 description: row.get("description"),
2091 })
2092}
2093
2094fn study_domain_from_row(row: &Row) -> Result<PgStudyDomainReadModel, PgMetaError> {
2095 Ok(PgStudyDomainReadModel {
2096 study_id: StudyId(row.get("study_id")),
2097 domain_id: DomainId(i64::from(row.get::<_, i32>("domain_id"))),
2098 })
2099}
2100
2101fn duo_code_from_row(row: &Row) -> Result<PgDuoCodeReadModel, PgMetaError> {
2102 Ok(PgDuoCodeReadModel {
2103 duo_id: row.get("duo_id"),
2104 duo_code: row.get("duo_code"),
2105 label: row.get("label"),
2106 definition: row.get("definition"),
2107 category: row.get("category"),
2108 qualifier_type: row.get("qualifier_type"),
2109 qualifier_required: row.get("qualifier_required"),
2110 qualifier_note: row.get("qualifier_note"),
2111 })
2112}
2113
2114fn study_access_from_row(row: &Row) -> Result<PgStudyAccessReadModel, PgMetaError> {
2115 Ok(PgStudyAccessReadModel {
2116 study_id: StudyId(row.get("study_id")),
2117 user_id: row.get("user_id"),
2118 date_granted: row.get::<_, Option<NaiveDateTime>>("date_granted").map(utc),
2119 granted_by: row.get("granted_by"),
2120 })
2121}
2122
2123fn study_custodian_from_row(row: &Row) -> Result<PgStudyCustodianReadModel, PgMetaError> {
2124 Ok(PgStudyCustodianReadModel {
2125 study_id: StudyId(row.get("study_id")),
2126 user_id: row.get("user_id"),
2127 is_primary: row.get("is_primary"),
2128 date_granted: row.get::<_, Option<NaiveDateTime>>("date_granted").map(utc),
2129 granted_by: row.get("granted_by"),
2130 })
2131}
2132
2133fn study_duo_from_row(row: &Row) -> Result<PgDuoRestrictionReadModel, PgMetaError> {
2134 Ok(PgDuoRestrictionReadModel {
2135 restriction_id: i64::from(row.get::<_, i32>("study_duo_restriction_id")),
2136 study_id: StudyId(row.get("study_id")),
2137 duo_id: row.get("duo_id"),
2138 duo_code: row.get("duo_code"),
2139 label: row.get("label"),
2140 qualifier_ontology_namespace: row.get("qualifier_ontology_namespace"),
2141 qualifier_ontology_id: row.get("qualifier_ontology_id"),
2142 qualifier_text: row.get("qualifier_text"),
2143 qualifier_date: row.get("qualifier_date"),
2144 qualifier_integer: row.get("qualifier_integer"),
2145 })
2146}
2147
2148fn asset_from_row(row: &Row) -> Result<PgAssetReadModel, PgMetaError> {
2149 Ok(PgAssetReadModel {
2150 asset_id: AssetId(row.get("asset_id")),
2151 study_id: StudyId(row.get("study_id")),
2152 name: row.get("name"),
2153 description: row.get("description"),
2154 agent_instructions: row.get("agent_instructions"),
2155 asset_type: parse_asset_type(row.get("asset_type"))?,
2156 date_created: row.get::<_, Option<NaiveDateTime>>("date_created").map(utc),
2157 created_by: row.get("created_by"),
2158 })
2159}
2160
2161fn asset_version_from_row(row: &Row) -> Result<PgAssetVersionReadModel, PgMetaError> {
2162 Ok(PgAssetVersionReadModel {
2163 version_id: VersionId(row.get("version_id")),
2164 asset_id: AssetId(row.get("asset_id")),
2165 major: row.get("major"),
2166 minor: row.get("minor"),
2167 patch: row.get("patch"),
2168 version_label: row.get("version_label"),
2169 version_note: row.get("version_note"),
2170 is_latest: row.get("is_latest"),
2171 doi: row.get("doi"),
2172 created_at: row.get::<_, Option<NaiveDateTime>>("created_at").map(utc),
2173 created_by: row.get("created_by"),
2174 })
2175}
2176
2177fn asset_version_duo_from_row(
2178 row: &Row,
2179) -> Result<PgAssetVersionDuoRestrictionReadModel, PgMetaError> {
2180 Ok(PgAssetVersionDuoRestrictionReadModel {
2181 restriction_id: i64::from(row.get::<_, i32>("asset_version_duo_restriction_id")),
2182 version_id: VersionId(row.get("version_id")),
2183 duo_id: row.get("duo_id"),
2184 duo_code: row.get("duo_code"),
2185 label: row.get("label"),
2186 qualifier_ontology_namespace: row.get("qualifier_ontology_namespace"),
2187 qualifier_ontology_id: row.get("qualifier_ontology_id"),
2188 qualifier_text: row.get("qualifier_text"),
2189 qualifier_date: row.get("qualifier_date"),
2190 qualifier_integer: row.get("qualifier_integer"),
2191 })
2192}
2193
2194fn dataset_from_row(row: &Row) -> Result<PgDatasetReadModel, PgMetaError> {
2195 Ok(PgDatasetReadModel {
2196 dataset_id: VersionId(row.get("dataset_id")),
2197 })
2198}
2199
2200fn datafile_from_row(row: &Row) -> Result<PgDataFileReadModel, PgMetaError> {
2201 Ok(PgDataFileReadModel {
2202 datafile_id: VersionId(row.get("datafile_id")),
2203 size_bytes: row.get("size_bytes"),
2204 compressed: row.get("compressed"),
2205 encrypted: row.get("encrypted"),
2206 compression_algorithm: row.get("compression_algorithm"),
2207 encryption_algorithm: row.get("encryption_algorithm"),
2208 encryption_key: row.get("encryption_key"),
2209 storage_uri: row.get("storage_uri"),
2210 edam_format: row.get("edam_format"),
2211 digest: row.get("digest"),
2212 })
2213}
2214
2215fn value_type_from_row(row: &Row) -> Result<PgValueTypeReadModel, PgMetaError> {
2216 Ok(PgValueTypeReadModel {
2217 value_type_id: ValueTypeId(i64::from(row.get::<_, i32>("value_type_id"))),
2218 value_type: row.get("value_type"),
2219 description: row.get("description"),
2220 })
2221}
2222
2223fn vocabulary_from_row(row: &Row) -> Result<PgVocabularyReadModel, PgMetaError> {
2224 Ok(PgVocabularyReadModel {
2225 vocabulary_id: VocabularyId(i64::from(row.get::<_, i32>("vocabulary_id"))),
2226 domain_id: DomainId(i64::from(row.get::<_, i32>("domain_id"))),
2227 name: row.get("name"),
2228 description: row.get("description"),
2229 })
2230}
2231
2232fn vocabulary_mapping_from_row(row: &Row) -> Result<PgVocabularyMappingReadModel, PgMetaError> {
2233 Ok(PgVocabularyMappingReadModel {
2234 vocabulary_mapping_id: i64::from(row.get::<_, i32>("vocabulary_mapping_id")),
2235 from_vocabulary_item: VocabularyItemId(i64::from(
2236 row.get::<_, i32>("from_vocabulary_item"),
2237 )),
2238 to_vocabulary_item: VocabularyItemId(i64::from(row.get::<_, i32>("to_vocabulary_item"))),
2239 })
2240}
2241
2242fn vocabulary_item_from_row(row: &Row) -> Result<PgVocabularyItemReadModel, PgMetaError> {
2243 Ok(PgVocabularyItemReadModel {
2244 vocabulary_item_id: VocabularyItemId(i64::from(row.get::<_, i32>("vocabulary_item_id"))),
2245 vocabulary_id: VocabularyId(i64::from(row.get::<_, i32>("vocabulary_id"))),
2246 value: row.get("value"),
2247 code: row.get("code"),
2248 description: row.get("description"),
2249 })
2250}
2251
2252fn variable_from_row(row: &Row) -> Result<PgVariableReadModel, PgMetaError> {
2253 Ok(PgVariableReadModel {
2254 variable_id: VariableId(i64::from(row.get::<_, i32>("variable_id"))),
2255 domain_id: DomainId(i64::from(row.get::<_, i32>("domain_id"))),
2256 name: row.get("name"),
2257 value_type_id: ValueTypeId(i64::from(row.get::<_, i32>("value_type_id"))),
2258 value_type: row.get("value_type"),
2259 value_format: row.get("value_format"),
2260 vocabulary_id: optional_int_id(row, "vocabulary_id", VocabularyId),
2261 key_role: parse_key_role(row.get("keyrole"))?,
2262 description: row.get("description"),
2263 note: row.get("note"),
2264 ontology_namespace: row.get("ontology_namespace"),
2265 ontology_class: row.get("ontology_class"),
2266 })
2267}
2268
2269fn variable_mapping_from_row(
2270 row: &Row,
2271) -> Result<provisional::PgVariableMappingReadModel, PgMetaError> {
2272 Ok(provisional::PgVariableMappingReadModel {
2273 mapping_id: i64::from(row.get::<_, i32>("mapping_id")),
2274 from_variable_id: VariableId(i64::from(row.get::<_, i32>("from_variable_id"))),
2275 to_variable_id: VariableId(i64::from(row.get::<_, i32>("to_variable_id"))),
2276 operator: row.get("operator"),
2277 operants: row.get("operants"),
2278 prerequisite_id: optional_int_id(row, "prerequisite_id", VariableId),
2279 })
2280}
2281
2282fn dataset_variable_from_row(row: &Row) -> Result<PgDatasetVariableReadModel, PgMetaError> {
2283 Ok(PgDatasetVariableReadModel {
2284 dataset_id: VersionId(row.get("dataset_id")),
2285 variable_id: VariableId(i64::from(row.get::<_, i32>("variable_id"))),
2286 row_role: parse_key_role(row.get("row_role"))?,
2287 })
2288}
2289
2290fn entity_from_row(row: &Row) -> Result<PgEntityReadModel, PgMetaError> {
2291 Ok(PgEntityReadModel {
2292 entity_id: EntityId(i64::from(row.get::<_, i32>("entity_id"))),
2293 domain_id: DomainId(i64::from(row.get::<_, i32>("domain_id"))),
2294 uuid: row.get("uuid"),
2295 name: row.get("name"),
2296 description: row.get("description"),
2297 agent_instructions: row.get("agent_instructions"),
2298 ontology_namespace: row.get("ontology_namespace"),
2299 ontology_class: row.get("ontology_class"),
2300 })
2301}
2302
2303fn entity_instance_from_row(row: &Row) -> Result<PgEntityInstanceReadModel, PgMetaError> {
2304 Ok(PgEntityInstanceReadModel {
2305 instance_id: row.get("instance_id"),
2306 uuid: row.get("uuid"),
2307 entity_id: EntityId(i64::from(row.get::<_, i32>("entity_id"))),
2308 label: row.get("label"),
2309 note: row.get("note"),
2310 transformation_id: TransformationId(i64::from(row.get::<_, i32>("transformation_id"))),
2311 })
2312}
2313
2314fn study_entity_instance_from_row(
2315 row: &Row,
2316) -> Result<PgStudyEntityInstanceReadModel, PgMetaError> {
2317 Ok(PgStudyEntityInstanceReadModel {
2318 study_id: StudyId(row.get("study_id")),
2319 entity_instance_id: row.get("entity_instance_id"),
2320 entity_id: EntityId(i64::from(row.get::<_, i32>("entity_id"))),
2321 external_id: row.get("external_id"),
2322 transformation_id: TransformationId(i64::from(row.get::<_, i32>("transformation_id"))),
2323 })
2324}
2325
2326fn entity_relation_from_row(row: &Row) -> Result<PgEntityRelationReadModel, PgMetaError> {
2327 Ok(PgEntityRelationReadModel {
2328 entity_relation_id: EntityRelationId(i64::from(row.get::<_, i32>("entityrelation_id"))),
2329 subject_entity_id: EntityId(i64::from(row.get::<_, i32>("subject_entity_id"))),
2330 object_entity_id: EntityId(i64::from(row.get::<_, i32>("object_entity_id"))),
2331 domain_id: DomainId(i64::from(row.get::<_, i32>("domain_id"))),
2332 uuid: row.get("uuid"),
2333 name: row.get("name"),
2334 description: row.get("description"),
2335 agent_instructions: row.get("agent_instructions"),
2336 ontology_namespace: row.get("ontology_namespace"),
2337 ontology_class: row.get("ontology_class"),
2338 })
2339}
2340
2341fn relation_instance_from_row(row: &Row) -> Result<PgRelationInstanceReadModel, PgMetaError> {
2342 Ok(PgRelationInstanceReadModel {
2343 relation_instance_id: row.get("relation_instance_id"),
2344 uuid: row.get("uuid"),
2345 entity_relation_id: EntityRelationId(i64::from(row.get::<_, i32>("entityrelation_id"))),
2346 entity_instance_id_1: row.get("entity_instance_id_1"),
2347 entity_instance_id_2: row.get("entity_instance_id_2"),
2348 valid_from: row.get("valid_from"),
2349 valid_to: row.get("valid_to"),
2350 note: row.get("note"),
2351 transformation_id: TransformationId(i64::from(row.get::<_, i32>("transformation_id"))),
2352 })
2353}
2354
2355fn study_relation_instance_from_row(
2356 row: &Row,
2357) -> Result<PgStudyRelationInstanceReadModel, PgMetaError> {
2358 Ok(PgStudyRelationInstanceReadModel {
2359 study_id: StudyId(row.get("study_id")),
2360 relation_instance_id: row.get("relation_instance_id"),
2361 entity_relation_id: EntityRelationId(i64::from(row.get::<_, i32>("entityrelation_id"))),
2362 external_id: row.get("external_id"),
2363 transformation_id: TransformationId(i64::from(row.get::<_, i32>("transformation_id"))),
2364 })
2365}
2366
2367fn asset_version_entity_from_row(row: &Row) -> Result<PgAssetVersionEntityReadModel, PgMetaError> {
2368 Ok(PgAssetVersionEntityReadModel {
2369 version_id: VersionId(row.get("version_id")),
2370 entity_instance_id: row.get("entity_instance_id"),
2371 transformation_id: TransformationId(i64::from(row.get::<_, i32>("transformation_id"))),
2372 })
2373}
2374
2375fn dataset_version_entity_from_row(
2376 row: &Row,
2377) -> Result<PgDatasetVersionEntityReadModel, PgMetaError> {
2378 Ok(PgDatasetVersionEntityReadModel {
2379 version_id: VersionId(row.get("version_id")),
2380 entity_instance_id: row.get("entity_instance_id"),
2381 entity_variable_id: VariableId(i64::from(row.get::<_, i32>("entity_variable_id"))),
2382 transformation_id: TransformationId(i64::from(row.get::<_, i32>("transformation_id"))),
2383 })
2384}
2385
2386fn asset_version_relation_instance_from_row(
2387 row: &Row,
2388) -> Result<PgAssetVersionRelationInstanceReadModel, PgMetaError> {
2389 Ok(PgAssetVersionRelationInstanceReadModel {
2390 version_id: VersionId(row.get("version_id")),
2391 relation_instance_id: row.get("relation_instance_id"),
2392 transformation_id: TransformationId(i64::from(row.get::<_, i32>("transformation_id"))),
2393 })
2394}
2395
2396fn dataset_version_relation_instance_from_row(
2397 row: &Row,
2398) -> Result<PgDatasetVersionRelationInstanceReadModel, PgMetaError> {
2399 Ok(PgDatasetVersionRelationInstanceReadModel {
2400 version_id: VersionId(row.get("version_id")),
2401 relation_instance_id: row.get("relation_instance_id"),
2402 subject_variable_id: VariableId(i64::from(row.get::<_, i32>("subject_variable_id"))),
2403 object_variable_id: VariableId(i64::from(row.get::<_, i32>("object_variable_id"))),
2404 relation_variable_id: optional_int_id(row, "relation_variable_id", VariableId),
2405 transformation_id: TransformationId(i64::from(row.get::<_, i32>("transformation_id"))),
2406 })
2407}
2408
2409fn transformation_from_row(row: &Row) -> Result<PgTransformationReadModel, PgMetaError> {
2410 Ok(PgTransformationReadModel {
2411 transformation_id: TransformationId(i64::from(row.get::<_, i32>("transformation_id"))),
2412 transformation_type: parse_transformation_type(row.get("transformation_type"))?,
2413 description: row.get("description"),
2414 repository_url: row.get("repository_url"),
2415 commit_hash: row.get("commit_hash"),
2416 file_path: row.get("file_path"),
2417 date_created: row.get::<_, Option<NaiveDateTime>>("date_created").map(utc),
2418 created_by: row.get("created_by"),
2419 })
2420}
2421
2422fn tag_from_row(row: &Row) -> Result<PgTagReadModel, PgMetaError> {
2423 Ok(PgTagReadModel {
2424 tag_id: i64::from(row.get::<_, i32>("tag_id")),
2425 name: row.get("name"),
2426 })
2427}
2428
2429fn study_tag_from_row(row: &Row) -> Result<PgStudyTagReadModel, PgMetaError> {
2430 Ok(PgStudyTagReadModel {
2431 study_id: StudyId(row.get("study_id")),
2432 tag_id: i64::from(row.get::<_, i32>("tag_id")),
2433 })
2434}
2435
2436fn domain_tag_from_row(row: &Row) -> Result<PgDomainTagReadModel, PgMetaError> {
2437 Ok(PgDomainTagReadModel {
2438 domain_id: DomainId(i64::from(row.get::<_, i32>("domain_id"))),
2439 tag_id: i64::from(row.get::<_, i32>("tag_id")),
2440 })
2441}
2442
2443fn variable_tag_from_row(row: &Row) -> Result<PgVariableTagReadModel, PgMetaError> {
2444 Ok(PgVariableTagReadModel {
2445 variable_id: VariableId(i64::from(row.get::<_, i32>("variable_id"))),
2446 tag_id: i64::from(row.get::<_, i32>("tag_id")),
2447 })
2448}
2449
2450fn asset_tag_from_row(row: &Row) -> Result<PgAssetTagReadModel, PgMetaError> {
2451 Ok(PgAssetTagReadModel {
2452 asset_id: AssetId(row.get("asset_id")),
2453 tag_id: i64::from(row.get::<_, i32>("tag_id")),
2454 })
2455}
2456
2457fn asset_version_tag_from_row(row: &Row) -> Result<PgAssetVersionTagReadModel, PgMetaError> {
2458 Ok(PgAssetVersionTagReadModel {
2459 version_id: VersionId(row.get("version_id")),
2460 tag_id: i64::from(row.get::<_, i32>("tag_id")),
2461 })
2462}
2463
2464fn entity_tag_from_row(row: &Row) -> Result<PgEntityTagReadModel, PgMetaError> {
2465 Ok(PgEntityTagReadModel {
2466 entity_id: EntityId(i64::from(row.get::<_, i32>("entity_id"))),
2467 tag_id: i64::from(row.get::<_, i32>("tag_id")),
2468 })
2469}
2470
2471fn entity_relation_tag_from_row(row: &Row) -> Result<PgEntityRelationTagReadModel, PgMetaError> {
2472 Ok(PgEntityRelationTagReadModel {
2473 entity_relation_id: EntityRelationId(i64::from(row.get::<_, i32>("entityrelation_id"))),
2474 tag_id: i64::from(row.get::<_, i32>("tag_id")),
2475 })
2476}
2477
2478fn transformation_input_from_row(row: &Row) -> Result<PgTransformationInputReadModel, PgMetaError> {
2479 Ok(PgTransformationInputReadModel {
2480 transformation_id: TransformationId(i64::from(row.get::<_, i32>("transformation_id"))),
2481 version_id: VersionId(row.get("version_id")),
2482 })
2483}
2484
2485fn transformation_output_from_row(
2486 row: &Row,
2487) -> Result<PgTransformationOutputReadModel, PgMetaError> {
2488 Ok(PgTransformationOutputReadModel {
2489 transformation_id: TransformationId(i64::from(row.get::<_, i32>("transformation_id"))),
2490 version_id: VersionId(row.get("version_id")),
2491 })
2492}
2493
2494fn parse_asset_type(value: String) -> Result<AssetType, PgMetaError> {
2495 match value.as_str() {
2496 "dataset" => Ok(AssetType::Dataset),
2497 "file" => Ok(AssetType::File),
2498 _ => decode_error("asset_type", value, "expected dataset or file"),
2499 }
2500}
2501
2502fn parse_key_role(value: String) -> Result<KeyRole, PgMetaError> {
2503 match value.as_str() {
2504 "none" => Ok(KeyRole::None),
2505 "record" => Ok(KeyRole::Record),
2506 "external" => Ok(KeyRole::External),
2507 _ => decode_error("keyrole", value, "expected none, record, or external"),
2508 }
2509}
2510
2511fn parse_transformation_type(value: String) -> Result<TransformationType, PgMetaError> {
2512 match value.as_str() {
2513 "ingest" => Ok(TransformationType::Ingest),
2514 "transform" => Ok(TransformationType::Transform),
2515 "entity" => Ok(TransformationType::Entity),
2516 "export" => Ok(TransformationType::Export),
2517 "repository" => Ok(TransformationType::Repository),
2518 _ => decode_error(
2519 "transformation_type",
2520 value,
2521 "expected ingest, transform, entity, export, or repository",
2522 ),
2523 }
2524}
2525
2526fn optional_int_id<T>(row: &Row, field: &'static str, wrap: fn(i64) -> T) -> Option<T> {
2527 row.get::<_, Option<i32>>(field)
2528 .map(|value| wrap(i64::from(value)))
2529}
2530
2531fn id_i32(value: i64, field: &'static str) -> Result<i32, PgMetaError> {
2532 i32::try_from(value).map_err(|_| PgMetaError::Decode {
2533 field,
2534 value: value.to_string(),
2535 message: "integer identifier is outside PostgreSQL INTEGER range".to_string(),
2536 })
2537}
2538
2539fn utc(value: NaiveDateTime) -> DateTime<Utc> {
2540 value.and_utc()
2541}
2542
2543fn decode_error<T>(
2544 field: &'static str,
2545 value: String,
2546 message: impl Into<String>,
2547) -> Result<T, PgMetaError> {
2548 Err(PgMetaError::Decode {
2549 field,
2550 value,
2551 message: message.into(),
2552 })
2553}
2554
2555#[cfg(test)]
2556mod tests {
2557 use super::*;
2558
2559 #[test]
2560 fn parse_key_role_decodes_external() {
2561 assert_eq!(
2562 parse_key_role("external".to_string()).expect("external should decode"),
2563 KeyRole::External
2564 );
2565 }
2566}