Skip to main content

dpp/data_contract/v1/methods/
schema.rs

1use crate::data_contract::accessors::v0::DataContractV0Getters;
2use crate::data_contract::accessors::v1::DataContractV1Getters;
3use crate::data_contract::document_type::accessors::DocumentTypeV0Getters;
4use crate::data_contract::document_type::class_methods::{
5    resolve_derived_index_properties, validate_preallocated_indexes_kept_on_removal,
6    validate_summable_off_count_indexes_lossless,
7};
8use crate::data_contract::document_type::DocumentType;
9use crate::data_contract::schema::DataContractSchemaMethodsV0;
10use crate::data_contract::{DataContractV1, DefinitionName, DocumentName};
11use crate::validation::operations::ProtocolValidationOperation;
12use crate::ProtocolError;
13use platform_value::Value;
14use platform_version::version::PlatformVersion;
15use std::collections::BTreeMap;
16
17impl DataContractSchemaMethodsV0 for DataContractV1 {
18    fn set_document_schemas(
19        &mut self,
20        schemas: BTreeMap<DocumentName, Value>,
21        defs: Option<BTreeMap<DefinitionName, Value>>,
22        full_validation: bool,
23        validation_operations: &mut Vec<ProtocolValidationOperation>,
24        platform_version: &PlatformVersion,
25    ) -> Result<(), ProtocolError> {
26        self.document_types = DocumentType::create_document_types_from_document_schemas(
27            self.id,
28            self.system_version_type(),
29            self.config.version(),
30            schemas,
31            defs.as_ref(),
32            &self.tokens,
33            &self.config,
34            full_validation,
35            !self.tokens.is_empty(),
36            validation_operations,
37            platform_version,
38        )?;
39
40        Ok(())
41    }
42
43    fn set_document_schema(
44        &mut self,
45        name: &str,
46        schema: Value,
47        full_validation: bool,
48        validation_operations: &mut Vec<ProtocolValidationOperation>,
49        platform_version: &PlatformVersion,
50    ) -> Result<(), ProtocolError> {
51        let document_type = DocumentType::try_from_schema(
52            self.id,
53            self.system_version_type(),
54            self.config.version(),
55            name,
56            schema,
57            self.schema_defs.as_ref(),
58            self.tokens(),
59            &self.config,
60            full_validation,
61            validation_operations,
62            platform_version,
63        )?;
64
65        self.document_types
66            .insert(document_type.name().clone(), document_type);
67
68        // A derived index property reading a schema property of another document type takes
69        // its type from it (protocol version 14): the type added, or one reading from it, is
70        // resolved as the parse of the whole contract resolves it. The rules on what it reads
71        // are the whole contract's parse's to judge.
72        let data_contract_system_version = self.system_version_type();
73        let contract_config_version = self.config.version();
74        resolve_derived_index_properties(
75            &mut self.document_types,
76            data_contract_system_version,
77            contract_config_version,
78            false,
79            platform_version,
80        )?;
81        // A preallocated index bound through a moderatedDocument reference needs every key of
82        // its path kept by the referenced type's removal record, judged as the parse of the
83        // whole contract judges it
84        if full_validation {
85            validate_preallocated_indexes_kept_on_removal(&self.document_types)?;
86            // Inert before protocol version 14: it judges only
87            // `summableOffCountIndex` indexes, which only meta-schema v3
88            // admits.
89            validate_summable_off_count_indexes_lossless(&self.document_types)?;
90        }
91
92        Ok(())
93    }
94
95    fn document_schemas(&self) -> BTreeMap<DocumentName, &Value> {
96        self.document_types
97            .iter()
98            .map(|(name, document_type)| (name.to_owned(), document_type.schema()))
99            .collect()
100    }
101
102    fn schema_defs(&self) -> Option<&BTreeMap<DefinitionName, Value>> {
103        self.schema_defs.as_ref()
104    }
105
106    fn set_schema_defs(
107        &mut self,
108        defs: Option<BTreeMap<DefinitionName, Value>>,
109        full_validation: bool,
110        validation_operations: &mut Vec<ProtocolValidationOperation>,
111        platform_version: &PlatformVersion,
112    ) -> Result<(), ProtocolError> {
113        let document_schemas = self
114            .document_types
115            .iter()
116            .map(|(name, document_type)| (name.to_owned(), document_type.schema().to_owned()))
117            .collect();
118
119        self.set_document_schemas(
120            document_schemas,
121            defs.clone(),
122            full_validation,
123            validation_operations,
124            platform_version,
125        )?;
126
127        self.schema_defs = defs;
128
129        Ok(())
130    }
131}
132
133#[cfg(test)]
134mod test {
135    use super::*;
136    use crate::data_contract::config::DataContractConfig;
137    use crate::data_contract::serialized_version::v0::DataContractInSerializationFormatV0;
138    use crate::data_contract::v1::DataContractV1;
139    use platform_value::{platform_value, Identifier};
140
141    #[test]
142    fn should_set_a_new_schema_defs() {
143        let platform_version = PlatformVersion::latest();
144
145        let config = DataContractConfig::default_for_version(platform_version)
146            .expect("should create a default config");
147
148        let schema = platform_value!({
149            "type": "object",
150            "properties": {
151                "a": {
152                    "type": "string",
153                    "maxLength": 10,
154                    "position": 0
155                }
156            },
157            "additionalProperties": false,
158        });
159
160        let serialization_format = DataContractInSerializationFormatV0 {
161            id: Identifier::random(),
162            config,
163            version: 0,
164            owner_id: Default::default(),
165            schema_defs: None,
166            document_schemas: BTreeMap::from([("document_type_name".to_string(), schema.clone())]),
167        };
168
169        let mut data_contract = DataContractV1::try_from_platform_versioned(
170            serialization_format.into(),
171            true,
172            &mut vec![],
173            platform_version,
174        )
175        .expect("should create a contract from serialization format");
176
177        let defs = platform_value!({
178            "test": {
179                "type": "string",
180            },
181        });
182
183        let defs_map = Some(defs.into_btree_string_map().expect("should convert to map"));
184
185        data_contract
186            .set_schema_defs(defs_map.clone(), true, &mut vec![], platform_version)
187            .expect("should set defs");
188
189        assert_eq!(defs_map.as_ref(), data_contract.schema_defs())
190    }
191
192    #[test]
193    fn should_set_empty_schema_defs() {
194        let platform_version = PlatformVersion::latest();
195
196        let config = DataContractConfig::default_for_version(platform_version)
197            .expect("should create a default config");
198
199        let defs = platform_value!({
200            "test": {
201                "type": "string",
202            },
203        });
204
205        let defs_map = Some(defs.into_btree_string_map().expect("should convert to map"));
206
207        let schema = platform_value!({
208            "type": "object",
209            "properties": {
210                "a": {
211                    "type": "string",
212                    "maxLength": 10,
213                    "position": 0
214                }
215            },
216            "additionalProperties": false,
217        });
218
219        let serialization_format = DataContractInSerializationFormatV0 {
220            id: Identifier::random(),
221            config,
222            version: 0,
223            owner_id: Default::default(),
224            schema_defs: defs_map,
225            document_schemas: BTreeMap::from([("document_type_name".to_string(), schema)]),
226        };
227
228        let mut data_contract = DataContractV1::try_from_platform_versioned(
229            serialization_format.into(),
230            true,
231            &mut vec![],
232            platform_version,
233        )
234        .expect("should create a contract from serialization format");
235
236        data_contract
237            .set_schema_defs(None, true, &mut vec![], platform_version)
238            .expect("should set defs");
239
240        assert_eq!(None, data_contract.schema_defs());
241        assert_eq!(
242            1,
243            data_contract.document_types().len(),
244            "document types should be preserved after clearing schema defs"
245        );
246    }
247}