Projects STRLCPY graphql-engine Commits 4bc2f21f
🤬
  • remove `TypeRepresentation` (#456)

    <!-- Thank you for submitting this PR! :) -->
    
    ## Description
    
    Previously we moved all our types around in one big bucket, meaning we
    often had to check we had the thing we wanted, this splits it up so
    dependencies are more granular and clearer.
    
    This means instead of passing `types` around, we'll have both
    `scalar_types` or `object_types`. Usually just `object_types` though.
    
    V3_GIT_ORIGIN_REV_ID: 6a6b8d6265b0391f8910f3d4f8932ad151453c18
  • Loading...
  • Daniel Harvey committed with hasura-bot 1 month ago
    4bc2f21f
    1 parent 624df9a5
  • ■ ■ ■ ■ ■ ■
    v3/crates/engine/src/metadata/resolved/argument.rs
    1 1  use crate::metadata::resolved::ndc_validation;
    2 2  use crate::metadata::resolved::subgraph::{ArgumentInfo, Qualified};
    3 3  use crate::metadata::resolved::types::{
    4  - get_underlying_object_type_or_unknown_type, TypeMappingToCollect, TypeRepresentation,
     4 + get_type_representation, unwrap_custom_type_name, ObjectTypeRepresentation,
     5 + ScalarTypeRepresentation, TypeMappingToCollect, TypeRepresentation,
    5 6  };
    6 7  use indexmap::IndexMap;
    7 8  use itertools::Itertools;
    skipped 46 lines
    54 55   arguments: &'a IndexMap<ArgumentName, ArgumentInfo>,
    55 56   argument_mapping: &HashMap<ArgumentName, String>,
    56 57   ndc_arguments: &'a BTreeMap<String, ndc_models::ArgumentInfo>,
    57  - all_type_representations: &'a HashMap<Qualified<CustomTypeName>, TypeRepresentation>,
     58 + object_types: &'a HashMap<Qualified<CustomTypeName>, ObjectTypeRepresentation>,
     59 + scalar_types: &'a HashMap<Qualified<CustomTypeName>, ScalarTypeRepresentation>,
    58 60  ) -> Result<(HashMap<ArgumentName, String>, Vec<TypeMappingToCollect<'a>>), ArgumentMappingError> {
    59 61   let mut unconsumed_argument_mappings: HashMap<&ArgumentName, &String> =
    60 62   HashMap::from_iter(argument_mapping.iter());
    skipped 26 lines
    87 89   });
    88 90   }
    89 91   
    90  - if let Some(object_type_name) = get_underlying_object_type_or_unknown_type(
    91  - &argument_type.argument_type,
    92  - all_type_representations,
    93  - )
    94  - .map_err(|custom_type_name| ArgumentMappingError::UnknownType {
    95  - argument_name: argument_name.clone(),
    96  - data_type: custom_type_name.clone(),
    97  - })? {
    98  - let underlying_ndc_argument_named_type =
    99  - ndc_validation::get_underlying_named_type(&ndc_argument_info.argument_type)
    100  - .map_err(ArgumentMappingError::NDCValidationError)?;
     92 + // only do further checks if this is not a built-in type
     93 + if let Some(object_type_name) = unwrap_custom_type_name(&argument_type.argument_type) {
     94 + match get_type_representation(object_type_name, object_types, scalar_types).map_err(
     95 + |_| ArgumentMappingError::UnknownType {
     96 + argument_name: argument_name.clone(),
     97 + data_type: object_type_name.clone(),
     98 + },
     99 + )? {
     100 + TypeRepresentation::Object(_) => {
     101 + let underlying_ndc_argument_named_type =
     102 + ndc_validation::get_underlying_named_type(&ndc_argument_info.argument_type)
     103 + .map_err(ArgumentMappingError::NDCValidationError)?;
    101 104   
    102  - type_mappings_to_collect.push(TypeMappingToCollect {
    103  - type_name: object_type_name,
    104  - ndc_object_type_name: underlying_ndc_argument_named_type,
    105  - })
     105 + type_mappings_to_collect.push(TypeMappingToCollect {
     106 + type_name: object_type_name,
     107 + ndc_object_type_name: underlying_ndc_argument_named_type,
     108 + })
     109 + }
     110 + TypeRepresentation::Scalar(_) => (),
     111 + }
    106 112   }
    107 113   }
    108 114   
    skipped 14 lines
  • ■ ■ ■ ■ ■ ■
    v3/crates/engine/src/metadata/resolved/command.rs
    skipped 6 lines
    7 7   deserialize_qualified_btreemap, mk_qualified_type_reference, serialize_qualified_btreemap,
    8 8   ArgumentInfo, Qualified, QualifiedTypeReference,
    9 9  };
    10  -use crate::metadata::resolved::types::{get_underlying_object_type, TypeRepresentation};
     10 +use crate::metadata::resolved::types::{
     11 + get_type_representation, get_underlying_object_type, unwrap_custom_type_name,
     12 + ObjectTypeRepresentation, ScalarTypeRepresentation,
     13 +};
    11 14  use crate::metadata::resolved::types::{mk_name, TypeMapping};
    12 15  use indexmap::IndexMap;
    13 16  use lang_graphql::ast::common as ast;
    skipped 53 lines
    67 70  fn is_valid_type(
    68 71   type_obj: &TypeReference,
    69 72   subgraph: &str,
    70  - types: &HashMap<Qualified<CustomTypeName>, TypeRepresentation>,
     73 + object_types: &HashMap<Qualified<CustomTypeName>, ObjectTypeRepresentation>,
     74 + scalar_types: &HashMap<Qualified<CustomTypeName>, ScalarTypeRepresentation>,
    71 75  ) -> bool {
    72 76   match &type_obj.underlying_type {
    73  - BaseType::List(type_obj) => is_valid_type(type_obj, subgraph, types),
     77 + BaseType::List(type_obj) => is_valid_type(type_obj, subgraph, object_types, scalar_types),
    74 78   BaseType::Named(type_name) => match type_name {
    75 79   TypeName::Inbuilt(_) => true,
    76  - TypeName::Custom(type_name) => types
    77  - .get(&Qualified::new(subgraph.to_string(), type_name.to_owned()))
    78  - .is_some(),
     80 + TypeName::Custom(type_name) => {
     81 + let qualified_type_name =
     82 + Qualified::new(subgraph.to_string(), type_name.to_owned());
     83 + 
     84 + get_type_representation(&qualified_type_name, object_types, scalar_types).is_ok()
     85 + }
    79 86   },
    80 87   }
    81 88  }
    skipped 1 lines
    83 90  pub fn resolve_command(
    84 91   command: &CommandV1,
    85 92   subgraph: &str,
    86  - types: &HashMap<Qualified<CustomTypeName>, TypeRepresentation>,
     93 + object_types: &HashMap<Qualified<CustomTypeName>, ObjectTypeRepresentation>,
     94 + scalar_types: &HashMap<Qualified<CustomTypeName>, ScalarTypeRepresentation>,
    87 95  ) -> Result<Command, Error> {
    88 96   let mut arguments = IndexMap::new();
    89 97   let qualified_command_name = Qualified::new(subgraph.to_string(), command.name.clone());
    90 98   let command_description = command.description.clone();
    91 99   // duplicate command arguments should not be allowed
    92 100   for argument in &command.arguments {
    93  - if is_valid_type(&argument.argument_type, subgraph, types) {
     101 + if is_valid_type(
     102 + &argument.argument_type,
     103 + subgraph,
     104 + object_types,
     105 + scalar_types,
     106 + ) {
    94 107   if arguments
    95 108   .insert(
    96 109   argument.name.clone(),
    skipped 50 lines
    147 160   command: &mut Command,
    148 161   subgraph: &str,
    149 162   data_connectors: &data_connectors::DataConnectors,
    150  - types: &HashMap<Qualified<CustomTypeName>, TypeRepresentation>,
     163 + object_types: &HashMap<Qualified<CustomTypeName>, ObjectTypeRepresentation>,
     164 + scalar_types: &HashMap<Qualified<CustomTypeName>, ScalarTypeRepresentation>,
     165 + 
    151 166   data_connector_type_mappings: &DataConnectorTypeMappings,
    152 167  ) -> Result<(), Error> {
    153 168   if command.source.is_some() {
    skipped 54 lines
    208 223   &command.arguments,
    209 224   &command_source.argument_mapping,
    210 225   ndc_arguments,
    211  - types,
     226 + object_types,
     227 + scalar_types,
    212 228   )
    213 229   .map_err(|err| match &command_source.data_connector_command {
    214 230   DataConnectorCommand::Function(function_name) => {
    skipped 14 lines
    229 245   }
    230 246   })?;
    231 247   
    232  - let command_result_base_object_type_name =
    233  - get_underlying_object_type(&command.output_type, types)?;
     248 + // get object type name if it exists for the output type, and refers to a valid object
     249 + let command_result_base_object_type_name = unwrap_custom_type_name(&command.output_type)
     250 + .and_then(|custom_type_name| {
     251 + get_underlying_object_type(custom_type_name, object_types).ok()
     252 + });
     253 + 
    234 254   let mut type_mappings = BTreeMap::new();
    235 255   
    236 256   // Get the type mapping to resolve for the result type
    skipped 26 lines
    263 283   type_mapping_to_collect,
    264 284   data_connector_type_mappings,
    265 285   &qualified_data_connector_name,
    266  - types,
     286 + object_types,
     287 + scalar_types,
    267 288   &mut type_mappings,
    268 289   )
    269 290   .map_err(|error| Error::CommandTypeMappingCollectionError {
    skipped 82 lines
  • ■ ■ ■ ■ ■ ■
    v3/crates/engine/src/metadata/resolved/error.rs
    skipped 2 lines
    3 3   
    4 4  use crate::metadata::resolved::argument::ArgumentMappingError;
    5 5  use crate::metadata::resolved::subgraph::Qualified;
    6  -use crate::metadata::resolved::types::TypeRepresentation;
    7 6  use lang_graphql::ast::common as ast;
    8 7  use open_dds::{
    9 8   arguments::ArgumentName,
    skipped 247 lines
    257 256   model_name: Qualified<ModelName>,
    258 257   field_name: FieldName,
    259 258   },
    260  - 
    261  - #[error("invalid type represented for model {model_name:}: {type_representation:}")]
    262  - InvalidTypeRepresentation {
    263  - model_name: Qualified<ModelName>,
    264  - type_representation: TypeRepresentation,
    265  - },
    266 259   #[error("a source must be defined for model {model:} in order to use filter expressions")]
    267 260   CannotUseFilterExpressionsWithoutSource { model: Qualified<ModelName> },
    268 261   #[error("graphql config must be defined for a filter expression to be used in a {model:}")]
    skipped 207 lines
    476 469   header_name: String,
    477 470   },
    478 471   #[error("the data type {data_type:} has not been defined")]
    479  - UnknownDataType {
     472 + UnknownType {
     473 + data_type: Qualified<CustomTypeName>,
     474 + },
     475 + #[error("the object type {data_type:} has not been defined")]
     476 + UnknownObjectType {
     477 + data_type: Qualified<CustomTypeName>,
     478 + },
     479 + #[error("the scalar type {data_type:} has not been defined")]
     480 + UnknownScalarType {
    480 481   data_type: Qualified<CustomTypeName>,
    481 482   },
     483 + 
    482 484   #[error("model {model_name:} with arguments is unsupported as a global ID source")]
    483 485   ModelWithArgumentsAsGlobalIdSource { model_name: Qualified<ModelName> },
    484 486   #[error(
    skipped 267 lines
  • ■ ■ ■ ■ ■ ■
    v3/crates/engine/src/metadata/resolved/metadata.rs
    skipped 21 lines
    22 22  use crate::metadata::resolved::subgraph::Qualified;
    23 23  use crate::metadata::resolved::types::{
    24 24   mk_name, resolve_object_type, resolve_output_type_permission, store_new_graphql_type,
    25  - TypeRepresentation,
     25 + ObjectTypeRepresentation,
    26 26  };
    27 27   
    28 28  use super::types::{
    skipped 5 lines
    34 34  /// Resolved and validated metadata for a project. Used internally in the v3 server.
    35 35  #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
    36 36  pub struct Metadata {
    37  - pub types: HashMap<Qualified<CustomTypeName>, TypeRepresentation>,
     37 + pub object_types: HashMap<Qualified<CustomTypeName>, ObjectTypeRepresentation>,
     38 + pub scalar_types: HashMap<Qualified<CustomTypeName>, ScalarTypeRepresentation>,
    38 39   pub models: IndexMap<Qualified<ModelName>, Model>,
    39 40   pub commands: IndexMap<Qualified<CommandName>, command::Command>,
    40 41   pub boolean_expression_types: HashMap<Qualified<CustomTypeName>, ObjectBooleanExpressionType>,
    skipped 2 lines
    43 44   
    44 45  pub type DataConnectorTypeMappingsForObjectType =
    45 46   HashMap<Qualified<DataConnectorName>, HashMap<String, TypeMapping>>;
     47 + 
     48 +#[derive(Debug)]
    46 49  pub struct DataConnectorTypeMappings(
    47 50   HashMap<Qualified<CustomTypeName>, DataConnectorTypeMappingsForObjectType>,
    48 51  );
    skipped 74 lines
    123 126   > = HashMap::new();
    124 127   
    125 128   // resolve object types
    126  - let (data_connector_type_mappings, types) = resolve_data_connector_type_mappings_and_objects(
    127  - metadata_accessor,
    128  - &data_connectors,
    129  - &mut existing_graphql_types,
    130  - &mut global_id_enabled_types,
    131  - &mut apollo_federation_entity_enabled_types,
    132  - )?;
     129 + let (data_connector_type_mappings, object_types) =
     130 + resolve_data_connector_type_mappings_and_objects(
     131 + metadata_accessor,
     132 + &data_connectors,
     133 + &mut existing_graphql_types,
     134 + &mut global_id_enabled_types,
     135 + &mut apollo_federation_entity_enabled_types,
     136 + )?;
    133 137   
    134 138   // resolve scalar types
    135 139   let scalar_types = resolve_scalar_types(metadata_accessor, &mut existing_graphql_types)?;
    136 140   
    137 141   // resolve type permissions
    138  - let types = resolve_type_permissions(metadata_accessor, extend_types(types, scalar_types)?)?;
     142 + let object_types = resolve_type_permissions(metadata_accessor, object_types)?;
    139 143   
    140 144   // resolve object boolean expression types
    141 145   let boolean_expression_types = resolve_boolean_expression_types(
    142 146   metadata_accessor,
    143 147   &data_connectors,
    144 148   &data_connector_type_mappings,
    145  - &types,
     149 + &object_types,
    146 150   &mut existing_graphql_types,
    147 151   )?;
    148 152   
    skipped 2 lines
    151 155   resolve_data_connector_scalar_representations(
    152 156   metadata_accessor,
    153 157   &mut data_connectors,
    154  - &types,
     158 + &scalar_types,
    155 159   &mut existing_graphql_types,
    156 160   )?;
    157 161   
    skipped 3 lines
    161 165   metadata_accessor,
    162 166   &data_connectors,
    163 167   &data_connector_type_mappings,
    164  - &types,
     168 + &object_types,
     169 + &scalar_types,
    165 170   &mut existing_graphql_types,
    166 171   &mut global_id_enabled_types,
    167 172   &mut apollo_federation_entity_enabled_types,
    skipped 23 lines
    191 196   metadata_accessor,
    192 197   &data_connectors,
    193 198   &data_connector_type_mappings,
    194  - &types,
     199 + &object_types,
     200 + &scalar_types,
    195 201   )?;
    196 202   
    197 203   // resolve relationships
    198  - let types = resolve_relationships(
     204 + let object_types = resolve_relationships(
    199 205   metadata_accessor,
    200 206   &data_connectors,
    201  - types,
     207 + object_types,
    202 208   &models,
    203 209   &commands,
    204 210   )?;
    skipped 7 lines
    212 218   // hence Model permissions should be resolved after the relationships of a
    213 219   // model is resolved.
    214 220   // TODO: make this return values rather than blindly mutating it's inputs
    215  - resolve_model_permissions(metadata_accessor, &data_connectors, &types, &mut models)?;
     221 + resolve_model_permissions(
     222 + metadata_accessor,
     223 + &data_connectors,
     224 + &object_types,
     225 + &mut models,
     226 + )?;
    216 227   
    217 228   Ok(Metadata {
    218  - types,
     229 + scalar_types,
     230 + object_types,
    219 231   models,
    220 232   commands,
    221 233   boolean_expression_types,
    skipped 6 lines
    228 240   metadata_accessor: &open_dds::accessor::MetadataAccessor,
    229 241   data_connectors: &data_connectors::DataConnectors,
    230 242   data_connector_type_mappings: &DataConnectorTypeMappings,
    231  - types: &HashMap<Qualified<CustomTypeName>, TypeRepresentation>,
     243 + object_types: &HashMap<Qualified<CustomTypeName>, ObjectTypeRepresentation>,
     244 + scalar_types: &HashMap<Qualified<CustomTypeName>, ScalarTypeRepresentation>,
    232 245  ) -> Result<IndexMap<Qualified<CommandName>, command::Command>, Error> {
    233 246   let mut commands: IndexMap<Qualified<CommandName>, command::Command> = IndexMap::new();
    234 247   for open_dds::accessor::QualifiedObject {
    skipped 1 lines
    236 249   object: command,
    237 250   } in &metadata_accessor.commands
    238 251   {
    239  - let mut resolved_command = command::resolve_command(command, subgraph, types)?;
     252 + let mut resolved_command =
     253 + command::resolve_command(command, subgraph, object_types, scalar_types)?;
    240 254   if let Some(command_source) = &command.source {
    241 255   command::resolve_command_source(
    242 256   command_source,
    243 257   &mut resolved_command,
    244 258   subgraph,
    245 259   data_connectors,
    246  - types,
     260 + object_types,
     261 + scalar_types,
    247 262   data_connector_type_mappings,
    248 263   )?;
    249 264   }
    skipped 11 lines
    261 276  }
    262 277   
    263 278  /// resolve object types, matching them to that in the data connectors
    264  -/// this currently works by mutating `types` and `existing_graphql_types`, we should try
    265  -/// and change this to return new values here and make the caller combine them together
    266 279  fn resolve_data_connector_type_mappings_and_objects(
    267 280   metadata_accessor: &open_dds::accessor::MetadataAccessor,
    268 281   data_connectors: &data_connectors::DataConnectors,
    skipped 6 lines
    275 288  ) -> Result<
    276 289   (
    277 290   DataConnectorTypeMappings,
    278  - HashMap<Qualified<CustomTypeName>, TypeRepresentation>,
     291 + HashMap<Qualified<CustomTypeName>, ObjectTypeRepresentation>,
    279 292   ),
    280 293   Error,
    281 294  > {
    282 295   let mut data_connector_type_mappings = DataConnectorTypeMappings::new();
    283  - let mut types = HashMap::new();
     296 + let mut object_types = HashMap::new();
    284 297   
    285 298   for open_dds::accessor::QualifiedObject {
    286 299   subgraph,
    skipped 3 lines
    290 303   let qualified_object_type_name =
    291 304   Qualified::new(subgraph.to_string(), object_type_definition.name.clone());
    292 305   
    293  - let mut resolved_type = resolve_object_type(
     306 + let resolved_object_type = resolve_object_type(
    294 307   object_type_definition,
    295 308   existing_graphql_types,
    296 309   &qualified_object_type_name,
    skipped 3 lines
    300 313   )?;
    301 314   
    302 315   // resolve object types' type mappings
    303  - if let TypeRepresentation::Object(ref mut resolved_object_type) = resolved_type {
    304  - for dc_type_mapping in &object_type_definition.data_connector_type_mapping {
    305  - let qualified_data_connector_name = Qualified::new(
    306  - subgraph.to_string(),
    307  - dc_type_mapping.data_connector_name.clone(),
    308  - );
    309  - let type_mapping = resolve_data_connector_type_mapping(
    310  - dc_type_mapping,
    311  - &qualified_object_type_name,
    312  - subgraph,
    313  - resolved_object_type,
    314  - data_connectors,
    315  - )
    316  - .map_err(|type_validation_error| {
    317  - Error::DataConnectorTypeMappingValidationError {
    318  - type_name: qualified_object_type_name.clone(),
    319  - error: type_validation_error,
    320  - }
    321  - })?;
    322  - data_connector_type_mappings.insert(
    323  - &qualified_object_type_name,
    324  - &qualified_data_connector_name,
    325  - &dc_type_mapping.data_connector_object_type,
    326  - type_mapping,
    327  - )?;
    328  - }
     316 + for dc_type_mapping in &object_type_definition.data_connector_type_mapping {
     317 + let qualified_data_connector_name = Qualified::new(
     318 + subgraph.to_string(),
     319 + dc_type_mapping.data_connector_name.clone(),
     320 + );
     321 + let type_mapping = resolve_data_connector_type_mapping(
     322 + dc_type_mapping,
     323 + &qualified_object_type_name,
     324 + subgraph,
     325 + &resolved_object_type,
     326 + data_connectors,
     327 + )
     328 + .map_err(|type_validation_error| {
     329 + Error::DataConnectorTypeMappingValidationError {
     330 + type_name: qualified_object_type_name.clone(),
     331 + error: type_validation_error,
     332 + }
     333 + })?;
     334 + data_connector_type_mappings.insert(
     335 + &qualified_object_type_name,
     336 + &qualified_data_connector_name,
     337 + &dc_type_mapping.data_connector_object_type,
     338 + type_mapping,
     339 + )?;
    329 340   }
    330 341   
    331  - if types
    332  - .insert(qualified_object_type_name.clone(), resolved_type)
     342 + if object_types
     343 + .insert(qualified_object_type_name.clone(), resolved_object_type)
    333 344   .is_some()
    334 345   {
    335 346   return Err(Error::DuplicateTypeDefinition {
    skipped 2 lines
    338 349   }
    339 350   }
    340 351   
    341  - Ok((data_connector_type_mappings, types))
    342  -}
    343  - 
    344  -/// combine two sets of types, returning a `DuplicateTypeDefinition` error if we find duplicates
    345  -/// along the way
    346  -fn extend_types(
    347  - mut old_types: HashMap<Qualified<CustomTypeName>, TypeRepresentation>,
    348  - new_types: HashMap<Qualified<CustomTypeName>, TypeRepresentation>,
    349  -) -> Result<HashMap<Qualified<CustomTypeName>, TypeRepresentation>, Error> {
    350  - for new_type_name in new_types.keys() {
    351  - if old_types.contains_key(new_type_name) {
    352  - return Err(Error::DuplicateTypeDefinition {
    353  - name: new_type_name.clone(),
    354  - });
    355  - }
    356  - }
    357  - old_types.extend(new_types);
    358  - Ok(old_types)
     352 + Ok((data_connector_type_mappings, object_types))
    359 353  }
    360 354   
    361 355  /// resolve scalar types
    skipped 2 lines
    364 358  fn resolve_scalar_types(
    365 359   metadata_accessor: &open_dds::accessor::MetadataAccessor,
    366 360   existing_graphql_types: &mut HashSet<ast::TypeName>,
    367  -) -> Result<HashMap<Qualified<CustomTypeName>, TypeRepresentation>, Error> {
     361 +) -> Result<HashMap<Qualified<CustomTypeName>, ScalarTypeRepresentation>, Error> {
    368 362   let mut scalar_types = HashMap::new();
    369 363   for open_dds::accessor::QualifiedObject {
    370 364   subgraph,
    skipped 13 lines
    384 378   if scalar_types
    385 379   .insert(
    386 380   qualified_scalar_type_name.clone(),
    387  - TypeRepresentation::ScalarType(ScalarTypeRepresentation {
     381 + ScalarTypeRepresentation {
    388 382   graphql_type_name: graphql_type_name.clone(),
    389 383   description: scalar_type.description.clone(),
    390  - }),
     384 + },
    391 385   )
    392 386   .is_some()
    393 387   {
    skipped 10 lines
    404 398  /// this works by mutating `types`, and returning an owned value
    405 399  fn resolve_type_permissions(
    406 400   metadata_accessor: &open_dds::accessor::MetadataAccessor,
    407  - mut types: HashMap<Qualified<CustomTypeName>, TypeRepresentation>,
    408  -) -> Result<HashMap<Qualified<CustomTypeName>, TypeRepresentation>, Error> {
     401 + mut object_types: HashMap<Qualified<CustomTypeName>, ObjectTypeRepresentation>,
     402 +) -> Result<HashMap<Qualified<CustomTypeName>, ObjectTypeRepresentation>, Error> {
    409 403   // resolve type permissions
    410 404   for open_dds::accessor::QualifiedObject {
    411 405   subgraph,
    skipped 4 lines
    416 410   subgraph.to_string(),
    417 411   output_type_permission.type_name.to_owned(),
    418 412   );
    419  - match types.get_mut(&qualified_type_name) {
     413 + match object_types.get_mut(&qualified_type_name) {
    420 414   None => {
    421 415   return Err(Error::UnknownTypeInOutputPermissionsDefinition {
    422 416   type_name: qualified_type_name,
    skipped 4 lines
    427 421   }
    428 422   }
    429 423   }
    430  - Ok(types)
     424 + Ok(object_types)
    431 425  }
    432 426   
    433 427  /// resolve object boolean expression types
    skipped 1 lines
    435 429   metadata_accessor: &open_dds::accessor::MetadataAccessor,
    436 430   data_connectors: &data_connectors::DataConnectors,
    437 431   data_connector_type_mappings: &DataConnectorTypeMappings,
    438  - types: &HashMap<Qualified<CustomTypeName>, TypeRepresentation>,
     432 + object_types: &HashMap<Qualified<CustomTypeName>, ObjectTypeRepresentation>,
    439 433   existing_graphql_types: &mut HashSet<ast::TypeName>,
    440 434  ) -> Result<HashMap<Qualified<CustomTypeName>, ObjectBooleanExpressionType>, Error> {
    441 435   let mut boolean_expression_types = HashMap::new();
    skipped 6 lines
    448 442   boolean_expression_type,
    449 443   subgraph,
    450 444   data_connectors,
    451  - types,
     445 + object_types,
    452 446   data_connector_type_mappings,
    453 447   existing_graphql_types,
    454 448   )?;
    skipped 17 lines
    472 466  fn resolve_data_connector_scalar_representations(
    473 467   metadata_accessor: &open_dds::accessor::MetadataAccessor,
    474 468   data_connectors: &mut data_connectors::DataConnectors,
    475  - types: &HashMap<Qualified<CustomTypeName>, TypeRepresentation>,
     469 + scalar_types: &HashMap<Qualified<CustomTypeName>, ScalarTypeRepresentation>,
    476 470   existing_graphql_types: &mut HashSet<ast::TypeName>,
    477 471  ) -> Result<(), Error> {
    478 472   for open_dds::accessor::QualifiedObject {
    skipped 32 lines
    511 505   TypeName::Custom(type_name) => {
    512 506   let qualified_type_name =
    513 507   Qualified::new(subgraph.to_string(), type_name.to_owned());
    514  - let _representation = types.get(&qualified_type_name).ok_or_else(|| {
    515  - Error::ScalarTypeUnknownRepresentation {
    516  - scalar_type: scalar_type_name.clone(),
    517  - type_name: qualified_type_name,
    518  - }
    519  - })?;
     508 + let _representation =
     509 + scalar_types.get(&qualified_type_name).ok_or_else(|| {
     510 + Error::ScalarTypeUnknownRepresentation {
     511 + scalar_type: scalar_type_name.clone(),
     512 + type_name: qualified_type_name,
     513 + }
     514 + })?;
    520 515   }
    521 516   }
    522 517   scalar_type.representation = Some(scalar_type_representation.representation.clone());
    skipped 28 lines
    551 546   metadata_accessor: &open_dds::accessor::MetadataAccessor,
    552 547   data_connectors: &data_connectors::DataConnectors,
    553 548   data_connector_type_mappings: &DataConnectorTypeMappings,
    554  - types: &HashMap<Qualified<CustomTypeName>, TypeRepresentation>,
     549 + object_types: &HashMap<Qualified<CustomTypeName>, ObjectTypeRepresentation>,
     550 + scalar_types: &HashMap<Qualified<CustomTypeName>, ScalarTypeRepresentation>,
    555 551   existing_graphql_types: &mut HashSet<ast::TypeName>,
    556 552   global_id_enabled_types: &mut HashMap<Qualified<CustomTypeName>, Vec<Qualified<ModelName>>>,
    557 553   apollo_federation_entity_enabled_types: &mut HashMap<
    skipped 16 lines
    574 570   let mut resolved_model = resolve_model(
    575 571   subgraph,
    576 572   model,
    577  - types,
     573 + object_types,
    578 574   global_id_enabled_types,
    579 575   apollo_federation_entity_enabled_types,
    580 576   boolean_expression_types,
    skipped 20 lines
    601 597   &mut resolved_model,
    602 598   subgraph,
    603 599   data_connectors,
    604  - types,
     600 + object_types,
     601 + scalar_types,
    605 602   data_connector_type_mappings,
    606 603   )?;
    607 604   }
    skipped 26 lines
    634 631  fn resolve_relationships(
    635 632   metadata_accessor: &open_dds::accessor::MetadataAccessor,
    636 633   data_connectors: &data_connectors::DataConnectors,
    637  - mut types: HashMap<Qualified<CustomTypeName>, TypeRepresentation>,
     634 + mut object_types: HashMap<Qualified<CustomTypeName>, ObjectTypeRepresentation>,
    638 635   models: &IndexMap<Qualified<ModelName>, Model>,
    639 636   commands: &IndexMap<Qualified<CommandName>, command::Command>,
    640  -) -> Result<HashMap<Qualified<CustomTypeName>, TypeRepresentation>, Error> {
     637 +) -> Result<HashMap<Qualified<CustomTypeName>, ObjectTypeRepresentation>, Error> {
    641 638   for open_dds::accessor::QualifiedObject {
    642 639   subgraph,
    643 640   object: relationship,
    skipped 1 lines
    645 642   {
    646 643   let qualified_relationship_source_type_name =
    647 644   Qualified::new(subgraph.to_string(), relationship.source.to_owned());
    648  - let source_type = types
     645 + let object_representation = object_types
    649 646   .get_mut(&qualified_relationship_source_type_name)
    650 647   .ok_or_else(|| Error::RelationshipDefinedOnUnknownType {
    651 648   relationship_name: relationship.name.clone(),
    652 649   type_name: qualified_relationship_source_type_name.clone(),
    653 650   })?;
    654 651   
    655  - match source_type {
    656  - TypeRepresentation::Object(object_representation) => {
    657  - let resolved_relationship = resolve_relationship(
    658  - relationship,
    659  - subgraph,
    660  - models,
    661  - commands,
    662  - data_connectors,
    663  - object_representation,
    664  - )?;
    665  - if object_representation
    666  - .relationships
    667  - .insert(
    668  - resolved_relationship.field_name.clone(),
    669  - resolved_relationship,
    670  - )
    671  - .is_some()
    672  - {
    673  - return Err(Error::DuplicateRelationshipInSourceType {
    674  - type_name: qualified_relationship_source_type_name,
    675  - relationship_name: relationship.name.clone(),
    676  - });
    677  - }
    678  - }
    679  - TypeRepresentation::ScalarType { .. } => {
    680  - return Err(Error::NotSupported {
    681  - reason: "A relationship can only be defined on an OBJECT type.".to_string(),
    682  - })
    683  - }
     652 + let resolved_relationship = resolve_relationship(
     653 + relationship,
     654 + subgraph,
     655 + models,
     656 + commands,
     657 + data_connectors,
     658 + object_representation,
     659 + )?;
     660 + if object_representation
     661 + .relationships
     662 + .insert(
     663 + resolved_relationship.field_name.clone(),
     664 + resolved_relationship,
     665 + )
     666 + .is_some()
     667 + {
     668 + return Err(Error::DuplicateRelationshipInSourceType {
     669 + type_name: qualified_relationship_source_type_name,
     670 + relationship_name: relationship.name.clone(),
     671 + });
    684 672   }
    685 673   }
    686  - Ok(types)
     674 + 
     675 + Ok(object_types)
    687 676  }
    688 677   
    689 678  /// resolve command permissions
    skipped 35 lines
    725 714  fn resolve_model_permissions(
    726 715   metadata_accessor: &open_dds::accessor::MetadataAccessor,
    727 716   data_connectors: &data_connectors::DataConnectors,
    728  - types: &HashMap<Qualified<CustomTypeName>, TypeRepresentation>,
     717 + object_types: &HashMap<Qualified<CustomTypeName>, ObjectTypeRepresentation>,
    729 718   models: &mut IndexMap<Qualified<ModelName>, Model>,
    730 719  ) -> Result<(), Error> {
    731 720   // Note: Model permissions's predicate can include the relationship field,
    skipped 11 lines
    743 732   .ok_or_else(|| Error::UnknownModelInModelSelectPermissions {
    744 733   model_name: model_name.clone(),
    745 734   })?;
     735 + 
    746 736   if model.select_permissions.is_none() {
    747 737   let select_permissions = Some(resolve_model_select_permissions(
    748 738   model,
    749 739   subgraph,
    750 740   permissions,
    751 741   data_connectors,
    752  - types,
     742 + object_types,
    753 743   models, // This is required to get the model for the relationship target
    754 744   )?);
    755 745   
    skipped 15 lines
  • ■ ■ ■ ■ ■ ■
    v3/crates/engine/src/metadata/resolved/model.rs
    skipped 4 lines
    5 5  use super::typecheck;
    6 6  use super::types::{
    7 7   collect_type_mapping_for_source, NdcColumnForComparison, ObjectBooleanExpressionType,
    8  - ObjectTypeRepresentation, TypeMappingToCollect,
     8 + ObjectTypeRepresentation, ScalarTypeRepresentation, TypeMappingToCollect,
    9 9  };
    10 10  use crate::metadata::resolved::argument::get_argument_mappings;
    11 11  use crate::metadata::resolved::data_connector;
    skipped 9 lines
    21 21   QualifiedTypeReference,
    22 22  };
    23 23  use crate::metadata::resolved::types::store_new_graphql_type;
    24  -use crate::metadata::resolved::types::TypeRepresentation;
    25 24  use crate::metadata::resolved::types::{mk_name, FieldDefinition, TypeMapping};
    26 25  use crate::schema::types::output_type::relationship::{
    27 26   ModelTargetSource, PredicateRelationshipAnnotation,
    skipped 250 lines
    278 277  pub fn resolve_model(
    279 278   subgraph: &str,
    280 279   model: &ModelV1,
    281  - types: &HashMap<Qualified<CustomTypeName>, TypeRepresentation>,
     280 + object_types: &HashMap<Qualified<CustomTypeName>, ObjectTypeRepresentation>,
    282 281   global_id_enabled_types: &mut HashMap<Qualified<CustomTypeName>, Vec<Qualified<ModelName>>>,
    283 282   apollo_federation_entity_enabled_types: &mut HashMap<
    284 283   Qualified<CustomTypeName>,
    skipped 5 lines
    290 289   Qualified::new(subgraph.to_string(), model.object_type.to_owned());
    291 290   let qualified_model_name = Qualified::new(subgraph.to_string(), model.name.clone());
    292 291   let object_type_representation = get_model_object_type_representation(
    293  - types,
     292 + object_types,
    294 293   &qualified_object_type_name,
    295 294   &qualified_model_name,
    296 295   )?;
    skipped 221 lines
    518 517   subgraph: &str,
    519 518   data_connectors: &data_connectors::DataConnectors,
    520 519   fields: &IndexMap<FieldName, FieldDefinition>,
    521  - types: &HashMap<Qualified<CustomTypeName>, TypeRepresentation>,
     520 + object_types: &HashMap<Qualified<CustomTypeName>, ObjectTypeRepresentation>,
    522 521   models: &IndexMap<Qualified<ModelName>, Model>,
    523 522   // type_representation: &TypeRepresentation,
    524 523  ) -> Result<ModelPredicate, Error> {
    skipped 101 lines
    626 625   }
    627 626   permissions::ModelPredicate::Relationship(RelationshipPredicate { name, predicate }) => {
    628 627   if let Some(nested_predicate) = predicate {
    629  - let object_type_representation =
    630  - get_model_object_type_representation(types, &model.data_type, &model.name)?;
     628 + let object_type_representation = get_model_object_type_representation(
     629 + object_types,
     630 + &model.data_type,
     631 + &model.name,
     632 + )?;
    631 633   let relationship_field_name = mk_name(&name.0)?;
    632 634   let relationship = &object_type_representation
    633 635   .relationships
    skipped 65 lines
    699 701   subgraph,
    700 702   data_connectors,
    701 703   &target_model.type_fields,
    702  - types,
     704 + object_types,
    703 705   models,
    704 706   )?;
    705 707   
    skipped 31 lines
    737 739   subgraph,
    738 740   data_connectors,
    739 741   fields,
    740  - types,
     742 + object_types,
    741 743   models,
    742 744   )?;
    743 745   Ok(ModelPredicate::Not(Box::new(resolved_predicate)))
    skipped 7 lines
    751 753   subgraph,
    752 754   data_connectors,
    753 755   fields,
    754  - types,
     756 + object_types,
    755 757   models,
    756 758   )?);
    757 759   }
    skipped 8 lines
    766 768   subgraph,
    767 769   data_connectors,
    768 770   fields,
    769  - types,
     771 + object_types,
    770 772   models,
    771 773   )?);
    772 774   }
    skipped 7 lines
    780 782   subgraph: &str,
    781 783   model_permissions: &ModelPermissionsV1,
    782 784   data_connectors: &data_connectors::DataConnectors,
    783  - types: &HashMap<Qualified<CustomTypeName>, TypeRepresentation>,
     785 + object_types: &HashMap<Qualified<CustomTypeName>, ObjectTypeRepresentation>,
    784 786   models: &IndexMap<Qualified<ModelName>, Model>,
    785 787  ) -> Result<HashMap<Role, SelectPermission>, Error> {
    786 788   let mut validated_permissions = HashMap::new();
    skipped 6 lines
    793 795   subgraph,
    794 796   data_connectors,
    795 797   &model.type_fields,
    796  - types,
     798 + object_types,
    797 799   models,
    798 800   )
    799 801   .map(FilterPermission::Filter)?,
    skipped 475 lines
    1275 1277   model: &mut Model,
    1276 1278   subgraph: &str,
    1277 1279   data_connectors: &data_connectors::DataConnectors,
    1278  - types: &HashMap<Qualified<CustomTypeName>, TypeRepresentation>,
     1280 + object_types: &HashMap<Qualified<CustomTypeName>, ObjectTypeRepresentation>,
     1281 + scalar_types: &HashMap<Qualified<CustomTypeName>, ScalarTypeRepresentation>,
    1279 1282   data_connector_type_mappings: &DataConnectorTypeMappings,
    1280 1283  ) -> Result<(), Error> {
    1281 1284   if model.source.is_some() {
    skipped 29 lines
    1311 1314   &model.arguments,
    1312 1315   &model_source.argument_mapping,
    1313 1316   &source_collection.arguments,
    1314  - types,
     1317 + object_types,
     1318 + scalar_types,
    1315 1319   )
    1316 1320   .map_err(|err| Error::ModelCollectionArgumentMappingError {
    1317 1321   data_connector_name: qualified_data_connector_name.clone(),
    skipped 15 lines
    1333 1337   type_mapping_to_collect,
    1334 1338   data_connector_type_mappings,
    1335 1339   &qualified_data_connector_name,
    1336  - types,
     1340 + object_types,
     1341 + scalar_types,
    1337 1342   &mut type_mappings,
    1338 1343   )
    1339 1344   .map_err(|error| Error::ModelTypeMappingCollectionError {
    skipped 36 lines
    1376 1381   };
    1377 1382   
    1378 1383   let model_object_type =
    1379  - get_model_object_type_representation(types, &model.data_type, &model.name)?;
     1384 + get_model_object_type_representation(object_types, &model.data_type, &model.name)?;
    1380 1385   
    1381 1386   if let Some(global_id_source) = &mut model.global_id_source {
    1382 1387   for global_id_field in &model_object_type.global_id_fields {
    skipped 45 lines
    1428 1433  /// `data_type`, it will throw an error if the type is not found to be an object
    1429 1434  /// or if the model has an unknown data type.
    1430 1435  pub(crate) fn get_model_object_type_representation<'s>(
    1431  - types: &'s HashMap<Qualified<CustomTypeName>, TypeRepresentation>,
     1436 + object_types: &'s HashMap<Qualified<CustomTypeName>, ObjectTypeRepresentation>,
    1432 1437   data_type: &Qualified<CustomTypeName>,
    1433 1438   model_name: &Qualified<ModelName>,
    1434 1439  ) -> Result<&'s ObjectTypeRepresentation, crate::metadata::resolved::error::Error> {
    1435  - let object_type_representation = match types.get(data_type) {
    1436  - Some(TypeRepresentation::Object(object_type_representation)) => {
    1437  - Ok(object_type_representation)
    1438  - }
    1439  - Some(type_rep) => Err(Error::InvalidTypeRepresentation {
    1440  - model_name: model_name.clone(),
    1441  - type_representation: type_rep.clone(),
    1442  - }),
     1440 + match object_types.get(data_type) {
     1441 + Some(object_type_representation) => Ok(object_type_representation),
    1443 1442   None => Err(Error::UnknownModelDataType {
    1444 1443   model_name: model_name.clone(),
    1445 1444   data_type: data_type.clone(),
    1446 1445   }),
    1447  - }?;
    1448  - Ok(object_type_representation)
     1446 + }
    1449 1447  }
    1450 1448   
  • ■ ■ ■ ■ ■ ■
    v3/crates/engine/src/metadata/resolved/types.rs
    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 name
    488  - let data_connector_context = data_connectors
    489  - .data_connectors
    490  - .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 type
    501  - if !data_connector_context
    502  - .schema
    503  - .object_types
    504  - .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 fields
    535  - for comparable_field in object_boolean_expression.comparable_fields.iter() {
    536  - if !object_type_representation
    537  - .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 operators
    550  - 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 fields
    561  - 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 config
    570  - let graphql_config = object_boolean_expression
    571  - .graphql
    572  - .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_expression
    588  - .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
  • ■ ■ ■ ■ ■ ■
    v3/crates/engine/src/schema/types/input_type.rs
    1 1  use crate::{
    2 2   metadata::resolved::{
    3 3   subgraph::{Qualified, QualifiedBaseType, QualifiedTypeName, QualifiedTypeReference},
    4  - types::{mk_name, FieldDefinition, ObjectTypeRepresentation, TypeRepresentation},
     4 + types::{
     5 + get_type_representation, mk_name, FieldDefinition, ObjectTypeRepresentation,
     6 + TypeRepresentation,
     7 + },
    5 8   },
    6 9   schema::{types, GDS},
    7 10  };
    skipped 63 lines
    71 74   builder: &mut gql_schema::Builder<GDS>,
    72 75   gds_type_name: &Qualified<CustomTypeName>,
    73 76  ) -> Result<gql_schema::RegisteredTypeName, Error> {
    74  - let type_representation = gds.metadata.types.get(gds_type_name).ok_or_else(|| {
    75  - crate::schema::Error::InternalTypeNotFound {
    76  - type_name: gds_type_name.clone(),
    77  - }
    78  - })?;
    79  - match type_representation {
     77 + match get_type_representation(
     78 + gds_type_name,
     79 + &gds.metadata.object_types,
     80 + &gds.metadata.scalar_types,
     81 + )
     82 + .map_err(|_| crate::schema::Error::InternalTypeNotFound {
     83 + type_name: gds_type_name.clone(),
     84 + })? {
    80 85   TypeRepresentation::Object(ObjectTypeRepresentation {
    81 86   graphql_input_type_name,
    82 87   ..
    skipped 6 lines
    89 94   })?
    90 95   .clone(),
    91 96   })),
    92  - TypeRepresentation::ScalarType(graphql_type_name) => {
     97 + TypeRepresentation::Scalar(graphql_type_name) => {
    93 98   Ok(builder.register_type(super::TypeId::ScalarType {
    94 99   gds_type_name: gds_type_name.clone(),
    95 100   graphql_type_name: graphql_type_name
    skipped 42 lines
    138 143   type_name: &Qualified<CustomTypeName>,
    139 144   graphql_type_name: &ast::TypeName,
    140 145  ) -> Result<gql_schema::TypeInfo<GDS>, Error> {
    141  - let type_representation =
     146 + let object_type_representation =
    142 147   gds.metadata
    143  - .types
     148 + .object_types
    144 149   .get(type_name)
    145 150   .ok_or_else(|| Error::InternalTypeNotFound {
    146 151   type_name: type_name.clone(),
    147 152   })?;
    148 153   
    149 154   let graphql_type_name = graphql_type_name.clone();
    150  - 
    151  - let object_type_representation = match &type_representation {
    152  - TypeRepresentation::ScalarType { .. } => Err(Error::InternalUnsupported {
    153  - summary: format!(
    154  - "a scalar type {} mapping to non-scalar GraphQL types",
    155  - type_name.clone()
    156  - ),
    157  - }),
    158  - TypeRepresentation::Object(object_type_representation) => Ok(object_type_representation),
    159  - }?;
    160 155   
    161 156   let input_fields =
    162 157   input_object_type_input_fields(gds, builder, &object_type_representation.fields)?;
    skipped 11 lines
  • ■ ■ ■ ■ ■ ■
    v3/crates/engine/src/schema/types/output_type.rs
    skipped 17 lines
    18 18   Qualified, QualifiedBaseType, QualifiedTypeName, QualifiedTypeReference,
    19 19  };
    20 20  use crate::metadata::resolved::types::{
    21  - ObjectTypeRepresentation, ResolvedObjectApolloFederationConfig,
    22  -};
    23  -use crate::metadata::resolved::{
    24  - self,
    25  - types::{mk_name, TypeRepresentation},
     21 + get_type_representation, ObjectTypeRepresentation, ResolvedObjectApolloFederationConfig,
     22 + TypeRepresentation,
    26 23  };
     24 +use crate::metadata::resolved::{self, types::mk_name};
    27 25  use crate::schema::commands::generate_command_argument;
    28 26  use crate::schema::query_root::select_many::generate_select_many_arguments;
    29 27  use crate::schema::{mk_deprecation_status, permissions};
    skipped 94 lines
    124 122   builder: &mut gql_schema::Builder<GDS>,
    125 123   gds_type: &Qualified<CustomTypeName>,
    126 124  ) -> Result<gql_schema::RegisteredTypeName, Error> {
    127  - let type_representation = gds.metadata.types.get(gds_type).ok_or_else(|| {
    128  - crate::schema::Error::InternalTypeNotFound {
    129  - type_name: gds_type.clone(),
    130  - }
    131  - })?;
    132  - match type_representation {
     125 + match get_type_representation(
     126 + gds_type,
     127 + &gds.metadata.object_types,
     128 + &gds.metadata.scalar_types,
     129 + )
     130 + .map_err(|_| crate::schema::Error::InternalTypeNotFound {
     131 + type_name: gds_type.clone(),
     132 + })? {
    133 133   TypeRepresentation::Object(object_type_representation) => {
    134 134   Ok(builder.register_type(super::TypeId::OutputType {
    135 135   gds_type_name: gds_type.clone(),
    skipped 6 lines
    142 142   .clone(),
    143 143   }))
    144 144   }
    145  - TypeRepresentation::ScalarType(scalar_type_representation) => {
     145 + TypeRepresentation::Scalar(scalar_type_representation) => {
    146 146   Ok(builder.register_type(super::TypeId::ScalarType {
    147 147   gds_type_name: gds_type.clone(),
    148 148   graphql_type_name: scalar_type_representation
    skipped 16 lines
    165 165   QualifiedBaseType::Named(qualified_type_name) => match qualified_type_name {
    166 166   QualifiedTypeName::Inbuilt(_) => Ok(super::TypeKind::Scalar), // Inbuilt types are all scalars
    167 167   QualifiedTypeName::Custom(type_name) => {
    168  - let type_rep =
    169  - gds.metadata
    170  - .types
    171  - .get(type_name)
    172  - .ok_or(Error::InternalTypeNotFound {
     168 + match gds.metadata.object_types.get(type_name) {
     169 + Some(_) => Ok(super::TypeKind::Object),
     170 + None => match gds.metadata.scalar_types.get(type_name) {
     171 + Some(_) => Ok(super::TypeKind::Scalar),
     172 + None => Err(Error::InternalTypeNotFound {
    173 173   type_name: type_name.to_owned(),
    174  - })?;
    175  - match type_rep {
    176  - TypeRepresentation::Object(_) => Ok(super::TypeKind::Object),
    177  - TypeRepresentation::ScalarType(_) => Ok(super::TypeKind::Scalar),
     174 + }),
     175 + },
    178 176   }
    179 177   }
    180 178   },
    skipped 220 lines
    401 399   type_name: &Qualified<CustomTypeName>,
    402 400   graphql_type_name: &ast::TypeName,
    403 401  ) -> Result<gql_schema::TypeInfo<GDS>, Error> {
    404  - let type_representation =
     402 + let object_type_representation =
    405 403   gds.metadata
    406  - .types
     404 + .object_types
    407 405   .get(type_name)
    408 406   .ok_or_else(|| Error::InternalTypeNotFound {
    409 407   type_name: type_name.clone(),
    skipped 1 lines
    411 409   
    412 410   let graphql_type_name = graphql_type_name.clone();
    413 411   
    414  - match &type_representation {
    415  - resolved::types::TypeRepresentation::Object(object_type_representation) => {
    416  - let mut object_type_fields =
    417  - object_type_fields(gds, builder, type_name, object_type_representation)?;
    418  - let directives = match &object_type_representation.apollo_federation_config {
    419  - Some(apollo_federation_config) => {
    420  - generate_apollo_federation_directives(apollo_federation_config)
    421  - }
    422  - None => Vec::new(),
    423  - };
    424  - if object_type_representation.global_id_fields.is_empty() {
    425  - Ok(gql_schema::TypeInfo::Object(gql_schema::Object::new(
    426  - builder,
    427  - graphql_type_name,
    428  - object_type_representation.description.clone(),
    429  - object_type_fields,
    430  - BTreeMap::new(),
    431  - directives,
    432  - )))
    433  - } else {
    434  - // Generate the Global object `id` field and insert it
    435  - // into the `object_type_fields`.
    436  - let mut interfaces = BTreeMap::new();
    437  - let global_id_field_name = lang_graphql::mk_name!("id");
    438  - let global_id_field = gql_schema::Field::<GDS>::new(
    439  - global_id_field_name.clone(),
    440  - object_type_representation.description.clone(),
    441  - Annotation::Output(super::OutputAnnotation::GlobalIDField {
    442  - global_id_fields: object_type_representation.global_id_fields.to_vec(),
    443  - }),
    444  - get_output_type(gds, builder, &ID_TYPE_REFERENCE)?,
    445  - BTreeMap::new(),
    446  - gql_schema::DeprecationStatus::NotDeprecated,
    447  - );
    448  - if object_type_fields
    449  - .insert(
    450  - global_id_field_name.clone(),
    451  - builder.conditional_namespaced(
    452  - global_id_field,
    453  - permissions::get_node_interface_annotations(object_type_representation),
    454  - ),
    455  - )
    456  - .is_some()
    457  - {
    458  - return Err(Error::DuplicateFieldNameGeneratedInObjectType {
    459  - field_name: global_id_field_name,
    460  - type_name: type_name.clone(),
    461  - });
    462  - }
    463  - let node_interface_annotations =
    464  - permissions::get_node_interface_annotations(object_type_representation);
    465  - if !node_interface_annotations.is_empty() {
    466  - interfaces.insert(
    467  - node_interface_type(builder),
    468  - builder.conditional_namespaced((), node_interface_annotations),
    469  - );
    470  - }
    471  - let directives = match &object_type_representation.apollo_federation_config {
    472  - Some(apollo_federation_config) => {
    473  - generate_apollo_federation_directives(apollo_federation_config)
    474  - }
    475  - None => Vec::new(),
    476  - };
    477  - Ok(gql_schema::TypeInfo::Object(gql_schema::Object::new(
    478  - builder,
    479  - graphql_type_name,
    480  - object_type_representation.description.clone(),
    481  - object_type_fields,
    482  - interfaces,
    483  - directives,
    484  - )))
    485  - }
     412 + let mut object_type_fields =
     413 + object_type_fields(gds, builder, type_name, object_type_representation)?;
     414 + let directives = match &object_type_representation.apollo_federation_config {
     415 + Some(apollo_federation_config) => {
     416 + generate_apollo_federation_directives(apollo_federation_config)
     417 + }
     418 + None => Vec::new(),
     419 + };
     420 + if object_type_representation.global_id_fields.is_empty() {
     421 + Ok(gql_schema::TypeInfo::Object(gql_schema::Object::new(
     422 + builder,
     423 + graphql_type_name,
     424 + object_type_representation.description.clone(),
     425 + object_type_fields,
     426 + BTreeMap::new(),
     427 + directives,
     428 + )))
     429 + } else {
     430 + // Generate the Global object `id` field and insert it
     431 + // into the `object_type_fields`.
     432 + let mut interfaces = BTreeMap::new();
     433 + let global_id_field_name = lang_graphql::mk_name!("id");
     434 + let global_id_field = gql_schema::Field::<GDS>::new(
     435 + global_id_field_name.clone(),
     436 + object_type_representation.description.clone(),
     437 + Annotation::Output(super::OutputAnnotation::GlobalIDField {
     438 + global_id_fields: object_type_representation.global_id_fields.to_vec(),
     439 + }),
     440 + get_output_type(gds, builder, &ID_TYPE_REFERENCE)?,
     441 + BTreeMap::new(),
     442 + gql_schema::DeprecationStatus::NotDeprecated,
     443 + );
     444 + if object_type_fields
     445 + .insert(
     446 + global_id_field_name.clone(),
     447 + builder.conditional_namespaced(
     448 + global_id_field,
     449 + permissions::get_node_interface_annotations(object_type_representation),
     450 + ),
     451 + )
     452 + .is_some()
     453 + {
     454 + return Err(Error::DuplicateFieldNameGeneratedInObjectType {
     455 + field_name: global_id_field_name,
     456 + type_name: type_name.clone(),
     457 + });
     458 + }
     459 + let node_interface_annotations =
     460 + permissions::get_node_interface_annotations(object_type_representation);
     461 + if !node_interface_annotations.is_empty() {
     462 + interfaces.insert(
     463 + node_interface_type(builder),
     464 + builder.conditional_namespaced((), node_interface_annotations),
     465 + );
    486 466   }
    487  - resolved::types::TypeRepresentation::ScalarType { .. } => Err(Error::InternalUnsupported {
    488  - summary: format!(
    489  - "a scalar type {} mapping to non-scalar GraphQL types",
    490  - type_name.clone()
    491  - ),
    492  - }),
     467 + let directives = match &object_type_representation.apollo_federation_config {
     468 + Some(apollo_federation_config) => {
     469 + generate_apollo_federation_directives(apollo_federation_config)
     470 + }
     471 + None => Vec::new(),
     472 + };
     473 + Ok(gql_schema::TypeInfo::Object(gql_schema::Object::new(
     474 + builder,
     475 + graphql_type_name,
     476 + object_type_representation.description.clone(),
     477 + object_type_fields,
     478 + interfaces,
     479 + directives,
     480 + )))
    493 481   }
    494 482  }
    495 483   
    skipped 4 lines
    500 488   gds: &'s GDS,
    501 489   gds_type: &Qualified<CustomTypeName>,
    502 490  ) -> Result<&'s ObjectTypeRepresentation, crate::schema::Error> {
    503  - let type_representation = gds.metadata.types.get(gds_type).ok_or_else(|| {
     491 + gds.metadata.object_types.get(gds_type).ok_or_else(|| {
    504 492   crate::schema::Error::InternalTypeNotFound {
    505 493   type_name: gds_type.clone(),
    506 494   }
    507  - })?;
    508  - match type_representation {
    509  - TypeRepresentation::Object(object_type_representation) => Ok(object_type_representation),
    510  - TypeRepresentation::ScalarType { .. } => {
    511  - Err(crate::schema::Error::ExpectedTypeToBeObject {
    512  - type_name: gds_type.clone(),
    513  - })
    514  - }
    515  - }
     495 + })
    516 496  }
    517 497   
    518 498  pub(crate) fn representations_type_reference(
    skipped 7 lines
  • ■ ■ ■ ■ ■ ■
    v3/crates/engine/src/schema/types/scalar_type.rs
    1  -use crate::{
    2  - metadata::resolved::{subgraph::Qualified, types::TypeRepresentation},
    3  - schema::GDS,
    4  -};
     1 +use crate::{metadata::resolved::subgraph::Qualified, schema::GDS};
    5 2  use lang_graphql::ast::common as ast;
    6 3  use lang_graphql::schema as gql_schema;
    7 4  use open_dds::types::CustomTypeName;
    skipped 5 lines
    13 10   type_name: &Qualified<CustomTypeName>,
    14 11   graphql_type_name: &ast::TypeName,
    15 12  ) -> Result<gql_schema::TypeInfo<GDS>, Error> {
    16  - let type_representation =
     13 + let scalar_type_representation =
    17 14   gds.metadata
    18  - .types
     15 + .scalar_types
    19 16   .get(type_name)
    20 17   .ok_or_else(|| Error::InternalTypeNotFound {
    21 18   type_name: type_name.clone(),
    skipped 1 lines
    23 20   
    24 21   let graphql_type_name = graphql_type_name.clone();
    25 22   
    26  - match &type_representation {
    27  - TypeRepresentation::Object(_object_type_representation) => {
    28  - Err(Error::InternalUnsupported {
    29  - summary: format!(
    30  - "a non-scalar type {} mapping to scalar GraphQL types",
    31  - type_name.clone()
    32  - ),
    33  - })
    34  - }
    35  - TypeRepresentation::ScalarType(scalar_type_representation) => {
    36  - Ok(gql_schema::TypeInfo::Scalar(gql_schema::Scalar {
    37  - name: graphql_type_name,
    38  - description: scalar_type_representation.description.clone(),
    39  - directives: Vec::new(),
    40  - }))
    41  - }
    42  - }
     23 + Ok(gql_schema::TypeInfo::Scalar(gql_schema::Scalar {
     24 + name: graphql_type_name,
     25 + description: scalar_type_representation.description.clone(),
     26 + directives: Vec::new(),
     27 + }))
    43 28  }
    44 29   
  • ■ ■ ■ ■
    v3/justfile
    skipped 127 lines
    128 128  watch-local: start-docker-test-deps start-docker-run-deps
    129 129   RUST_LOG=DEBUG \
    130 130   cargo watch \
    131  - -x test \
     131 + -x 'test --message-format short' \
    132 132   -x 'clippy --no-deps' \
    133 133   -x 'run --bin engine -- \
    134 134   --otlp-endpoint http://localhost:4317 \
    skipped 22 lines
Please wait...
Page is in error, reload to recover