skipped 24 lines 25 25 use super::stages::data_connectors; 26 26 27 27 #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, derive_more::Display)] 28 - pub enum TypeRepresentation { 29 - Object(ObjectTypeRepresentation), 30 - #[display(fmt = "ScalarType")] 31 - ScalarType(ScalarTypeRepresentation), 32 - } 33 - 34 - #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, derive_more::Display)] 35 28 #[display(fmt = "Display")] 36 29 pub struct ScalarTypeRepresentation { 37 30 pub graphql_type_name: Option<ast::TypeName>, skipped 108 lines 146 139 Qualified<CustomTypeName>, 147 140 Option<Qualified<open_dds::models::ModelName>>, 148 141 >, 149 - ) -> Result<TypeRepresentation, Error> { 142 + ) -> Result<ObjectTypeRepresentation, Error> { 150 143 let mut resolved_fields = IndexMap::new(); 151 144 let mut resolved_global_id_fields = Vec::new(); 152 145 skipped 104 lines 257 250 store_new_graphql_type(existing_graphql_types, graphql_type_name.as_ref())?; 258 251 store_new_graphql_type(existing_graphql_types, graphql_input_type_name.as_ref())?; 259 252 260 - Ok(TypeRepresentation : : Object ( ObjectTypeRepresentation { 253 + Ok(ObjectTypeRepresentation { 261 254 fields: resolved_fields, 262 255 relationships: IndexMap::new(), 263 256 global_id_fields: resolved_global_id_fields, skipped 2 lines 266 259 graphql_input_type_name, 267 260 description: object_type_definition.description.clone(), 268 261 apollo_federation_config, 269 - })) 262 + }) 270 263 } 271 264 272 265 pub fn get_column<'a>( skipped 99 lines 372 365 Ok(resolved_type_mapping) 373 366 } 374 367 375 - // Get the underlying object type by resolving Custom ObjectType, Array and 376 - // Nullable container types 368 + #[derive(Debug)] 369 + /// we do not want to store our types like this, but occasionally it is useful 370 + /// for pattern matching 371 + pub enum TypeRepresentation<'a> { 372 + Scalar(&'a ScalarTypeRepresentation), 373 + Object(&'a ObjectTypeRepresentation), 374 + } 375 + 376 + /// validate whether a given CustomTypeName exists within `object_types` or `scalar_types` 377 + pub fn get_type_representation<'a>( 378 + custom_type_name: &Qualified<CustomTypeName>, 379 + object_types: &'a HashMap<Qualified<CustomTypeName>, ObjectTypeRepresentation>, 380 + scalar_types: &'a HashMap<Qualified<CustomTypeName>, ScalarTypeRepresentation>, 381 + ) -> Result<TypeRepresentation<'a>, Error> { 382 + match object_types.get(custom_type_name) { 383 + Some(object_type_representation) => { 384 + Ok(TypeRepresentation::Object(object_type_representation)) 385 + } 386 + None => match scalar_types.get(custom_type_name) { 387 + Some(scalar_type_representation) => { 388 + Ok(TypeRepresentation::Scalar(scalar_type_representation)) 389 + } 390 + None => Err(Error::UnknownType { 391 + data_type: custom_type_name.clone(), 392 + }), 393 + }, 394 + } 395 + } 396 + 397 + // check that `custom_type_name` exists in `object_types` 377 398 pub fn get_underlying_object_type( 378 - output_type: &QualifiedTypeReference, 379 - types : &HashMap<Qualified<CustomTypeName>, TypeRepresentation >, 380 - ) -> Result<Option < Qualified<CustomTypeName> >, Error> { 381 - get_underlying_object_type_or_unknown_type(output_type, types) 382 - .map(|opt| opt.cloned()) 383 - .map_err(|custom_type_name| Error::UnknownDataType { 399 + custom_type_name: &Qualified<CustomTypeName>, 400 + object_types : &HashMap<Qualified<CustomTypeName>, ObjectTypeRepresentation >, 401 + ) -> Result<Qualified<CustomTypeName>, Error> { 402 + object_types 403 + .get(custom_type_name) 404 + .map(|_| custom_type_name.clone()) 405 + .ok_or_else(|| Error::UnknownObjectType { 384 406 data_type: custom_type_name.clone(), 385 407 }) 386 408 } 387 409 388 - pub fn get_underlying_object_type_or_unknown_type<'a>( 389 - output_type: &'a QualifiedTypeReference, 390 - types : &' a HashMap < Qualified<CustomTypeName>, TypeRepresentation > , 391 - ) -> Result<Option<&'a Qualified<CustomTypeName>>, Qualified<CustomTypeName>> { 392 - match &output_type.underlying_type { 393 - QualifiedBaseType::List(output_type) => { 394 - get_underlying_object_type_or_unknown_type(output_type, types) 395 - } 410 + // check that `custom_type_name` exists in `scalar_types` 411 + pub fn get_underlying_scalar_type( 412 + custom_type_name : &Qualified<CustomTypeName>, 413 + scalar_types: &HashMap<Qualified<CustomTypeName>, ScalarTypeRepresentation>, 414 + ) -> Result<Qualified<CustomTypeName>, Error> { 415 + scalar_types 416 + .get(custom_type_name) 417 + .map(|_| custom_type_name.clone()) 418 + .ok_or_else(|| Error::UnknownScalarType { 419 + data_type: custom_type_name.clone(), 420 + }) 421 + } 422 + 423 + /// given a type like `Thing!` or `[Thing!]!` - try and extract `Thing` 424 + pub fn unwrap_custom_type_name( 425 + type_reference: &QualifiedTypeReference, 426 + ) -> Option<&Qualified<CustomTypeName>> { 427 + match &type_reference.underlying_type { 428 + QualifiedBaseType::List(inner_type) => unwrap_custom_type_name(inner_type), 396 429 QualifiedBaseType::Named(type_name) => match type_name { 397 - QualifiedTypeName::Inbuilt(_) => Ok ( None) , 398 - QualifiedTypeName::Custom(custom_type_name) => { 399 - let type_representation = types 400 - .get(custom_type_name) 401 - .ok_or_else(|| custom_type_name.clone())?; 402 - match type_representation { 403 - TypeRepresentation::ScalarType { .. } => Ok(None), 404 - TypeRepresentation::Object { .. } => Ok(Some(custom_type_name)), 405 - } 406 - } 430 + QualifiedTypeName::Inbuilt(_) => None, 431 + QualifiedTypeName::Custom(custom_type_name) => Some ( custom_type_name ) , 407 432 }, 408 433 } 409 434 } 410 435 411 436 pub fn resolve_output_type_permission( 412 - type_representation: &mut TypeRepresentation, 437 + object_type_representation: &mut ObjectTypeRepresentation, 413 438 type_permissions: &TypePermissionsV1, 414 439 ) -> Result<(), Error> { 415 - match type_representation { 416 - TypeRepresentation::ScalarType { .. } => Err(Error::UnsupportedTypeInOutputPermissions { 417 - type_name: type_permissions.type_name.clone(), 418 - }), 419 - TypeRepresentation::Object(object_type_representation) => { 420 - // validate all the fields definied in output permissions actually 421 - // exist in this type definition 422 - for type_permission in &type_permissions.permissions { 423 - if let Some(output) = &type_permission.output { 424 - for field_name in output.allowed_fields.iter() { 425 - if !object_type_representation.fields.contains_key(field_name) { 426 - return Err(Error::UnknownFieldInOutputPermissionsDefinition { 427 - field_name: field_name.clone(), 428 - type_name: type_permissions.type_name.clone(), 429 - }); 430 - } 431 - } 432 - if object_type_representation 433 - .type_permissions 434 - .insert(type_permission.role.clone(), output.clone()) 435 - .is_some() 436 - { 437 - return Err(Error::DuplicateOutputTypePermissions { 438 - type_name: type_permissions.type_name.clone(), 439 - }); 440 - } 440 + // validate all the fields definied in output permissions actually 441 + // exist in this type definition 442 + for type_permission in &type_permissions.permissions { 443 + if let Some(output) = &type_permission.output { 444 + for field_name in output.allowed_fields.iter() { 445 + if !object_type_representation.fields.contains_key(field_name) { 446 + return Err(Error::UnknownFieldInOutputPermissionsDefinition { 447 + field_name: field_name.clone(), 448 + type_name: type_permissions.type_name.clone(), 449 + }); 441 450 } 442 451 } 443 - Ok(()) 452 + if object_type_representation 453 + .type_permissions 454 + .insert(type_permission.role.clone(), output.clone()) 455 + .is_some() 456 + { 457 + return Err(Error::DuplicateOutputTypePermissions { 458 + type_name: type_permissions.type_name.clone(), 459 + }); 460 + } 444 461 } 445 462 } 463 + Ok(()) 446 464 } 447 465 448 466 /// Resolves a given object boolean expression type skipped 1 lines 450 468 object_boolean_expression: &ObjectBooleanExpressionTypeV1, 451 469 subgraph: &str, 452 470 data_connectors: &data_connectors::DataConnectors, 453 - types : &HashMap<Qualified<CustomTypeName>, TypeRepresentation >, 471 + object_types : &HashMap<Qualified<CustomTypeName>, ObjectTypeRepresentation >, 454 472 data_connector_type_mappings: &DataConnectorTypeMappings, 455 473 existing_graphql_types: &mut HashSet<ast::TypeName>, 456 474 ) -> Result<ObjectBooleanExpressionType, Error> { skipped 7 lines 464 482 subgraph.to_string(), 465 483 object_boolean_expression.object_type.to_owned(), 466 484 ); 467 - let type_representation = types.get(&qualified_object_type_name).ok_or_else(|| { 468 - Error::from( 469 - BooleanExpressionError::UnknownTypeInObjectBooleanExpressionType { 470 - type_name: qualified_object_type_name.clone(), 471 - }, 472 - ) 473 - })?; 474 - match type_representation { 475 - // validate it should only be an object type 476 - TypeRepresentation::ScalarType { .. } => Err(Error::from( 477 - BooleanExpressionError::UnsupportedTypeInObjectBooleanExpressionType { 478 - type_name: qualified_name.clone(), 479 - }, 480 - )), 481 - TypeRepresentation::Object(object_type_representation) => { 482 - let qualified_data_connector_name = Qualified::new( 483 - subgraph.to_string(), 484 - object_boolean_expression.data_connector_name.to_owned(), 485 - ); 485 + let object_type_representation = 486 + object_types 487 + .get(&qualified_object_type_name) 488 + .ok_or_else(|| { 489 + Error::from( 490 + BooleanExpressionError::UnknownTypeInObjectBooleanExpressionType { 491 + type_name: qualified_object_type_name.clone(), 492 + }, 493 + ) 494 + })?; 495 + let qualified_data_connector_name = Qualified::new( 496 + subgraph.to_string(), 497 + object_boolean_expression.data_connector_name.to_owned(), 498 + ); 486 499 487 - // validate data connector name488 - let data_connector_context = data_connectors489 - .data_connectors490 - .get(&qualified_data_connector_name)491 - .ok_or_else(|| {492 - Error::from( 493 - BooleanExpressionError::UnknownDataConnectorInObjectBooleanExpressionType { 494 - data_connector: qualified_data_connector_name.clone(), 495 - boolean_expression_type: qualified_name.clone(), 496 - }, 497 - ) 498 - })?; 500 + // validate data connector name 501 + let data_connector_context = data_connectors 502 + .data_connectors 503 + .get(&qualified_data_connector_name) 504 + .ok_or_else(|| { 505 + Error::from( 506 + BooleanExpressionError::UnknownDataConnectorInObjectBooleanExpressionType { 507 + data_connector: qualified_data_connector_name.clone(), 508 + boolean_expression_type: qualified_name.clone(), 509 + }, 510 + ) 511 + })?; 499 512 500 - // validate data connector object type501 - if !data_connector_context502 - .schema503 - .object_types504 - .contains_key(&object_boolean_expression.data_connector_object_type)505 - {506 - return Err(Error::from(507 - BooleanExpressionError::UnknownDataConnectorTypeInObjectBooleanExpressionType { 508 - data_connector: qualified_data_connector_name.clone(), 509 - boolean_expression_type: qualified_name.clone(), 510 - data_connector_object_type: object_boolean_expression 511 - .data_connector_object_type 512 - .clone(), 513 - }, 514 - ));515 - } 513 + // validate data connector object type 514 + if !data_connector_context 515 + .schema 516 + .object_types 517 + .contains_key(&object_boolean_expression.data_connector_object_type) 518 + { 519 + return Err(Error::from( 520 + BooleanExpressionError::UnknownDataConnectorTypeInObjectBooleanExpressionType { 521 + data_connector: qualified_data_connector_name.clone(), 522 + boolean_expression_type: qualified_name.clone(), 523 + data_connector_object_type: object_boolean_expression 524 + .data_connector_object_type 525 + .clone(), 526 + }, 527 + )); 528 + } 516 529 517 - data_connector_type_mappings 530 + data_connector_type_mappings 518 531 .get( 519 532 &qualified_object_type_name, 520 533 &qualified_data_connector_name, skipped 10 lines 531 544 }) 532 545 })?; 533 546 534 - // validate comparable fields535 - for comparable_field in object_boolean_expression.comparable_fields.iter() {536 - if !object_type_representation537 - .fields 538 - .contains_key(&comparable_field.field_name) 539 - {540 - return Err( 541 - BooleanExpressionError::UnknownFieldInObjectBooleanExpressionType { 542 - field_name: comparable_field.field_name.clone(), 543 - boolean_expression_type: qualified_name.clone(), 544 - } 545 - .into(), 546 - ); 547 + // validate comparable fields 548 + for comparable_field in object_boolean_expression.comparable_fields.iter() { 549 + if !object_type_representation 550 + .fields 551 + .contains_key(&comparable_field.field_name) 552 + { 553 + return Err( 554 + BooleanExpressionError::UnknownFieldInObjectBooleanExpressionType { 555 + field_name: comparable_field.field_name.clone(), 556 + boolean_expression_type: qualified_name.clone(), 547 557 } 558 + .into(), 559 + ); 560 + } 548 561 549 - // As of now, only `"enableAll": true` is allowed for field operators550 - match &comparable_field.operators { 562 + // As of now, only `"enableAll": true` is allowed for field operators 563 + match &comparable_field.operators { 551 564 EnableAllOrSpecific::EnableAll(true) => {} 552 565 _ => { 553 566 return Err(Error::UnsupportedFeature { skipped 1 lines 555 568 }) 556 569 } 557 570 } 558 - } 571 + } 559 572 560 - // Comparable fields should have all type fields561 - if object_boolean_expression.comparable_fields.len()562 - != object_type_representation.fields.len() 563 - { 564 - return Err(Error::UnsupportedFeature { 573 + // Comparable fields should have all type fields 574 + if object_boolean_expression.comparable_fields.len() ! = object_type_representation . fields . len ( ) 575 + { 576 + return Err(Error::UnsupportedFeature { 565 577 message: "Field level comparison operator configuration is not fully supported yet. Please add all fields in filterable_fields.".to_string(), 566 578 }); 567 - } 579 + } 568 580 569 - // validate graphql config570 - let graphql_config = object_boolean_expression571 - .graphql572 - .as_ref()573 - .map(|graphql_config| {574 - let graphql_type_name = 575 - mk_name(graphql_config.type_name.0.as_ref()).map(ast::TypeName)?; 576 - store_new_graphql_type(existing_graphql_types, Some(&graphql_type_name))?; 577 - Ok::<_, Error>(ObjectBooleanExpressionTypeGraphQlConfiguration { 578 - type_name: graphql_type_name, 579 - }) 580 - })581 - .transpose()?; 581 + // validate graphql config 582 + let graphql_config = object_boolean_expression 583 + .graphql 584 + .as_ref() 585 + .map(|graphql_config| { 586 + let graphql_type_name = 587 + mk_name(graphql_config.type_name.0.as_ref()).map(ast::TypeName)?; 588 + store_new_graphql_type(existing_graphql_types, Some(&graphql_type_name))?; 589 + Ok::<_, Error>(ObjectBooleanExpressionTypeGraphQlConfiguration { 590 + type_name: graphql_type_name, 591 + }) 592 + }) 593 + .transpose()?; 582 594 583 - let resolved_boolean_expression = ObjectBooleanExpressionType {584 - name: qualified_name.clone(),585 - object_type: qualified_object_type_name.clone(),586 - data_connector_name: qualified_data_connector_name,587 - data_connector_object_type: object_boolean_expression588 - .data_connector_object_type 589 - .clone(), 590 - graphql: graphql_config, 591 - }; 592 - Ok(resolved_boolean_expression) 593 - } 594 - } 595 + let resolved_boolean_expression = ObjectBooleanExpressionType { 596 + name: qualified_name.clone(), 597 + object_type: qualified_object_type_name.clone(), 598 + data_connector_name: qualified_data_connector_name, 599 + data_connector_object_type: object_boolean_expression. data_connector_object_type . clone ( ) , 600 + graphql: graphql_config, 601 + }; 602 + Ok(resolved_boolean_expression) 595 603 } 596 604 597 605 /// Helper function to create GraphQL compliant name skipped 3 lines 601 609 }) 602 610 } 603 611 612 + #[derive(Debug)] 604 613 pub struct TypeMappingToCollect<'a> { 605 614 pub type_name: &'a Qualified<CustomTypeName>, 606 615 pub ndc_object_type_name: &'a str, skipped 32 lines 639 648 mapping_to_collect: &TypeMappingToCollect, 640 649 data_connector_type_mappings: &DataConnectorTypeMappings, 641 650 data_connector_name: &Qualified<DataConnectorName>, 642 - type_representations : &HashMap<Qualified<CustomTypeName>, TypeRepresentation >, 643 - // ndc_object_types: &BTreeMap<String, ndc_models::ObjectType>, 651 + object_types : &HashMap<Qualified<CustomTypeName>, ObjectTypeRepresentation >, 652 + scalar_types: &HashMap<Qualified<CustomTypeName>, ScalarTypeRepresentation>, 644 653 collected_mappings: &mut BTreeMap<Qualified<CustomTypeName>, TypeMapping>, 645 654 ) -> Result<(), TypeMappingCollectionError> { 646 655 let type_mapping = data_connector_type_mappings skipped 29 lines 676 685 } 677 686 } 678 687 679 - let type_representation = type_representations 680 - .get(mapping_to_collect.type_name) 681 - .ok_or_else(|| TypeMappingCollectionError::InternalUnknownType { 682 - type_name: mapping_to_collect.type_name.clone(), 683 - })?; 688 + match object_types.get(mapping_to_collect.type_name) { 689 + Some(object_type_representation) => { 690 + let TypeMapping::Object { field_mappings, .. } = type_mapping; 691 + // For each field in the ObjectType, if that field is using an ObjectType in its type, 692 + // resolve the type mappings for that ObjectType too 693 + for (field_name, field_definition) in &object_type_representation.fields { 694 + let field_mapping = field_mappings.get(field_name).ok_or_else(|| { 695 + TypeMappingCollectionError::MissingFieldMapping { 696 + type_name: mapping_to_collect.type_name.clone(), 697 + field_name: field_name.clone(), 698 + data_connector: data_connector_name.clone(), 699 + ndc_type_name: mapping_to_collect.ndc_object_type_name.to_string(), 700 + } 701 + })?; 684 702 685 - if let TypeRepresentation::Object(object_type_representation) = type_representation { 686 - let TypeMapping::Object { field_mappings, .. } = type_mapping; 687 - // For each field in the ObjectType, if that field is using an ObjectType in its type, 688 - // resolve the type mappings for that ObjectType too 689 - for (field_name, field_definition) in &object_type_representation.fields { 690 - let field_mapping = field_mappings.get(field_name).ok_or_else(|| { 691 - TypeMappingCollectionError::MissingFieldMapping { 692 - type_name: mapping_to_collect.type_name.clone(), 693 - field_name: field_name.clone(), 694 - data_connector: data_connector_name.clone(), 695 - ndc_type_name: mapping_to_collect.ndc_object_type_name.to_string(), 696 - } 697 - })?; 698 - if let Some(object_type_name) = get_underlying_object_type_or_unknown_type( 699 - &field_definition.field_type, 700 - type_representations, 701 - ) 702 - .map_err(|unknown_type| { 703 - TypeMappingCollectionError::InternalUnknownType { 704 - type_name: unknown_type, 703 + if let Some(object_type_name) = 704 + unwrap_custom_type_name(&field_definition.field_type) 705 + { 706 + match get_type_representation(object_type_name, object_types, scalar_types) 707 + .map_err(|_| TypeMappingCollectionError::InternalUnknownType { 708 + type_name: object_type_name.clone(), 709 + })? { 710 + TypeRepresentation::Object(_) => { 711 + let underlying_ndc_field_named_type = 712 + get_underlying_named_type(&field_mapping.column_type)?; 713 + 714 + let field_type_mapping_to_collect = TypeMappingToCollect { 715 + type_name: object_type_name, 716 + ndc_object_type_name: underlying_ndc_field_named_type, 717 + }; 718 + 719 + collect_type_mapping_for_source( 720 + &field_type_mapping_to_collect, 721 + data_connector_type_mappings, 722 + data_connector_name, 723 + object_types, 724 + scalar_types, 725 + collected_mappings, 726 + )?; 727 + } 728 + TypeRepresentation::Scalar(_) => {} 729 + } 705 730 } 706 - })? { 707 - let underlying_ndc_field_named_type = 708 - get_underlying_named_type(&field_mapping.column_type)?; 709 - 710 - let field_type_mapping_to_collect = TypeMappingToCollect { 711 - type_name: object_type_name, 712 - ndc_object_type_name: underlying_ndc_field_named_type, 713 - }; 714 - collect_type_mapping_for_source( 715 - &field_type_mapping_to_collect, 716 - data_connector_type_mappings, 717 - data_connector_name, 718 - type_representations, 719 - collected_mappings, 720 - )?; 721 731 } 732 + Ok(()) 722 733 } 723 - } 734 + None => match scalar_types.get(mapping_to_collect.type_name) { 735 + Some(_) => Ok(()), 736 + None => Err(TypeMappingCollectionError::InternalUnknownType { 737 + type_name: mapping_to_collect.type_name.clone(), 738 + }), 739 + }, 740 + }?; 724 741 725 742 Ok(()) 726 743 } skipped 1 lines