reactive_graph_graphql_schema/mutation/instances/
entity_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::EntityBehaviourManager;
10use reactive_graph_behaviour_service_api::EntityComponentBehaviourManager;
11use reactive_graph_graph::EntityInstance;
12use reactive_graph_graph::PropertyInstanceSetter;
13use reactive_graph_graph::PropertyType;
14use reactive_graph_graph::PropertyTypeDefinition;
15use reactive_graph_reactive_model_api::ReactiveInstance;
16use reactive_graph_reactive_model_api::ReactivePropertyContainer;
17use reactive_graph_reactive_service_api::ReactiveEntityManager;
18use reactive_graph_reactive_service_api::ReactiveRelationManager;
19use reactive_graph_runtime_model::ActionProperties::TRIGGER;
20use reactive_graph_type_system_api::EntityTypeManager;
21
22use crate::mutation::BehaviourTypeIdDefinition;
23use crate::mutation::ComponentTypeIdDefinition;
24use crate::mutation::EntityTypeIdDefinition;
25use crate::query::GraphQLEntityInstance;
26use crate::query::GraphQLPropertyInstance;
27
28#[derive(Default)]
29pub struct MutationEntityInstances;
30
31#[Object]
33impl MutationEntityInstances {
34 async fn create(
45 &self,
46 context: &Context<'_>,
47 #[graphql(name = "type", desc = "The entity type")] entity_ty: EntityTypeIdDefinition,
48 #[graphql(desc = "The id of the entity instance. If none is given a random uuid will be generated.")] id: Option<Uuid>,
49 #[graphql(desc = "Description of the entity instance.")] description: Option<String>,
50 #[graphql(desc = "Creates the entity instance with the given components.")] components: Option<Vec<ComponentTypeIdDefinition>>,
51 properties: Option<Vec<GraphQLPropertyInstance>>,
52 ) -> Result<GraphQLEntityInstance> {
53 let reactive_entity_manager = context.data::<Arc<dyn ReactiveEntityManager + Send + Sync>>()?;
54 let entity_type_manager = context.data::<Arc<dyn EntityTypeManager + Send + Sync>>()?;
55
56 let entity_ty = entity_ty.into();
57 let entity_type = entity_type_manager
58 .get(&entity_ty)
59 .ok_or(Error::new(format!("Entity type {entity_ty} does not exist")))?;
60
61 let properties = GraphQLPropertyInstance::to_property_instances_with_defaults(properties, entity_type.properties);
62
63 let entity_instance = EntityInstance::builder()
64 .ty(&entity_ty)
65 .id(id.unwrap_or(Uuid::new_v4()))
66 .description(description.unwrap_or_default())
67 .properties(properties)
68 .build();
69 let entity_instance = reactive_entity_manager.create_reactive_instance(entity_instance);
70 match entity_instance {
71 Ok(entity_instance) => {
72 if let Some(components) = components {
73 for component in components {
74 let component_ty = component.into();
77 let _ = reactive_entity_manager.add_component(entity_instance.id, &component_ty);
78 }
79 }
80 Ok(entity_instance.into())
81 }
82 Err(e) => Err(e.into()),
83 }
84 }
85
86 #[allow(clippy::too_many_arguments)]
88 async fn update(
89 &self,
90 context: &Context<'_>,
91 #[graphql(desc = "Updates the entity instance with the given id.")] id: Option<Uuid>,
92 #[graphql(desc = "Updates the entity instance with the given label.")] label: Option<String>,
93 #[graphql(desc = "Adds the given components.")] add_components: Option<Vec<ComponentTypeIdDefinition>>,
94 #[graphql(desc = "Removes the given components.")] remove_components: Option<Vec<ComponentTypeIdDefinition>>,
95 #[graphql(desc = "Updates the given properties")] properties: Option<Vec<GraphQLPropertyInstance>>,
96 #[graphql(desc = "Adds the given properties")] add_properties: Option<Vec<crate::mutation::PropertyTypeDefinition>>,
97 #[graphql(desc = "Removes the given properties")] remove_properties: Option<Vec<String>>,
98 ) -> Result<GraphQLEntityInstance> {
99 let reactive_entity_manager = context.data::<Arc<dyn ReactiveEntityManager + Send + Sync>>()?;
100 let entity_instance;
101 if let Some(id) = id {
102 entity_instance = reactive_entity_manager.get(id);
103 } else if let Some(label) = label {
104 entity_instance = reactive_entity_manager.get_by_label(label.as_str());
105 } else {
106 return Err("Either id or label must be given!".into());
107 }
108 if entity_instance.is_none() {
109 return Err("Entity instance not found!".into());
110 }
111 let entity_instance = entity_instance.unwrap();
112
113 if let Some(components) = add_components {
114 for component in components {
115 let component = component.into();
117 let _ = reactive_entity_manager.add_component(entity_instance.id, &component);
118 }
119 }
120 if let Some(components) = remove_components {
121 for component in components {
122 let component = component.into();
123 reactive_entity_manager.remove_component(entity_instance.id, &component);
124 }
125 }
126 if let Some(properties) = properties {
127 for property in properties.clone() {
129 debug!("set property {} = {}", property.name.clone(), property.value.clone());
130 entity_instance.set_no_propagate_checked(property.name.clone(), property.value.clone());
132 }
133 for property in properties {
135 debug!("tick property {} = {}", property.name.clone(), property.value.clone());
136 if let Some(property_instance) = entity_instance.properties.get(property.name.as_str()) {
137 property_instance.tick_checked();
139 }
140 }
141 }
142 if let Some(add_properties) = add_properties {
143 for property_type in add_properties.clone() {
144 let property_type: PropertyType = property_type.into();
145 debug!("add property {} ({})", &property_type.name, &property_type.data_type);
146 entity_instance.add_property_by_type(&property_type);
147 }
148 }
149 if let Some(remove_properties) = remove_properties {
150 for property_name in remove_properties.clone() {
151 debug!("remove property {}", &property_name);
152 entity_instance.remove_property(property_name);
153 }
154 }
155 Ok(entity_instance.into())
158 }
159
160 async fn trigger(
162 &self,
163 context: &Context<'_>,
164 #[graphql(desc = "Triggers the entity instance with the given id.")] id: Option<Uuid>,
165 #[graphql(desc = "Triggers the entity instance with the given label.")] label: Option<String>,
166 ) -> Result<GraphQLEntityInstance> {
167 let reactive_entity_manager = context.data::<Arc<dyn ReactiveEntityManager + Send + Sync>>()?;
168 let Some(entity_instance) = (if let Some(id) = id {
169 reactive_entity_manager.get(id)
170 } else if let Some(label) = label {
171 reactive_entity_manager.get_by_label(label.as_str())
172 } else {
173 return Err("Either id or label must be given!".into());
174 }) else {
175 return Err("Entity instance not found!".into());
176 };
177 if entity_instance.has_property(&TRIGGER.property_name()) {
178 entity_instance.set_checked(TRIGGER.property_name(), json!(true));
179 Ok(entity_instance.into())
180 } else {
181 Err(Error::new(format!("Unable to trigger {}", entity_instance.id)))
182 }
183 }
184
185 async fn tick(&self, context: &Context<'_>, id: Uuid) -> Result<GraphQLEntityInstance> {
194 let reactive_entity_manager = context.data::<Arc<dyn ReactiveEntityManager + Send + Sync>>()?;
195 let entity_instance = reactive_entity_manager.get(id);
196 if entity_instance.is_none() {
197 return Err(Error::new(format!("Entity instance {id} does not exist!")));
198 }
199 let entity_instance = entity_instance.unwrap();
200 entity_instance.tick();
201 Ok(entity_instance.into())
202 }
203
204 async fn delete(
206 &self,
207 context: &Context<'_>,
208 #[graphql(desc = "The id of the entity instance")] id: Uuid,
209 #[graphql(desc = "If true, all relations to and from the entity instance will be deleted as well")] delete_relations: Option<bool>,
210 ) -> Result<bool> {
211 let reactive_entity_manager = context.data::<Arc<dyn ReactiveEntityManager + Send + Sync>>()?;
212 if delete_relations.is_some() && delete_relations.unwrap() {
213 let relation_instance_manager = context.data::<Arc<dyn ReactiveRelationManager + Send + Sync>>()?;
214 relation_instance_manager.get_by_inbound_entity(id).iter().for_each(|reactive_relation| {
215 let id = reactive_relation.id();
216 relation_instance_manager.delete(&id);
217 });
218 relation_instance_manager.get_by_outbound_entity(id).iter().for_each(|reactive_relation| {
219 let id = reactive_relation.id();
220 relation_instance_manager.delete(&id);
221 });
222 }
223 Ok(reactive_entity_manager.delete(id))
224 }
225
226 async fn connect(
227 &self,
228 context: &Context<'_>,
229 id: Uuid,
230 #[graphql(name = "type")] behaviour_ty: BehaviourTypeIdDefinition,
231 ) -> Result<GraphQLEntityInstance> {
232 let reactive_entity_manager = context.data::<Arc<dyn ReactiveEntityManager + Send + Sync>>()?;
233 let entity_behaviour_manager = context.data::<Arc<dyn EntityBehaviourManager + Send + Sync>>()?;
234 let entity_component_behaviour_manager = context.data::<Arc<dyn EntityComponentBehaviourManager + Send + Sync>>()?;
235 let reactive_instance = reactive_entity_manager.get(id).ok_or(Error::new("Entity instance not found"))?;
236 let behaviour_ty = BehaviourTypeId::from(behaviour_ty);
237 if entity_behaviour_manager.has(reactive_instance.clone(), &behaviour_ty) {
238 entity_behaviour_manager
239 .connect(reactive_instance.clone(), &behaviour_ty)
240 .map_err(|e| Error::new(format!("Failed to connect entity behaviour {e:?}")))?;
241 }
242 if entity_component_behaviour_manager.has(reactive_instance.clone(), &behaviour_ty) {
243 entity_component_behaviour_manager
244 .connect(reactive_instance.clone(), &behaviour_ty)
245 .map_err(|e| Error::new(format!("Failed to connect entity component behaviour {e:?}")))?;
246 }
247 Ok(reactive_instance.into())
248 }
249
250 async fn disconnect(
251 &self,
252 context: &Context<'_>,
253 id: Uuid,
254 #[graphql(name = "type")] behaviour_ty: BehaviourTypeIdDefinition,
255 ) -> Result<GraphQLEntityInstance> {
256 let reactive_entity_manager = context.data::<Arc<dyn ReactiveEntityManager + Send + Sync>>()?;
257 let entity_behaviour_manager = context.data::<Arc<dyn EntityBehaviourManager + Send + Sync>>()?;
258 let entity_component_behaviour_manager = context.data::<Arc<dyn EntityComponentBehaviourManager + Send + Sync>>()?;
259 let reactive_instance = reactive_entity_manager.get(id).ok_or(Error::new("Entity instance not found"))?;
260 let behaviour_ty = BehaviourTypeId::from(behaviour_ty);
261 if entity_behaviour_manager.has(reactive_instance.clone(), &behaviour_ty) {
262 entity_behaviour_manager
263 .disconnect(reactive_instance.clone(), &behaviour_ty)
264 .map_err(|e| Error::new(format!("Failed to disconnect entity behaviour {e:?}")))?;
265 }
266 if entity_component_behaviour_manager.has(reactive_instance.clone(), &behaviour_ty) {
267 entity_component_behaviour_manager
268 .disconnect(reactive_instance.clone(), &behaviour_ty)
269 .map_err(|e| Error::new(format!("Failed to connect entity component behaviour {e:?}")))?;
270 }
271 Ok(reactive_instance.into())
272 }
273
274 async fn reconnect(
275 &self,
276 context: &Context<'_>,
277 id: Uuid,
278 #[graphql(name = "type")] behaviour_ty: BehaviourTypeIdDefinition,
279 ) -> Result<GraphQLEntityInstance> {
280 let reactive_entity_manager = context.data::<Arc<dyn ReactiveEntityManager + Send + Sync>>()?;
281 let entity_behaviour_manager = context.data::<Arc<dyn EntityBehaviourManager + Send + Sync>>()?;
282 let entity_component_behaviour_manager = context.data::<Arc<dyn EntityComponentBehaviourManager + Send + Sync>>()?;
283 let reactive_instance = reactive_entity_manager.get(id).ok_or(Error::new("Entity instance not found"))?;
284 let behaviour_ty = BehaviourTypeId::from(behaviour_ty);
285 if entity_behaviour_manager.has(reactive_instance.clone(), &behaviour_ty) {
286 entity_behaviour_manager
287 .reconnect(reactive_instance.clone(), &behaviour_ty)
288 .map_err(|e| Error::new(format!("Failed to reconnect entity behaviour {e:?}")))?;
289 }
290 if entity_component_behaviour_manager.has(reactive_instance.clone(), &behaviour_ty) {
291 entity_component_behaviour_manager
292 .reconnect(reactive_instance.clone(), &behaviour_ty)
293 .map_err(|e| Error::new(format!("Failed to connect entity component behaviour {e:?}")))?;
294 }
295 Ok(reactive_instance.into())
296 }
297}