1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
use crate::decoder::RecordSchema;
use crate::indexer::mapping::MappingWithMeta;
use crate::stash::DecryptedCollection;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use uuid::Uuid;

/// The schema for a collection - combining the schema for individual records with the collection's
/// index specifications.
#[derive(Deserialize, Serialize)]
pub struct CollectionSchema {
    pub indexes: HashMap<String, MappingWithMeta>,
    // This is required because serde will strip the "alias" field when flattened.
    // Since the `CollectionSchema` is flattened onto [`AnnotatedCollectionSchema`] it needs to be
    // renamed to "type" and then aliased to support both.
    #[serde(rename = "type", alias = "schema")]
    pub schema: RecordSchema,
}

#[derive(Deserialize, Serialize)]
pub struct AnnotatedCollectionSchema {
    pub id: Uuid,
    pub name: String,
    #[serde(alias = "ref")]
    pub collection_ref: Vec<u8>,
    #[serde(flatten)]
    pub schema: CollectionSchema,
}

impl From<DecryptedCollection> for AnnotatedCollectionSchema {
    fn from(collection: DecryptedCollection) -> Self {
        let indexes: HashMap<String, MappingWithMeta> = collection
            .indexes
            .into_iter()
            .map(|index| {
                let name = index.settings.index_name().to_string();
                let meta = index.settings.into_mapping_with_meta(index.id);

                (name, meta)
            })
            .collect();

        Self {
            name: collection.metadata.name,
            id: collection.id,
            schema: CollectionSchema {
                indexes,
                schema: collection.metadata.record_type,
            },
            collection_ref: collection.collection_ref,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use serde_json::json;

    #[test]
    fn test_load_from_json() {
        let schema: AnnotatedCollectionSchema = serde_json::from_value(json!({
            "name": "Test Collection",
            "id": "00000000-0000-0000-0000-000000000000",
            "ref": [ 1, 2, 3 ],
            "type": {
                "name": "string",
                "email": "string",
                "dob": "date"
            },
            "indexes": {
                "name": {
                    "kind": "exact",
                    "field": "name",
                    "$prpKey": [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 ],
                    "$prfKey": [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 ],
                    "$indexId": "00000000-0000-0000-0000-000000000000"
                }
            }
        }))
        .expect("Expected schema to load");

        assert_eq!(schema.name, "Test Collection")
    }
}