use crate::query_planner::federation_spec::directives::FederationDirective;
use crate::query_planner::federation_spec::directives::InaccessibleDirective;
use crate::query_planner::utils::schema_transformer::Transformed;
use crate::query_planner::utils::schema_transformer::TransformedValue;
use crate::query_planner::utils::schema_transformer::SchemaTransformer;
use graphql_tools::parser::query::Text;
use graphql_tools::parser::schema::*;
pub(crate) struct PruneInaccessible;
impl PruneInaccessible {
pub fn prune(schema: &Document<'static, String>) -> Document<'static, String> {
let mut transformer = PruneInaccessible {};
let result = transformer
.transform_document(schema)
.replace_or_else(|| schema.clone());
result
}
pub(crate) fn has_inaccessible_directive<'a, T: Text<'a> + Clone>(
directives: &Vec<Directive<'a, T>>,
) -> bool {
directives
.iter()
.any(|d| d.name == InaccessibleDirective::directive_name().into())
}
}
impl<'a, T: Text<'a> + Clone> SchemaTransformer<'a, T> for PruneInaccessible {
fn transform_document(
&mut self,
document: &Document<'a, T>,
) -> TransformedValue<Document<'a, T>> {
let new_doc = Document::new(
document
.definitions
.iter()
.filter(|def| match def {
Definition::SchemaDefinition(_) => true,
Definition::TypeDefinition(TypeDefinition::Object(obj)) => {
!Self::has_inaccessible_directive(&obj.directives)
}
Definition::TypeDefinition(TypeDefinition::Interface(interface)) => {
!Self::has_inaccessible_directive(&interface.directives)
}
Definition::TypeDefinition(TypeDefinition::Union(union)) => {
!Self::has_inaccessible_directive(&union.directives)
}
Definition::TypeDefinition(TypeDefinition::Scalar(scalar)) => {
!Self::has_inaccessible_directive(&scalar.directives)
}
Definition::TypeDefinition(TypeDefinition::Enum(enm)) => {
!Self::has_inaccessible_directive(&enm.directives)
}
Definition::TypeDefinition(TypeDefinition::InputObject(input)) => {
!Self::has_inaccessible_directive(&input.directives)
}
Definition::DirectiveDefinition(_) => true,
Definition::TypeExtension(_) => true,
})
.cloned()
.collect(),
);
self.default_transform_document(&new_doc)
}
fn transform_input_values(
&mut self,
values: &Vec<InputValue<'a, T>>,
) -> TransformedValue<Vec<InputValue<'a, T>>> {
TransformedValue::Replace(
values
.iter()
.filter(|v| !Self::has_inaccessible_directive(&v.directives))
.cloned()
.collect(),
)
}
fn transform_input_object_type(
&mut self,
input: &InputObjectType<'a, T>,
) -> Transformed<InputObjectType<'a, T>> {
Transformed::Replace(InputObjectType {
description: input.description.clone(),
directives: input.directives.clone(),
name: input.name.clone(),
fields: input
.fields
.iter()
.filter(|v| !Self::has_inaccessible_directive(&v.directives))
.cloned()
.collect(),
position: input.position,
})
}
fn transform_enum_type(&mut self, enum_type: &EnumType<'a, T>) -> Transformed<EnumType<'a, T>> {
Transformed::Replace(EnumType {
description: enum_type.description.clone(),
directives: enum_type.directives.clone(),
name: enum_type.name.clone(),
values: enum_type
.values
.iter()
.filter(|v| !Self::has_inaccessible_directive(&v.directives))
.cloned()
.collect(),
position: enum_type.position,
})
}
fn transform_fields(
&mut self,
fields: &Vec<Field<'a, T>>,
) -> TransformedValue<Vec<Field<'a, T>>> {
let new_fields = fields
.iter()
.filter(|v| !Self::has_inaccessible_directive(&v.directives))
.cloned()
.collect();
self.default_transform_fields(&new_fields)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::query_planner::utils::parsing::parse_schema;
#[test]
fn prune_preserves_root_type_names() {
let schema = parse_schema(
"
type Query { hello: String }
type Mutation { doThing: String }
type Subscription { onThing: String }
",
);
let result = PruneInaccessible::prune(&schema);
assert_eq!(result.query_type_name().map(|s| s.as_str()), Some("Query"));
assert_eq!(
result.mutation_type_name().map(|s| s.as_str()),
Some("Mutation")
);
assert_eq!(
result.subscription_type_name().map(|s| s.as_str()),
Some("Subscription")
);
}
#[test]
fn prune_preserves_renamed_root_type_names() {
let schema = parse_schema(
"
schema {
query: RootQuery
mutation: RootMutation
subscription: RootSubscription
}
type RootQuery { hello: String }
type RootMutation { doThing: String }
type RootSubscription { onThing: String }
",
);
let result = PruneInaccessible::prune(&schema);
assert_eq!(
result.query_type_name().map(|s| s.as_str()),
Some("RootQuery")
);
assert_eq!(
result.mutation_type_name().map(|s| s.as_str()),
Some("RootMutation")
);
assert_eq!(
result.subscription_type_name().map(|s| s.as_str()),
Some("RootSubscription")
);
}
}