reactive_graph_graphql_schema/query/instances/
properties.rs

1use std::collections::HashMap;
2use std::sync::Arc;
3
4use async_graphql::Context;
5use async_graphql::InputObject;
6use async_graphql::Object;
7use serde::Deserialize;
8use serde::Serialize;
9use serde_json::Value;
10
11use reactive_graph_graph::EntityTypeId;
12use reactive_graph_graph::PropertyInstances;
13use reactive_graph_graph::PropertyTypes;
14use reactive_graph_graph::RelationTypeId;
15use reactive_graph_type_system_api::EntityTypeManager;
16use reactive_graph_type_system_api::RelationTypeManager;
17
18use crate::query::GraphQLPropertyType;
19
20#[derive(Serialize, Deserialize, Clone, Debug, Default)]
21pub enum GraphQLPropertyTypeContainer {
22    #[default]
23    None,
24    Entity(EntityTypeId),
25    Relation(RelationTypeId),
26}
27
28#[derive(Serialize, Deserialize, Clone, Debug, InputObject)]
29#[graphql(name = "PropertyInstanceDefinition")]
30pub struct GraphQLPropertyInstance {
31    #[graphql(skip)]
32    pub property_type_container: GraphQLPropertyTypeContainer,
33
34    /// The name of the property.
35    pub name: String,
36
37    /// The value of the property as JSON representation.
38    pub value: Value,
39}
40
41/// The named property stores a value/document as JSON representation.
42///
43/// Each property is represented by it's name (String) and it's value. The value is
44/// a representation of a JSON value/document. Therefore the value can be boolean,
45/// number, string, array or an object. For more information about the data types
46/// please look at https://docs.serde.rs/serde_json/value/enum.Value.html
47#[Object(name = "PropertyInstance")]
48impl GraphQLPropertyInstance {
49    /// The name of the property.
50    async fn name(&self) -> String {
51        self.name.clone()
52    }
53
54    /// The value of the property as JSON representation.
55    async fn value(&self) -> Value {
56        self.value.clone()
57    }
58
59    /// The type of the property.
60    #[graphql(name = "type")]
61    async fn property_type(&self, context: &Context<'_>) -> Option<GraphQLPropertyType> {
62        let property_name = self.name.clone();
63        match &self.property_type_container {
64            GraphQLPropertyTypeContainer::None => None,
65            GraphQLPropertyTypeContainer::Entity(ty) => match context.data::<Arc<dyn EntityTypeManager + Send + Sync>>() {
66                Ok(entity_type_manager) => match entity_type_manager.get(ty) {
67                    Some(entity_type) => entity_type
68                        .properties
69                        .iter()
70                        .find(|property_type| property_type.name == property_name)
71                        .map(|property_type| property_type.value().clone().into()),
72                    None => None,
73                },
74                Err(_) => None,
75            },
76            GraphQLPropertyTypeContainer::Relation(ty) => match context.data::<Arc<dyn RelationTypeManager + Send + Sync>>() {
77                Ok(relation_type_manager) => match relation_type_manager.get(ty) {
78                    Some(relation_type) => relation_type
79                        .properties
80                        .iter()
81                        .find(|property_type| property_type.name == property_name)
82                        .map(|property_type| property_type.value().clone().into()),
83                    None => None,
84                },
85                Err(_) => None,
86            },
87        }
88    }
89}
90
91impl GraphQLPropertyInstance {
92    pub fn new_entity_property(ty: EntityTypeId, name: String, value: Value) -> Self {
93        GraphQLPropertyInstance {
94            property_type_container: GraphQLPropertyTypeContainer::Entity(ty),
95            name,
96            value,
97        }
98    }
99
100    // TODO: Change to ty: RelationInstanceTypeId ???
101    pub fn new_relation_property(ty: RelationTypeId, name: String, value: Value) -> Self {
102        GraphQLPropertyInstance {
103            property_type_container: GraphQLPropertyTypeContainer::Relation(ty),
104            name,
105            value,
106        }
107    }
108
109    pub fn to_map(properties: Option<Vec<GraphQLPropertyInstance>>) -> HashMap<String, Value> {
110        match properties {
111            Some(properties) => {
112                let mut props = HashMap::new();
113                for property in properties {
114                    props.insert(property.name.clone(), property.value.clone());
115                }
116                props
117            }
118            None => HashMap::new(),
119        }
120    }
121
122    pub fn to_map_with_defaults(properties: Option<Vec<GraphQLPropertyInstance>>, property_types: PropertyTypes) -> HashMap<String, Value> {
123        let mut props = HashMap::new();
124        for property_type in property_types.iter() {
125            props.insert(property_type.name.clone(), property_type.data_type.default_value());
126        }
127        if let Some(properties) = properties {
128            for property in properties {
129                props.insert(property.name.clone(), property.value.clone());
130            }
131        }
132        props
133    }
134
135    pub fn to_property_instances_with_defaults(properties: Option<Vec<GraphQLPropertyInstance>>, property_types: PropertyTypes) -> PropertyInstances {
136        let property_instances = PropertyInstances::new();
137        for property_type in property_types.iter() {
138            property_instances.insert(property_type.name.clone(), property_type.data_type.default_value());
139        }
140        if let Some(properties) = properties {
141            for property in properties {
142                property_instances.insert(property.name.clone(), property.value.clone());
143            }
144        }
145        property_instances
146    }
147}