reactive_graph_graphql_schema/mutation/instances/
relation_instance.rs1use std::sync::Arc;
2
3use async_graphql::*;
4use log::debug;
5use serde_json::json;
6use uuid::Uuid;
7
8use reactive_graph_behaviour_model_api::BehaviourTypeId;
9use reactive_graph_behaviour_service_api::RelationBehaviourManager;
10use reactive_graph_behaviour_service_api::RelationComponentBehaviourManager;
11use reactive_graph_graph::PropertyInstanceGetter;
12use reactive_graph_graph::PropertyInstanceSetter;
13use reactive_graph_graph::PropertyType;
14use reactive_graph_graph::RelationInstance;
15use reactive_graph_graph::RelationInstanceId;
16use reactive_graph_graph::RelationInstanceTypeId;
17use reactive_graph_reactive_model_api::ReactivePropertyContainer;
18use reactive_graph_reactive_service_api::ReactiveEntityManager;
19use reactive_graph_reactive_service_api::ReactiveRelationManager;
20use reactive_graph_type_system_api::RelationTypeManager;
21
22use crate::mutation::BehaviourTypeIdDefinition;
23use crate::mutation::ComponentTypeIdDefinition;
24use crate::mutation::GraphQLRelationInstanceId;
25use crate::mutation::RelationTypeIdDefinition;
26use crate::query::GraphQLPropertyInstance;
27use crate::query::GraphQLRelationInstance;
28
29#[derive(Default)]
30pub struct MutationRelationInstances;
31
32#[Object]
34impl MutationRelationInstances {
35 async fn create(
51 &self,
52 context: &Context<'_>,
53 #[graphql(desc = "Specifies the outbound id, the inbound id, the relation type and the instance_id.")] relation_instance_id: GraphQLRelationInstanceId,
54 #[graphql(desc = "Description of the entity instance.")] description: Option<String>,
55 #[graphql(desc = "Creates the relation instance with the given components.")] components: Option<Vec<ComponentTypeIdDefinition>>,
56 properties: Option<Vec<GraphQLPropertyInstance>>,
57 ) -> Result<GraphQLRelationInstance> {
58 let relation_type_manager = context.data::<Arc<dyn RelationTypeManager + Send + Sync>>()?;
59 let relation_instance_manager = context.data::<Arc<dyn ReactiveRelationManager + Send + Sync>>()?;
60 let entity_instance_manager = context.data::<Arc<dyn ReactiveEntityManager + Send + Sync>>()?;
61
62 let relation_instance_ty = relation_instance_id.ty();
63 let relation_ty = relation_instance_ty.relation_type_id();
64
65 let relation_type = relation_type_manager
66 .get(&relation_ty)
67 .ok_or_else(|| Error::new(format!("Relation type {} does not exist!", &relation_ty)))?;
68
69 if !entity_instance_manager.has(relation_instance_id.outbound_id) {
70 return Err(Error::new(format!("Outbound entity {} does not exist!", relation_instance_id.outbound_id)));
71 }
72
73 if !entity_instance_manager.has(relation_instance_id.inbound_id) {
74 return Err(Error::new(format!("Inbound entity {} does not exist!", relation_instance_id.inbound_id)));
75 }
76
77 let properties = GraphQLPropertyInstance::to_property_instances_with_defaults(properties, relation_type.properties);
78
79 let relation_instance = RelationInstance::builder()
80 .outbound_id(relation_instance_id.outbound_id)
81 .ty(relation_instance_ty)
82 .inbound_id(relation_instance_id.inbound_id)
83 .description(description.unwrap_or_default())
84 .properties(properties)
85 .build();
86
87 let id: RelationInstanceId = relation_instance_id.into();
88
89 let relation_instance = relation_instance_manager.create_reactive_instance(relation_instance);
90
91 match relation_instance {
92 Ok(relation_instance) => {
93 if let Some(components) = components {
94 for component in components {
95 let component = component.into();
96 let _ = relation_instance_manager.add_component(&id, &component);
98 }
99 }
100 Ok(relation_instance.into())
101 }
102 Err(e) => Err(e.into()),
103 }
104 }
105
106 #[allow(clippy::too_many_arguments)]
110 async fn create_connector(
111 &self,
112 context: &Context<'_>,
113 #[graphql(desc = "The id of the outbound entity instance")] outbound_id: Uuid,
114 #[graphql(desc = "The name of the property of the outbound entity instance")] outbound_property_name: String,
115 #[graphql(name = "type", desc = "The name of the connector relation type")] relation_ty: RelationTypeIdDefinition,
116 #[graphql(desc = "The id of the inbound entity instance")] inbound_id: Uuid,
117 #[graphql(desc = "The name of the property of the inbound entity instance")] inbound_property_name: String,
118 #[graphql(desc = "Creates the relation instance with the given components.")] components: Option<Vec<ComponentTypeIdDefinition>>,
119 #[graphql(desc = "The initial property values")] properties: Option<Vec<GraphQLPropertyInstance>>,
120 ) -> Result<GraphQLRelationInstance> {
121 let relation_type_manager = context.data::<Arc<dyn RelationTypeManager + Send + Sync>>()?;
122 let relation_instance_manager = context.data::<Arc<dyn ReactiveRelationManager + Send + Sync>>()?;
123 let entity_instance_manager = context.data::<Arc<dyn ReactiveEntityManager + Send + Sync>>()?;
124
125 let relation_ty = relation_ty.into();
126
127 let relation_type = relation_type_manager
129 .get(&relation_ty)
130 .ok_or(Error::new(format!("Connector relation type {} does not exist!", &relation_ty)))?;
131
132 if !entity_instance_manager.has(outbound_id) {
134 return Err(Error::new(format!("Outbound entity {outbound_id} does not exist!")));
135 }
136
137 if !entity_instance_manager.has(inbound_id) {
139 return Err(Error::new(format!("Inbound entity {inbound_id} does not exist!")));
140 }
141
142 if entity_instance_manager.get(outbound_id).map(|e| e.get(&outbound_property_name)).is_none() {
144 return Err(Error::new(format!("Outbound entity {outbound_id} has no property named {outbound_property_name}!")));
145 }
146
147 if entity_instance_manager.get(inbound_id).map(|e| e.get(&inbound_property_name)).is_none() {
149 return Err(Error::new(format!("Inbound entity {inbound_id} has no property named {inbound_property_name}!")));
150 }
151
152 let instance_id = format!("{outbound_property_name}__{inbound_property_name}");
157 let ty = RelationInstanceTypeId::new_unique_for_instance_id(relation_ty, instance_id);
158
159 let id = RelationInstanceId::new(outbound_id, ty, inbound_id);
162
163 let properties = GraphQLPropertyInstance::to_property_instances_with_defaults(properties, relation_type.properties);
164 properties.insert("outbound_property_name".to_string(), json!(outbound_property_name));
165 properties.insert("inbound_property_name".to_string(), json!(inbound_property_name));
166 match relation_instance_manager.create_reactive_relation(&id, properties) {
167 Ok(relation_instance) => {
168 if let Some(components) = components {
170 for component in components {
171 let _ = relation_instance_manager.add_component(&id, &component.into());
173 }
174 }
175 Ok(relation_instance.into())
176 }
177 Err(e) => Err(Error::new(format!("Failed to create relation instance: {e:?}"))),
178 }
179 }
180
181 #[allow(clippy::too_many_arguments)]
183 async fn update(
184 &self,
185 context: &Context<'_>,
186 relation_instance_id: GraphQLRelationInstanceId,
187 #[graphql(desc = "Adds the components with the given name")] add_components: Option<Vec<ComponentTypeIdDefinition>>,
188 #[graphql(desc = "Removes the components with the given name")] remove_components: Option<Vec<ComponentTypeIdDefinition>>,
189 #[graphql(desc = "Updates the given properties")] properties: Option<Vec<GraphQLPropertyInstance>>,
190 #[graphql(desc = "Adds the given properties")] add_properties: Option<Vec<crate::mutation::PropertyTypeDefinition>>,
191 #[graphql(desc = "Removes the given properties")] remove_properties: Option<Vec<String>>,
192 ) -> Result<GraphQLRelationInstance> {
193 let relation_type_manager = context.data::<Arc<dyn RelationTypeManager + Send + Sync>>()?;
194 let relation_instance_manager = context.data::<Arc<dyn ReactiveRelationManager + Send + Sync>>()?;
195 let entity_instance_manager = context.data::<Arc<dyn ReactiveEntityManager + Send + Sync>>()?;
196
197 if !entity_instance_manager.has(relation_instance_id.outbound_id) {
198 return Err(Error::new(format!("Outbound entity {} does not exist!", relation_instance_id.outbound_id)));
199 }
200
201 if !entity_instance_manager.has(relation_instance_id.inbound_id) {
202 return Err(Error::new(format!("Inbound entity {} does not exist!", relation_instance_id.inbound_id)));
203 }
204
205 let ty = relation_instance_id.ty();
206 let relation_ty = ty.relation_type_id();
207
208 if relation_type_manager.get(&relation_ty).is_none() {
209 return Err(Error::new(format!("Relation type {relation_instance_id} does not exist!")));
210 }
211
212 let id: RelationInstanceId = relation_instance_id.into();
213 let relation_instance = relation_instance_manager
214 .get(&id)
215 .ok_or_else(|| Error::new(format!("Relation instance {id} does not exist!")))?;
216
217 if let Some(components) = add_components {
218 for component in components {
219 let _ = relation_instance_manager.add_component(&id, &component.into());
221 }
222 }
223 if let Some(components) = remove_components {
224 for component in components {
225 let _ = relation_instance_manager.remove_component(&id, &component.into());
227 }
228 }
229 if let Some(properties) = properties {
230 for property in properties.clone() {
232 debug!("set property {} = {}", property.name.clone(), property.value.clone());
233 relation_instance.set_no_propagate_checked(property.name.clone(), property.value.clone());
235 }
236 for property in properties {
238 debug!("tick property {} = {}", property.name.clone(), property.value.clone());
239 if let Some(property_instance) = relation_instance.properties.get(property.name.as_str()) {
240 property_instance.tick_checked();
242 }
243 }
244 }
245 if let Some(add_properties) = add_properties {
246 for property_type in add_properties.clone() {
247 let property_type: PropertyType = property_type.into();
248 debug!("add property {} ({})", &property_type.name, &property_type.data_type);
249 relation_instance.add_property_by_type(&property_type);
250 }
251 }
252 if let Some(remove_properties) = remove_properties {
253 for property_name in remove_properties.clone() {
254 debug!("remove property {}", &property_name);
255 relation_instance.remove_property(property_name);
256 }
257 }
258 Ok(relation_instance.into())
261 }
262
263 async fn tick(&self, context: &Context<'_>, relation_instance_id: GraphQLRelationInstanceId) -> Result<GraphQLRelationInstance> {
272 let relation_instance_manager = context.data::<Arc<dyn ReactiveRelationManager + Send + Sync>>()?;
273 let id = relation_instance_id.into();
274 let relation_instance = relation_instance_manager
275 .get(&id)
276 .ok_or_else(|| Error::new(format!("Relation instance {id} does not exist!")))?;
277 relation_instance.tick();
278 Ok(relation_instance.into())
279 }
280
281 async fn delete(&self, context: &Context<'_>, relation_instance_id: GraphQLRelationInstanceId) -> Result<bool> {
283 let relation_instance_manager = context.data::<Arc<dyn ReactiveRelationManager + Send + Sync>>()?;
285 let entity_instance_manager = context.data::<Arc<dyn ReactiveEntityManager + Send + Sync>>()?;
286
287 debug!("Deleting relation instance {relation_instance_id:?}",);
288
289 if !entity_instance_manager.has(relation_instance_id.outbound_id) {
290 return Err(Error::new(format!("Outbound entity {} does not exist!", relation_instance_id.outbound_id)));
291 }
292
293 if !entity_instance_manager.has(relation_instance_id.inbound_id) {
294 return Err(Error::new(format!("Inbound entity {} does not exist!", relation_instance_id.inbound_id)));
295 }
296
297 Ok(relation_instance_manager.delete(&relation_instance_id.into()))
298 }
299
300 async fn connect(
301 &self,
302 context: &Context<'_>,
303 relation_instance_id: GraphQLRelationInstanceId,
304 #[graphql(name = "type")] behaviour_ty: BehaviourTypeIdDefinition,
305 ) -> Result<GraphQLRelationInstance> {
306 let relation_instance_manager = context.data::<Arc<dyn ReactiveRelationManager + Send + Sync>>()?;
307 let relation_behaviour_manager = context.data::<Arc<dyn RelationBehaviourManager + Send + Sync>>()?;
308 let relation_component_behaviour_manager = context.data::<Arc<dyn RelationComponentBehaviourManager + Send + Sync>>()?;
309 let relation_instance_id = RelationInstanceId::from(relation_instance_id);
310 let reactive_instance = relation_instance_manager
311 .get(&relation_instance_id)
312 .ok_or(Error::new("Relation instance not found"))?;
313 let behaviour_ty = BehaviourTypeId::from(behaviour_ty);
314 if relation_behaviour_manager.has(reactive_instance.clone(), &behaviour_ty) {
315 relation_behaviour_manager
316 .connect(reactive_instance.clone(), &behaviour_ty)
317 .map_err(|e| Error::new(format!("Failed to connect relation behaviour {e:?}")))?;
318 }
319 if relation_component_behaviour_manager.has(reactive_instance.clone(), &behaviour_ty) {
320 relation_component_behaviour_manager
321 .connect(reactive_instance.clone(), &behaviour_ty)
322 .map_err(|e| Error::new(format!("Failed to connect relation component behaviour {e:?}")))?;
323 }
324 Ok(reactive_instance.into())
325 }
326
327 async fn disconnect(
328 &self,
329 context: &Context<'_>,
330 relation_instance_id: GraphQLRelationInstanceId,
331 #[graphql(name = "type")] behaviour_ty: BehaviourTypeIdDefinition,
332 ) -> Result<GraphQLRelationInstance> {
333 let relation_instance_manager = context.data::<Arc<dyn ReactiveRelationManager + Send + Sync>>()?;
334 let relation_behaviour_manager = context.data::<Arc<dyn RelationBehaviourManager + Send + Sync>>()?;
335 let relation_component_behaviour_manager = context.data::<Arc<dyn RelationComponentBehaviourManager + Send + Sync>>()?;
336 let relation_instance_id = RelationInstanceId::from(relation_instance_id);
337 let reactive_instance = relation_instance_manager
338 .get(&relation_instance_id)
339 .ok_or(Error::new("Relation instance not found"))?;
340 let behaviour_ty = BehaviourTypeId::from(behaviour_ty);
341 if relation_behaviour_manager.has(reactive_instance.clone(), &behaviour_ty) {
342 relation_behaviour_manager
343 .disconnect(reactive_instance.clone(), &behaviour_ty)
344 .map_err(|e| Error::new(format!("Failed to disconnect relation behaviour {e:?}")))?;
345 }
346 if relation_component_behaviour_manager.has(reactive_instance.clone(), &behaviour_ty) {
347 relation_component_behaviour_manager
348 .disconnect(reactive_instance.clone(), &behaviour_ty)
349 .map_err(|e| Error::new(format!("Failed to disconnect relation component behaviour {e:?}")))?;
350 }
351 Ok(reactive_instance.into())
352 }
353
354 async fn reconnect(
355 &self,
356 context: &Context<'_>,
357 relation_instance_id: GraphQLRelationInstanceId,
358 #[graphql(name = "type")] behaviour_ty: BehaviourTypeIdDefinition,
359 ) -> Result<GraphQLRelationInstance> {
360 let relation_instance_manager = context.data::<Arc<dyn ReactiveRelationManager + Send + Sync>>()?;
361 let relation_behaviour_manager = context.data::<Arc<dyn RelationBehaviourManager + Send + Sync>>()?;
362 let relation_component_behaviour_manager = context.data::<Arc<dyn RelationComponentBehaviourManager + Send + Sync>>()?;
363 let relation_instance_id = RelationInstanceId::from(relation_instance_id);
364 let reactive_instance = relation_instance_manager
365 .get(&relation_instance_id)
366 .ok_or(Error::new("Relation instance not found"))?;
367 let behaviour_ty = BehaviourTypeId::from(behaviour_ty);
368 if relation_behaviour_manager.has(reactive_instance.clone(), &behaviour_ty) {
369 relation_behaviour_manager
370 .reconnect(reactive_instance.clone(), &behaviour_ty)
371 .map_err(|e| Error::new(format!("Failed to reconnect relation behaviour {e:?}")))?;
372 }
373 if relation_component_behaviour_manager.has(reactive_instance.clone(), &behaviour_ty) {
374 relation_component_behaviour_manager
375 .reconnect(reactive_instance.clone(), &behaviour_ty)
376 .map_err(|e| Error::new(format!("Failed to reconnect relation component behaviour {e:?}")))?;
377 }
378 Ok(reactive_instance.into())
379 }
380}