reactive_graph_dynamic_graph_impl/field/
relation.rs1use std::str::FromStr;
2use std::sync::Arc;
3
4use async_graphql::Error;
5use async_graphql::ID;
6use async_graphql::dynamic::*;
7use reactive_graph_reactive_service_api::ReactiveRelationRegistrationError;
8use serde_json::Value;
9use uuid::Uuid;
10
11use crate::field::create_properties_from_field_arguments;
12use crate::field::to_field_value;
13use crate::field::to_input_type_ref;
14use crate::field::to_type_ref;
15use crate::interface::relation::INTERFACE_RELATION_FIELD_INSTANCE_ID;
16use crate::interface::relation::INTERFACE_RELATION_FIELD_KEY;
17use crate::object::types::DynamicGraphTypeDefinition;
18use crate::union::entity::UNION_ALL_ENTITIES;
19use crate::union::entity::namespace_entities_union_type_name;
20use reactive_graph_dynamic_graph_api::SchemaBuilderContext;
21use reactive_graph_graph::ComponentOrEntityTypeId;
22use reactive_graph_graph::ComponentTypeId;
23use reactive_graph_graph::DataType;
24use reactive_graph_graph::EntityTypeId;
25use reactive_graph_graph::NamespacedTypeGetter;
26use reactive_graph_graph::PropertyInstanceGetter;
27use reactive_graph_graph::PropertyType;
28use reactive_graph_graph::PropertyTypeDefinition;
29use reactive_graph_graph::RelationInstanceId;
30use reactive_graph_graph::RelationInstanceTypeId;
31use reactive_graph_graph::RelationType;
32use reactive_graph_reactive_model_impl::ReactiveProperties;
33use reactive_graph_reactive_model_impl::ReactiveRelation;
34use reactive_graph_reactive_service_api::ReactiveEntityManager;
35use reactive_graph_reactive_service_api::ReactiveRelationCreationError;
36use reactive_graph_reactive_service_api::ReactiveRelationManager;
37use reactive_graph_runtime_model::LabeledProperties::LABEL;
38
39pub fn relation_query_field(relation_type: &RelationType) -> Field {
40 let ty = relation_type.ty.clone();
41 let relation_type_inner = relation_type.clone();
42 let dy_ty = DynamicGraphTypeDefinition::from(&ty);
43 let mut field = Field::new(dy_ty.field_name(), TypeRef::named_nn_list_nn(dy_ty.to_string()), move |ctx| {
44 let ty = ty.clone();
45 let relation_type = relation_type_inner.clone();
46 FieldFuture::new(async move {
47 let relation_instance_manager = ctx.data::<Arc<dyn ReactiveRelationManager + Send + Sync>>()?;
48 let mut instances = relation_instance_manager.get_by_type(&ty);
49 for property in relation_type.properties.iter() {
50 let Some(expected_value) = ctx.args.get(&property.name) else {
51 continue;
52 };
53 instances.retain(|instance| match instance.get(&property.name) {
54 Some(actual_value) => match &property.data_type {
55 DataType::Null => false,
56 DataType::Bool => expected_value
57 .boolean()
58 .map(|expected_value| actual_value.as_bool().map(|actual_value| expected_value == actual_value).unwrap_or(false))
59 .unwrap_or(false),
60 DataType::Number => {
61 if let Ok(expected_value) = expected_value.i64() {
62 actual_value.as_i64().map(|actual_value| expected_value == actual_value).unwrap_or(false)
63 } else if let Ok(expected_value) = expected_value.u64() {
64 actual_value.as_u64().map(|actual_value| expected_value == actual_value).unwrap_or(false)
65 } else if let Ok(expected_value) = expected_value.f64() {
66 actual_value.as_f64().map(|actual_value| expected_value == actual_value).unwrap_or(false)
67 } else {
68 false
69 }
70 }
71 DataType::String => expected_value
72 .string()
73 .map(|expected_value| actual_value.as_str().map(|actual_value| expected_value == actual_value).unwrap_or(false))
74 .unwrap_or(false),
75 DataType::Array => {
76 if let Ok(_l) = expected_value.list() {
77 if let Ok(expected_value) = expected_value.deserialize::<Value>() {
78 if expected_value.is_array() && actual_value.is_array() {
79 expected_value == actual_value
80 } else {
81 false
82 }
83 } else {
84 false
85 }
86 } else {
87 false
88 }
89 }
90 DataType::Object => {
91 if let Ok(_o) = expected_value.object() {
92 if let Ok(expected_value) = expected_value.deserialize::<Value>() {
93 if expected_value.is_object() && actual_value.is_object() {
94 expected_value == actual_value
95 } else {
96 false
97 }
98 } else {
99 false
100 }
101 } else {
102 false
103 }
104 }
105 DataType::Any => match expected_value.deserialize::<Value>() {
106 Ok(expected_value) => expected_value == actual_value,
107 Err(_) => false,
108 },
109 },
110 None => false,
111 });
112 }
113 Ok(Some(FieldValue::list(instances.into_iter().map(FieldValue::owned_any))))
114 })
115 })
116 .description(relation_type.description.clone());
117 for property in relation_type.properties.iter() {
118 if property.name == LABEL.property_name() {
119 continue;
120 }
121 if let Some(type_ref) = to_input_type_ref(property.value(), true) {
122 field = field.argument(InputValue::new(&property.name, type_ref));
123 }
124 }
125 field
126}
127
128pub fn relation_creation_field(relation_type: &RelationType) -> Option<Field> {
129 let ty = relation_type.ty.clone();
130 let relation_type_inner = relation_type.clone();
131 let dy_ty = DynamicGraphTypeDefinition::from(&ty);
132 let mut field = Field::new(dy_ty.mutation_field_name("create"), TypeRef::named_nn(dy_ty.to_string()), move |ctx| {
133 let ty = ty.clone();
134 let relation_type = relation_type_inner.clone();
135 FieldFuture::new(async move {
136 let entity_instance_manager = ctx.data::<Arc<dyn ReactiveEntityManager + Send + Sync>>()?;
137 let relation_instance_manager = ctx.data::<Arc<dyn ReactiveRelationManager + Send + Sync>>()?;
138
139 let outbound_id = Uuid::from_str(ctx.args.try_get("outboundId")?.string()?)?;
140 let inbound_id = Uuid::from_str(ctx.args.try_get("inboundId")?.string()?)?;
141 let rty = match ctx.args.get("instanceId").and_then(|s| s.string().map(|s| s.to_string()).ok()) {
142 Some(instance_id) => RelationInstanceTypeId::new_unique_for_instance_id(ty, instance_id),
143 None => RelationInstanceTypeId::new_with_random_instance_id(ty),
144 };
145 let id = RelationInstanceId::builder().outbound_id(outbound_id).ty(&rty).inbound_id(inbound_id).build();
146
147 if relation_instance_manager.has(&id) {
148 return Err(ReactiveRelationRegistrationError::RelationInstanceAlreadyExists(id.clone()).into());
149 }
150
151 let outbound = entity_instance_manager
152 .get(outbound_id)
153 .ok_or::<Error>(ReactiveRelationCreationError::MissingOutboundEntityInstance(outbound_id).into())?;
154
155 let inbound = entity_instance_manager
156 .get(inbound_id)
157 .ok_or::<Error>(ReactiveRelationCreationError::MissingInboundEntityInstance(inbound_id).into())?;
158
159 let properties = create_properties_from_field_arguments(&ctx, &relation_type.properties)?;
160 let properties = ReactiveProperties::new_with_id_from_properties(id, properties);
161 let reactive_relation = ReactiveRelation::builder()
162 .outbound(outbound)
163 .ty(&rty)
164 .inbound(inbound)
165 .properties(properties)
166 .build();
167 if let Ok(reactive_relation) = relation_instance_manager.register_reactive_instance(reactive_relation) {
168 return Ok(Some(FieldValue::owned_any(reactive_relation)));
169 }
170 Ok(None)
171 })
172 })
173 .argument(InputValue::new("outboundId", TypeRef::named(TypeRef::ID)))
174 .argument(InputValue::new("instanceId", TypeRef::named(TypeRef::ID)))
175 .argument(InputValue::new("inboundId", TypeRef::named(TypeRef::ID)));
176 for property in relation_type.properties.iter() {
177 if let Some(type_ref) = to_input_type_ref(property.value(), false) {
178 field = field.argument(InputValue::new(&property.name, type_ref));
179 }
180 }
181 Some(field)
182}
183
184pub fn relation_mutation_field(relation_type: &RelationType) -> Option<Field> {
185 let ty = relation_type.ty.clone();
186 let dy_ty = DynamicGraphTypeDefinition::from(&ty);
187 let field = Field::new(dy_ty.field_name(), TypeRef::named_nn(dy_ty.mutation_type_name()), move |ctx| {
188 let ty = ty.clone();
189 FieldFuture::new(async move {
190 let relation_instance_manager = ctx.data::<Arc<dyn ReactiveRelationManager + Send + Sync>>()?;
191 let relation_instances: Vec<ReactiveRelation> = relation_instance_manager
192 .get_by_type(&ty)
193 .into_iter()
194 .filter(|relation_instance| {
195 let Ok(id) = ctx.args.try_get("outboundId").and_then(|id| id.string().map(|s| s.to_string())) else {
196 return true;
197 };
198 let Ok(id) = Uuid::from_str(&id) else {
199 return true;
200 };
201 relation_instance.outbound.id == id
202 })
203 .filter(|relation_instance| {
204 let Ok(id) = ctx.args.try_get("inboundId").and_then(|id| id.string().map(|s| s.to_string())) else {
205 return true;
206 };
207 let Ok(id) = Uuid::from_str(&id) else {
208 return true;
209 };
210 relation_instance.outbound.id == id
211 })
212 .collect();
217 let field_value = FieldValue::owned_any(relation_instances);
218 Ok(Some(field_value))
219 })
220 })
221 .argument(InputValue::new("outboundId", TypeRef::named(TypeRef::ID)))
223 .argument(InputValue::new("inboundId", TypeRef::named(TypeRef::ID)))
224 .argument(InputValue::new("label", TypeRef::named(TypeRef::STRING)))
226 .description(relation_type.description.clone());
230 Some(field)
231}
232
233pub fn relation_key_field() -> Field {
234 Field::new(INTERFACE_RELATION_FIELD_KEY, TypeRef::named_nn(TypeRef::ID), |ctx| {
235 FieldFuture::new(async move {
236 let relation_instance = ctx.parent_value.try_downcast_ref::<ReactiveRelation>()?;
237 Ok(Some(FieldValue::value(ID(format!("{}", relation_instance.ty)))))
238 })
239 })
240}
241
242pub fn relation_instance_id_field() -> Field {
243 Field::new(INTERFACE_RELATION_FIELD_INSTANCE_ID, TypeRef::named_nn(TypeRef::ID), |ctx| {
244 FieldFuture::new(async move {
245 let relation_instance = ctx.parent_value.try_downcast_ref::<ReactiveRelation>()?;
246 Ok(Some(FieldValue::value(ID(relation_instance.instance_id()))))
247 })
248 })
249}
250
251pub fn relation_property_field(property_type: &PropertyType) -> Field {
252 let property_type_inner = property_type.clone();
253 Field::new(&property_type.name, to_type_ref(&property_type.data_type), move |ctx| {
254 let property_type = property_type_inner.clone();
255 FieldFuture::new(async move {
256 let relation_instance = ctx.parent_value.try_downcast_ref::<ReactiveRelation>()?;
257 Ok(relation_instance.get(&property_type.name).and_then(to_field_value))
258 })
259 })
260 .description(&property_type.description)
261}
262
263pub fn relation_outbound_field(
264 ty: &ComponentOrEntityTypeId,
265 field_name: Option<String>,
266 field_description: Option<String>,
267 context: &SchemaBuilderContext,
268) -> Vec<Field> {
269 match ty {
270 ComponentOrEntityTypeId::EntityType(ty) => {
271 if ty.namespace() == "*" {
272 vec![relation_outbound_entity_union_field(UNION_ALL_ENTITIES, field_name, field_description)]
273 } else if ty.type_name() == "*" {
274 vec![relation_outbound_entity_union_field(
275 &namespace_entities_union_type_name(&ty.namespace()),
276 field_name,
277 field_description,
278 )]
279 } else {
280 vec![relation_outbound_entity_field(ty, field_name, field_description)]
281 }
282 }
283 ComponentOrEntityTypeId::Component(ty) => {
284 if ty.namespace() == "*" {
285 context
286 .component_manager
287 .get_type_ids()
288 .iter()
289 .map(|ty| relation_outbound_component_field(ty.key(), None, None))
290 .collect()
291 } else if ty.type_name() == "*" {
297 context
298 .component_manager
299 .get_types_by_namespace(&ty.namespace())
300 .iter()
301 .map(|ty| relation_outbound_component_field(ty.key(), None, None))
302 .collect()
303 } else {
309 vec![relation_outbound_component_field(ty, field_name, field_description)]
310 }
311 }
312 }
313}
314
315pub fn relation_inbound_field(
316 ty: &ComponentOrEntityTypeId,
317 field_name: Option<String>,
318 field_description: Option<String>,
319 context: &SchemaBuilderContext,
320) -> Vec<Field> {
321 match ty {
322 ComponentOrEntityTypeId::EntityType(ty) => {
323 if ty.namespace() == "*" {
324 vec![relation_inbound_entity_union_field(UNION_ALL_ENTITIES, field_name, field_description)]
325 } else if ty.type_name() == "*" {
326 vec![relation_inbound_entity_union_field(
327 &namespace_entities_union_type_name(&ty.namespace()),
328 field_name,
329 field_description,
330 )]
331 } else {
332 vec![relation_inbound_entity_field(ty, field_name, field_description)]
333 }
334 }
335 ComponentOrEntityTypeId::Component(ty) => {
336 if ty.namespace() == "*" {
337 context
338 .component_manager
339 .get_type_ids()
340 .iter()
341 .map(|ty| relation_inbound_component_field(ty.key(), None, None))
342 .collect()
343 } else if ty.type_name() == "*" {
348 context
349 .component_manager
350 .get_types_by_namespace(&ty.namespace())
351 .iter()
352 .map(|ty| relation_inbound_component_field(ty.key(), None, None))
353 .collect()
354 } else {
359 vec![relation_inbound_component_field(ty, field_name, field_description)]
360 }
361 }
362 }
363}
364
365pub fn relation_outbound_entity_field(ty: &EntityTypeId, field_name: Option<String>, field_description: Option<String>) -> Field {
366 let dy_ty = DynamicGraphTypeDefinition::from(ty);
367 let field_name = field_name.unwrap_or(dy_ty.outbound_type_name());
368 create_relation_outbound_field(&dy_ty.to_string(), &field_name, field_description)
369}
370
371pub fn relation_outbound_entity_union_field(type_name: &str, field_name: Option<String>, field_description: Option<String>) -> Field {
372 let field_name = field_name.unwrap_or("outbound".to_string());
373 let mut field = Field::new(field_name, TypeRef::named_nn(type_name), move |ctx| {
374 FieldFuture::new(async move {
375 let relation_instance = ctx.parent_value.try_downcast_ref::<ReactiveRelation>()?;
376 let dy_ty = DynamicGraphTypeDefinition::from(&relation_instance.outbound.ty);
377 Ok(Some(FieldValue::owned_any(relation_instance.outbound.clone()).with_type(dy_ty.to_string())))
378 })
379 });
380 if let Some(field_description) = field_description {
381 field = field.description(field_description);
382 }
383 field
384}
385
386pub fn relation_outbound_component_field(ty: &ComponentTypeId, field_name: Option<String>, field_description: Option<String>) -> Field {
387 let dy_ty = DynamicGraphTypeDefinition::from(ty);
388 let field_name = field_name.unwrap_or(dy_ty.outbound_type_name());
389 create_relation_outbound_field(&dy_ty.to_string(), &field_name, field_description)
390}
391
392pub fn create_relation_outbound_field(type_name: &str, field_name: &str, field_description: Option<String>) -> Field {
393 let mut field = Field::new(field_name, TypeRef::named_nn(type_name), move |ctx| {
394 FieldFuture::new(async move {
395 let relation_instance = ctx.parent_value.try_downcast_ref::<ReactiveRelation>()?;
396 Ok(Some(FieldValue::owned_any(relation_instance.outbound.clone())))
397 })
398 });
399 if let Some(field_description) = field_description {
400 field = field.description(field_description);
401 }
402 field
403}
404
405pub fn relation_inbound_entity_field(ty: &EntityTypeId, field_name: Option<String>, field_description: Option<String>) -> Field {
406 let dy_ty = DynamicGraphTypeDefinition::from(ty);
407 let field_name = field_name.unwrap_or(dy_ty.inbound_type_name());
408 create_relation_inbound_field(&dy_ty.to_string(), &field_name, field_description)
409}
410
411pub fn relation_inbound_entity_union_field(type_name: &str, field_name: Option<String>, field_description: Option<String>) -> Field {
412 let field_name = field_name.unwrap_or("inbound".to_string());
413 let mut field = Field::new(field_name, TypeRef::named_nn(type_name), move |ctx| {
414 FieldFuture::new(async move {
415 let relation_instance = ctx.parent_value.try_downcast_ref::<ReactiveRelation>()?;
416 let dy_ty = DynamicGraphTypeDefinition::from(&relation_instance.inbound.ty);
417 Ok(Some(FieldValue::owned_any(relation_instance.inbound.clone()).with_type(dy_ty.to_string())))
418 })
419 });
420 if let Some(field_description) = field_description {
421 field = field.description(field_description);
422 }
423 field
424}
425
426pub fn relation_inbound_component_field(ty: &ComponentTypeId, field_name: Option<String>, field_description: Option<String>) -> Field {
427 let dy_ty = DynamicGraphTypeDefinition::from(ty);
428 let field_name = field_name.unwrap_or(dy_ty.inbound_type_name());
429 create_relation_inbound_field(&dy_ty.to_string(), &field_name, field_description)
430}
431
432pub fn create_relation_inbound_field(type_name: &str, field_name: &str, field_description: Option<String>) -> Field {
433 let mut field = Field::new(field_name, TypeRef::named_nn(type_name), move |ctx| {
434 FieldFuture::new(async move {
435 let relation_instance = ctx.parent_value.try_downcast_ref::<ReactiveRelation>()?;
436 Ok(Some(FieldValue::owned_any(relation_instance.inbound.clone())))
437 })
438 });
439 if let Some(field_description) = field_description {
440 field = field.description(field_description);
441 }
442 field
443}