scim_server/resource/value_objects/
factory.rs

1//! Dynamic value object factory for schema-driven construction.
2//!
3//! This module provides a factory system that can dynamically create value objects
4//! based on SCIM schema definitions and JSON values. It supports both core SCIM
5//! attributes and extension attributes, enabling flexible resource construction
6//! while maintaining type safety and validation.
7//!
8//! ## Design Principles
9//!
10//! - **Schema-Driven**: Factory decisions based on attribute definitions
11//! - **Extensible**: Support for registering new value object types
12//! - **Type-Safe**: Compile-time guarantees where possible
13//! - **Performance**: Efficient lookup and construction mechanisms
14
15use super::extension::ExtensionAttributeValue;
16use super::value_object_trait::{ValueObject, ValueObjectConstructor, ValueObjectRegistry};
17use super::{EmailAddress, ExternalId, Name, ResourceId, SchemaUri, UserName};
18use crate::error::{ValidationError, ValidationResult};
19use crate::schema::types::{AttributeDefinition, AttributeType};
20use serde_json::Value;
21use std::collections::HashMap;
22
23/// Factory for creating value objects from schema definitions and JSON values.
24///
25/// The factory maintains a registry of constructors and can dynamically
26/// create appropriate value objects based on attribute definitions.
27pub struct ValueObjectFactory {
28    registry: ValueObjectRegistry,
29    type_mappings: HashMap<String, AttributeType>,
30}
31
32impl ValueObjectFactory {
33    /// Create a new factory with default constructors registered.
34    pub fn new() -> Self {
35        let mut factory = Self {
36            registry: ValueObjectRegistry::new(),
37            type_mappings: HashMap::new(),
38        };
39
40        factory.register_default_constructors();
41        factory.setup_type_mappings();
42        factory
43    }
44
45    /// Create a value object from a schema definition and JSON value.
46    pub fn create_value_object(
47        &self,
48        definition: &AttributeDefinition,
49        value: &Value,
50    ) -> ValidationResult<Box<dyn ValueObject>> {
51        // Handle null values for optional attributes
52        if matches!(value, Value::Null) && !definition.required {
53            return Err(ValidationError::NullValueForOptionalAttribute(
54                definition.name.clone(),
55            ));
56        }
57
58        // Handle multi-valued attributes
59        if definition.multi_valued {
60            return self.create_multi_valued_object(definition, value);
61        }
62
63        // Try registered constructors first
64        match self.registry.create_value_object(definition, value) {
65            Ok(obj) => Ok(obj),
66            Err(_) => {
67                // Fall back to extension attribute if no specific constructor found
68                self.create_extension_attribute(definition, value)
69            }
70        }
71    }
72
73    /// Create a multi-valued attribute object.
74    fn create_multi_valued_object(
75        &self,
76        definition: &AttributeDefinition,
77        value: &Value,
78    ) -> ValidationResult<Box<dyn ValueObject>> {
79        let array = value
80            .as_array()
81            .ok_or_else(|| ValidationError::ExpectedArray(definition.name.clone()))?;
82
83        // Create individual value objects for each array element
84        let mut objects = Vec::new();
85        for item_value in array {
86            // Create a single-valued definition for each item
87            let item_definition = AttributeDefinition {
88                multi_valued: false,
89                ..definition.clone()
90            };
91
92            let obj = self.create_value_object(&item_definition, item_value)?;
93            objects.push(obj);
94        }
95
96        // For now, we'll create a generic multi-valued container
97        // In a more sophisticated implementation, we could have specific
98        // multi-valued types for different attribute types
99        Ok(Box::new(GenericMultiValuedAttribute::new(
100            definition.name.clone(),
101            objects,
102        )))
103    }
104
105    /// Create an extension attribute value object.
106    fn create_extension_attribute(
107        &self,
108        definition: &AttributeDefinition,
109        value: &Value,
110    ) -> ValidationResult<Box<dyn ValueObject>> {
111        // Create a default schema URI for unknown extensions
112        let schema_uri = SchemaUri::new(format!(
113            "urn:ietf:params:scim:schemas:extension:unknown:{}",
114            definition.name
115        ))?;
116
117        let ext_attr = ExtensionAttributeValue::new(
118            schema_uri,
119            definition.name.clone(),
120            value.clone(),
121            Some(definition.clone()),
122        )?;
123
124        Ok(Box::new(ext_attr))
125    }
126
127    /// Register default constructors for built-in value objects.
128    fn register_default_constructors(&mut self) {
129        // Register constructors for core value objects
130        self.registry
131            .register_constructor(Box::new(ResourceIdConstructor::new()));
132        self.registry
133            .register_constructor(Box::new(UserNameConstructor::new()));
134        self.registry
135            .register_constructor(Box::new(ExternalIdConstructor::new()));
136        self.registry
137            .register_constructor(Box::new(EmailAddressConstructor::new()));
138        self.registry
139            .register_constructor(Box::new(SchemaUriConstructor::new()));
140        self.registry
141            .register_constructor(Box::new(NameConstructor::new()));
142        // TODO: Add constructors for Address, PhoneNumber, and Meta when from_json methods are implemented
143    }
144
145    /// Setup mappings from attribute names to expected types.
146    fn setup_type_mappings(&mut self) {
147        self.type_mappings
148            .insert("id".to_string(), AttributeType::String);
149        self.type_mappings
150            .insert("userName".to_string(), AttributeType::String);
151        self.type_mappings
152            .insert("externalId".to_string(), AttributeType::String);
153        self.type_mappings
154            .insert("schemas".to_string(), AttributeType::Reference);
155        self.type_mappings
156            .insert("name".to_string(), AttributeType::Complex);
157        self.type_mappings
158            .insert("emails".to_string(), AttributeType::Complex);
159        self.type_mappings
160            .insert("phoneNumbers".to_string(), AttributeType::Complex);
161        self.type_mappings
162            .insert("addresses".to_string(), AttributeType::Complex);
163        self.type_mappings
164            .insert("meta".to_string(), AttributeType::Complex);
165    }
166
167    /// Get the expected attribute type for a given attribute name.
168    pub fn get_expected_type(&self, attribute_name: &str) -> Option<AttributeType> {
169        self.type_mappings.get(attribute_name).cloned()
170    }
171
172    /// Register a custom constructor.
173    pub fn register_constructor(&mut self, constructor: Box<dyn ValueObjectConstructor>) {
174        self.registry.register_constructor(constructor);
175    }
176
177    /// Validate composite rules across multiple value objects.
178    pub fn validate_composite_rules(
179        &self,
180        objects: &[Box<dyn ValueObject>],
181    ) -> ValidationResult<()> {
182        self.registry.validate_composite_rules(objects)
183    }
184
185    /// Check if the factory has any constructors registered.
186    pub fn has_constructors(&self) -> bool {
187        self.registry.has_constructors()
188    }
189
190    /// Create a collection of value objects from a JSON object.
191    pub fn create_value_objects_from_json(
192        &self,
193        definitions: &[AttributeDefinition],
194        json_obj: &serde_json::Map<String, Value>,
195    ) -> ValidationResult<Vec<Box<dyn ValueObject>>> {
196        let mut objects = Vec::new();
197
198        for definition in definitions {
199            if let Some(value) = json_obj.get(&definition.name) {
200                let obj = self.create_value_object(definition, value)?;
201                objects.push(obj);
202            } else if definition.required {
203                return Err(ValidationError::RequiredAttributeMissing(
204                    definition.name.clone(),
205                ));
206            }
207        }
208
209        Ok(objects)
210    }
211}
212
213impl Default for ValueObjectFactory {
214    fn default() -> Self {
215        Self::new()
216    }
217}
218
219/// Generic multi-valued attribute container for factory-created objects.
220#[derive(Debug)]
221pub struct GenericMultiValuedAttribute {
222    attribute_name: String,
223    values: Vec<Box<dyn ValueObject>>,
224    primary_index: Option<usize>,
225}
226
227impl GenericMultiValuedAttribute {
228    pub fn new(attribute_name: String, values: Vec<Box<dyn ValueObject>>) -> Self {
229        Self {
230            attribute_name,
231            values,
232            primary_index: None,
233        }
234    }
235
236    pub fn values(&self) -> &[Box<dyn ValueObject>] {
237        &self.values
238    }
239
240    pub fn primary(&self) -> Option<&Box<dyn ValueObject>> {
241        self.primary_index.map(|idx| &self.values[idx])
242    }
243
244    pub fn set_primary(&mut self, index: usize) -> ValidationResult<()> {
245        if index >= self.values.len() {
246            return Err(ValidationError::InvalidPrimaryIndex {
247                attribute: self.attribute_name.clone(),
248                index,
249                length: self.values.len(),
250            });
251        }
252        self.primary_index = Some(index);
253        Ok(())
254    }
255}
256
257impl ValueObject for GenericMultiValuedAttribute {
258    fn attribute_type(&self) -> AttributeType {
259        AttributeType::Complex // Multi-valued attributes are complex
260    }
261
262    fn attribute_name(&self) -> &str {
263        &self.attribute_name
264    }
265
266    fn to_json(&self) -> ValidationResult<Value> {
267        let mut array = Vec::new();
268        for value_obj in &self.values {
269            array.push(value_obj.to_json()?);
270        }
271        Ok(Value::Array(array))
272    }
273
274    fn validate_against_schema(&self, definition: &AttributeDefinition) -> ValidationResult<()> {
275        if !definition.multi_valued {
276            return Err(ValidationError::NotMultiValued(definition.name.clone()));
277        }
278
279        // Validate each value object
280        let single_def = AttributeDefinition {
281            multi_valued: false,
282            ..definition.clone()
283        };
284
285        for value_obj in &self.values {
286            value_obj.validate_against_schema(&single_def)?;
287        }
288
289        Ok(())
290    }
291
292    fn as_json_value(&self) -> Value {
293        self.to_json().unwrap_or(Value::Null)
294    }
295
296    fn supports_definition(&self, definition: &AttributeDefinition) -> bool {
297        definition.multi_valued && definition.name == self.attribute_name
298    }
299
300    fn clone_boxed(&self) -> Box<dyn ValueObject> {
301        Box::new(GenericMultiValuedAttribute {
302            attribute_name: self.attribute_name.clone(),
303            values: self.values.iter().map(|v| v.clone_boxed()).collect(),
304            primary_index: self.primary_index,
305        })
306    }
307
308    fn as_any(&self) -> &dyn std::any::Any {
309        self
310    }
311}
312
313// Specific constructors for built-in value objects
314
315pub struct ResourceIdConstructor;
316
317impl ResourceIdConstructor {
318    pub fn new() -> Self {
319        Self
320    }
321}
322
323impl ValueObjectConstructor for ResourceIdConstructor {
324    fn try_construct(
325        &self,
326        definition: &AttributeDefinition,
327        value: &Value,
328    ) -> Option<ValidationResult<Box<dyn ValueObject>>> {
329        if definition.name == "id" && definition.data_type == AttributeType::String {
330            if let Some(id_str) = value.as_str() {
331                Some(
332                    ResourceId::new(id_str.to_string())
333                        .map(|id| Box::new(id) as Box<dyn ValueObject>),
334                )
335            } else {
336                Some(Err(ValidationError::InvalidAttributeType {
337                    attribute: definition.name.clone(),
338                    expected: "string".to_string(),
339                    actual: "non-string".to_string(),
340                }))
341            }
342        } else {
343            None
344        }
345    }
346
347    fn priority(&self) -> u8 {
348        100 // High priority for exact matches
349    }
350
351    fn description(&self) -> &str {
352        "ResourceId constructor for 'id' attributes"
353    }
354}
355
356pub struct UserNameConstructor;
357
358impl UserNameConstructor {
359    pub fn new() -> Self {
360        Self
361    }
362}
363
364impl ValueObjectConstructor for UserNameConstructor {
365    fn try_construct(
366        &self,
367        definition: &AttributeDefinition,
368        value: &Value,
369    ) -> Option<ValidationResult<Box<dyn ValueObject>>> {
370        if definition.name == "userName" && definition.data_type == AttributeType::String {
371            if let Some(username_str) = value.as_str() {
372                Some(
373                    UserName::new(username_str.to_string())
374                        .map(|username| Box::new(username) as Box<dyn ValueObject>),
375                )
376            } else {
377                Some(Err(ValidationError::InvalidAttributeType {
378                    attribute: definition.name.clone(),
379                    expected: "string".to_string(),
380                    actual: "non-string".to_string(),
381                }))
382            }
383        } else {
384            None
385        }
386    }
387
388    fn priority(&self) -> u8 {
389        100
390    }
391
392    fn description(&self) -> &str {
393        "UserName constructor for 'userName' attributes"
394    }
395}
396
397pub struct ExternalIdConstructor;
398
399impl ExternalIdConstructor {
400    pub fn new() -> Self {
401        Self
402    }
403}
404
405impl ValueObjectConstructor for ExternalIdConstructor {
406    fn try_construct(
407        &self,
408        definition: &AttributeDefinition,
409        value: &Value,
410    ) -> Option<ValidationResult<Box<dyn ValueObject>>> {
411        if definition.name == "externalId" && definition.data_type == AttributeType::String {
412            if let Some(ext_id_str) = value.as_str() {
413                Some(
414                    ExternalId::new(ext_id_str.to_string())
415                        .map(|ext_id| Box::new(ext_id) as Box<dyn ValueObject>),
416                )
417            } else {
418                Some(Err(ValidationError::InvalidAttributeType {
419                    attribute: definition.name.clone(),
420                    expected: "string".to_string(),
421                    actual: "non-string".to_string(),
422                }))
423            }
424        } else {
425            None
426        }
427    }
428
429    fn priority(&self) -> u8 {
430        100
431    }
432
433    fn description(&self) -> &str {
434        "ExternalId constructor for 'externalId' attributes"
435    }
436}
437
438pub struct EmailAddressConstructor;
439
440impl EmailAddressConstructor {
441    pub fn new() -> Self {
442        Self
443    }
444}
445
446impl ValueObjectConstructor for EmailAddressConstructor {
447    fn try_construct(
448        &self,
449        definition: &AttributeDefinition,
450        value: &Value,
451    ) -> Option<ValidationResult<Box<dyn ValueObject>>> {
452        if (definition.name == "value" || definition.name.contains("email"))
453            && definition.data_type == AttributeType::String
454        {
455            if let Some(email_str) = value.as_str() {
456                Some(
457                    EmailAddress::new(email_str.to_string(), None, None, None)
458                        .map(|email| Box::new(email) as Box<dyn ValueObject>),
459                )
460            } else {
461                Some(Err(ValidationError::InvalidAttributeType {
462                    attribute: definition.name.clone(),
463                    expected: "string".to_string(),
464                    actual: "non-string".to_string(),
465                }))
466            }
467        } else {
468            None
469        }
470    }
471
472    fn priority(&self) -> u8 {
473        80 // Lower priority than exact name matches
474    }
475
476    fn description(&self) -> &str {
477        "EmailAddress constructor for email-related attributes"
478    }
479}
480
481pub struct SchemaUriConstructor;
482
483impl SchemaUriConstructor {
484    pub fn new() -> Self {
485        Self
486    }
487}
488
489impl ValueObjectConstructor for SchemaUriConstructor {
490    fn try_construct(
491        &self,
492        _definition: &AttributeDefinition,
493        _value: &Value,
494    ) -> Option<ValidationResult<Box<dyn ValueObject>>> {
495        // TODO: Implement when SchemaUri implements ValueObject trait
496        None
497    }
498
499    fn priority(&self) -> u8 {
500        100
501    }
502
503    fn description(&self) -> &str {
504        "SchemaUri constructor for 'schemas' attributes"
505    }
506}
507
508pub struct NameConstructor;
509
510impl NameConstructor {
511    pub fn new() -> Self {
512        Self
513    }
514}
515
516impl ValueObjectConstructor for NameConstructor {
517    fn try_construct(
518        &self,
519        definition: &AttributeDefinition,
520        value: &Value,
521    ) -> Option<ValidationResult<Box<dyn ValueObject>>> {
522        if definition.name == "name" && definition.data_type == AttributeType::Complex {
523            Some(Name::from_json(value).map(|name| Box::new(name) as Box<dyn ValueObject>))
524        } else {
525            None
526        }
527    }
528
529    fn priority(&self) -> u8 {
530        100
531    }
532
533    fn description(&self) -> &str {
534        "Name constructor for 'name' complex attributes"
535    }
536}
537
538// TODO: Implement constructors for Address, PhoneNumber, and Meta when from_json methods are available
539
540#[cfg(test)]
541mod tests {
542    use super::*;
543    use crate::schema::types::{Mutability, Uniqueness};
544
545    fn create_string_definition(name: &str) -> AttributeDefinition {
546        AttributeDefinition {
547            name: name.to_string(),
548            data_type: AttributeType::String,
549            multi_valued: false,
550            required: false,
551            case_exact: false,
552            mutability: Mutability::ReadWrite,
553            uniqueness: Uniqueness::None,
554            canonical_values: vec![],
555            sub_attributes: vec![],
556            returned: None,
557        }
558    }
559
560    #[test]
561    fn test_factory_creation() {
562        let factory = ValueObjectFactory::new();
563        assert!(factory.has_constructors());
564    }
565
566    #[test]
567    fn test_resource_id_construction() {
568        let factory = ValueObjectFactory::new();
569        let definition = create_string_definition("id");
570        let value = Value::String("test-id".to_string());
571
572        let result = factory.create_value_object(&definition, &value);
573        assert!(result.is_ok());
574
575        let obj = result.unwrap();
576        assert_eq!(obj.attribute_name(), "id");
577        assert_eq!(obj.attribute_type(), AttributeType::String);
578    }
579
580    #[test]
581    fn test_username_construction() {
582        let factory = ValueObjectFactory::new();
583        let definition = create_string_definition("userName");
584        let value = Value::String("testuser".to_string());
585
586        let result = factory.create_value_object(&definition, &value);
587        assert!(result.is_ok());
588
589        let obj = result.unwrap();
590        assert_eq!(obj.attribute_name(), "userName");
591    }
592}