fn build_refetch_operation()

in compiler/crates/relay-transforms/src/refetchable_fragment/node_query_generator.rs [25:160]


fn build_refetch_operation(
    schema: &SDLSchema,
    schema_config: &SchemaConfig,
    fragment: &Arc<FragmentDefinition>,
    query_name: StringKey,
    variables_map: &VariableMap,
) -> DiagnosticsResult<Option<RefetchRoot>> {
    let id_name = schema_config.node_interface_id_field;

    let node_interface_id = schema.get_type(CONSTANTS.node_type_name).and_then(|type_| {
        if let Type::Interface(id) = type_ {
            Some(id)
        } else {
            None
        }
    });

    match node_interface_id {
        None => Ok(None),
        Some(node_interface_id) => {
            let eligible = match fragment.type_condition {
                Type::Interface(id) => {
                    id == node_interface_id
                        || schema
                            .interface(id)
                            .implementing_objects
                            .iter()
                            .all(|&object_id| {
                                schema
                                    .object(object_id)
                                    .interfaces
                                    .iter()
                                    .any(|interface_id| *interface_id == node_interface_id)
                            })
                }
                Type::Object(id) => schema
                    .object(id)
                    .interfaces
                    .iter()
                    .any(|interface_id| *interface_id == node_interface_id),
                Type::Union(id) => schema.union(id).members.iter().all(|&object_id| {
                    schema
                        .object(object_id)
                        .interfaces
                        .iter()
                        .any(|interface_id| *interface_id == node_interface_id)
                }),
                _ => false,
            };
            if !eligible {
                return Ok(None);
            }

            // Check if the fragment type have an `id` field
            let should_generate_inline_fragment_on_node = schema
                .named_field(fragment.type_condition, id_name)
                .is_none();

            let query_type = schema.query_type().unwrap();
            let (node_field_id, id_arg) =
                get_node_field_id_and_id_arg(schema, query_type, fragment)?;
            let node_interface = schema.interface(node_interface_id);
            let id_field_id = *node_interface
                .fields
                .iter()
                .find(|&&id| schema.field(id).name.item == id_name)
                .unwrap_or_else(|| {
                    panic!("Expected `Node` to contain a field named `{:}`.", id_name)
                });

            let fragment = Arc::new(FragmentDefinition {
                directives: build_fragment_metadata_as_directive(
                    fragment,
                    RefetchableMetadata {
                        operation_name: query_name,
                        path: vec![CONSTANTS.node_field_name],
                        identifier_field: Some(id_name),
                    },
                ),
                used_global_variables: build_used_global_variables(
                    variables_map,
                    &fragment.variable_definitions,
                )?,
                variable_definitions: fragment.variable_definitions.clone(),
                selections: enforce_selections_with_id_field(
                    fragment,
                    schema,
                    id_field_id,
                    schema_config.node_interface_id_field,
                    if should_generate_inline_fragment_on_node {
                        Some(node_interface_id)
                    } else {
                        None
                    },
                ),
                ..fragment.as_ref().clone()
            });
            let mut variable_definitions = build_operation_variable_definitions(&fragment);
            if let Some(id_argument) = variable_definitions.named(id_name) {
                return Err(vec![Diagnostic::error(
                    ValidationMessage::RefetchableFragmentOnNodeWithExistingID {
                        fragment_name: fragment.name.item,
                    },
                    id_argument.name.location,
                )]);
            }

            variable_definitions.push(VariableDefinition {
                name: WithLocation::new(fragment.name.location, id_name),
                type_: id_arg.type_.non_null(),
                default_value: None,
                directives: vec![],
            });
            Ok(Some(RefetchRoot {
                variable_definitions,
                selections: vec![Selection::LinkedField(Arc::new(LinkedField {
                    alias: None,
                    definition: WithLocation::new(fragment.name.location, node_field_id),
                    arguments: vec![Argument {
                        name: WithLocation::new(fragment.name.location, id_arg.name),
                        value: WithLocation::new(
                            fragment.name.location,
                            Value::Variable(Variable {
                                name: WithLocation::new(fragment.name.location, id_name),
                                type_: id_arg.type_.non_null(),
                            }),
                        ),
                    }],
                    directives: vec![],
                    selections: vec![build_fragment_spread(&fragment)],
                }))],
                fragment,
            }))
        }
    }
}