Skip to content

Commit

Permalink
Merge pull request #328 from supabase/or/builtin-directives
Browse files Browse the repository at this point in the history
Support @Skip and @include
  • Loading branch information
olirice authored Feb 27, 2023
2 parents 169a63a + a640af9 commit 1c15518
Show file tree
Hide file tree
Showing 5 changed files with 745 additions and 29 deletions.
67 changes: 43 additions & 24 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ where
&query_field.selection_set,
fragment_definitions,
&type_name,
variables,
)?;

for selection_field in selection_fields {
Expand Down Expand Up @@ -407,6 +408,7 @@ where
&query_field.selection_set,
fragment_definitions,
&type_name,
variables,
)?;

for selection_field in selection_fields {
Expand Down Expand Up @@ -502,6 +504,7 @@ where
&query_field.selection_set,
fragment_definitions,
&type_name,
variables,
)?;

for selection_field in selection_fields {
Expand Down Expand Up @@ -1081,35 +1084,37 @@ where
&query_field.selection_set,
fragment_definitions,
&type_name,
variables,
)?;

for selection_field in selection_fields {
match field_map.get(selection_field.name.as_ref()) {
None => return Err("unknown field in connection".to_string()),
Some(f) => {
builder_fields.push(match &f.type_.unmodified_type() {
__Type::Edge(_) => ConnectionSelection::Edge(to_edge_builder(
f,
selection_field,
fragment_definitions,
variables,
)?),
__Type::PageInfo(_) => ConnectionSelection::PageInfo(
to_page_info_builder(f, selection_field, fragment_definitions)?,
),
Some(f) => builder_fields.push(match &f.type_.unmodified_type() {
__Type::Edge(_) => ConnectionSelection::Edge(to_edge_builder(
f,
selection_field,
fragment_definitions,
variables,
)?),
__Type::PageInfo(_) => ConnectionSelection::PageInfo(to_page_info_builder(
f,
selection_field,
fragment_definitions,
variables,
)?),

_ => match f.name().as_ref() {
"totalCount" => ConnectionSelection::TotalCount {
alias: alias_or_name(selection_field),
},
"__typename" => ConnectionSelection::Typename {
alias: alias_or_name(selection_field),
typename: xtype.name().unwrap(),
},
_ => return Err("unexpected field type on connection".to_string()),
_ => match f.name().as_ref() {
"totalCount" => ConnectionSelection::TotalCount {
alias: alias_or_name(selection_field),
},
})
}
"__typename" => ConnectionSelection::Typename {
alias: alias_or_name(selection_field),
typename: xtype.name().unwrap(),
},
_ => return Err("unexpected field type on connection".to_string()),
},
}),
}
}
Ok(ConnectionBuilder {
Expand Down Expand Up @@ -1138,6 +1143,7 @@ fn to_page_info_builder<'a, T>(
field: &__Field,
query_field: &graphql_parser::query::Field<'a, T>,
fragment_definitions: &Vec<FragmentDefinition<'a, T>>,
variables: &serde_json::Value,
) -> Result<PageInfoBuilder, String>
where
T: Text<'a> + Eq + AsRef<str>,
Expand All @@ -1158,6 +1164,7 @@ where
&query_field.selection_set,
fragment_definitions,
&type_name,
variables,
)?;

for selection_field in selection_fields {
Expand Down Expand Up @@ -1218,6 +1225,7 @@ where
&query_field.selection_set,
fragment_definitions,
&type_name,
variables,
)?;

for selection_field in selection_fields {
Expand Down Expand Up @@ -1326,8 +1334,12 @@ where
false => None,
};

let selection_fields =
normalize_selection_set(&query_field.selection_set, fragment_definitions, &type_name)?;
let selection_fields = normalize_selection_set(
&query_field.selection_set,
fragment_definitions,
&type_name,
variables,
)?;

for selection_field in selection_fields {
match field_map.get(selection_field.name.as_ref()) {
Expand Down Expand Up @@ -1545,6 +1557,7 @@ impl __Schema {
enum_value: &__EnumValue,
query_field: &graphql_parser::query::Field<'a, T>,
fragment_definitions: &Vec<FragmentDefinition<'a, T>>,
variables: &serde_json::Value,
) -> Result<__EnumValueBuilder, String>
where
T: Text<'a> + Eq + AsRef<str>,
Expand All @@ -1553,6 +1566,7 @@ impl __Schema {
&query_field.selection_set,
fragment_definitions,
&"__EnumValue".to_string(),
variables,
)?;

let mut builder_fields = vec![];
Expand Down Expand Up @@ -1603,6 +1617,7 @@ impl __Schema {
&query_field.selection_set,
fragment_definitions,
&"__InputValue".to_string(),
variables,
)?;

let mut builder_fields = vec![];
Expand Down Expand Up @@ -1665,6 +1680,7 @@ impl __Schema {
&query_field.selection_set,
fragment_definitions,
&"__Field".to_string(),
variables,
)?;

let mut builder_fields = vec![];
Expand Down Expand Up @@ -1792,6 +1808,7 @@ impl __Schema {
&query_field.selection_set,
fragment_definitions,
&"__Type".to_string(),
variables,
)?;

let mut builder_fields = vec![];
Expand Down Expand Up @@ -1884,6 +1901,7 @@ impl __Schema {
enum_value,
selection_field,
fragment_definitions,
variables,
)?;
f_builders.push(f_builder)
}
Expand Down Expand Up @@ -1979,6 +1997,7 @@ impl __Schema {
&query_field.selection_set,
fragment_definitions,
&type_name,
variables,
)?;

for selection_field in selection_fields {
Expand Down
131 changes: 126 additions & 5 deletions src/parser_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ where
pub fn normalize_selection_set<'a, 'b, T>(
selection_set: &'b SelectionSet<'a, T>,
fragment_definitions: &'b Vec<FragmentDefinition<'a, T>>,
type_name: &String, // for inline fragments
type_name: &String, // for inline fragments
variables: &serde_json::Value, // for directives
) -> Result<Vec<&'b Field<'a, T>>, String>
where
T: Text<'a> + Eq + AsRef<str>,
Expand All @@ -26,25 +27,140 @@ where

for selection in &selection_set.items {
let sel = selection;
match normalize_selection(sel, fragment_definitions, type_name) {
match normalize_selection(sel, fragment_definitions, type_name, variables) {
Ok(sels) => selections.extend(sels),
Err(err) => return Err(err),
}
}
Ok(selections)
}

/// Combines @skip and @include
pub fn selection_is_skipped<'a, 'b, T>(
query_selection: &'b Selection<'a, T>,
variables: &serde_json::Value,
) -> Result<bool, String>
where
T: Text<'a> + Eq + AsRef<str>,
{
let directives = match query_selection {
Selection::Field(x) => &x.directives,
Selection::FragmentSpread(x) => &x.directives,
Selection::InlineFragment(x) => &x.directives,
};

if directives.len() > 0 {
for directive in directives {
let directive_name = directive.name.as_ref();
match directive_name {
"skip" => {
if directive.arguments.len() != 1 {
return Err(format!("Incorrect arguments to directive @skip"));
}
let arg = &directive.arguments[0];
if arg.0.as_ref() != "if" {
return Err(format!("Unknown argument to @skip: {}", arg.0.as_ref()));
}

// the argument to @skip(if: <value>)
match &arg.1 {
Value::Boolean(x) => {
if *x {
return Ok(true);
}
}
Value::Variable(var_name) => {
let var = variables.get(var_name.as_ref());
match var {
None => {
return Err("Value for \"if\" in @skip directive is required"
.to_string())
}
Some(val) => match val {
serde_json::Value::Bool(bool_val) => {
if *bool_val {
// skip immediately
return Ok(true);
}
}
_ => {
return Err(
"Value for \"if\" in @skip directive is required"
.to_string(),
);
}
},
}
}
_ => (),
}
}
"include" => {
if directive.arguments.len() != 1 {
return Err(format!("Incorrect arguments to directive @include"));
}
let arg = &directive.arguments[0];
if arg.0.as_ref() != "if" {
return Err(format!("Unknown argument to @include: {}", arg.0.as_ref()));
}

// the argument to @include(if: <value>)
match &arg.1 {
Value::Boolean(x) => {
if !*x {
return Ok(true);
}
}
Value::Variable(var_name) => {
let var = variables.get(var_name.as_ref());
match var {
None => {
return Err(
"Value for \"if\" in @include directive is required"
.to_string(),
)
}
Some(val) => match val {
serde_json::Value::Bool(bool_val) => {
if !bool_val {
return Ok(true);
}
}
_ => {
return Err(
"Value for \"if\" in @include directive is required"
.to_string(),
);
}
},
}
}
_ => (),
}
}
_ => return Err(format!("Unknown directive {}", directive_name)),
}
}
}
Ok(false)
}

/// Normalizes literal selections, fragment spreads, and inline fragments
pub fn normalize_selection<'a, 'b, T>(
query_selection: &'b Selection<'a, T>,
fragment_definitions: &'b Vec<FragmentDefinition<'a, T>>,
type_name: &String, // for inline fragments
type_name: &String, // for inline fragments
variables: &serde_json::Value, // for directives
) -> Result<Vec<&'b Field<'a, T>>, String>
where
T: Text<'a> + Eq + AsRef<str>,
{
let mut selections: Vec<&Field<'a, T>> = vec![];

if selection_is_skipped(query_selection, variables)? {
return Ok(selections);
}

match query_selection {
Selection::Field(field) => {
selections.push(field);
Expand Down Expand Up @@ -73,8 +189,12 @@ where
};

// TODO handle directives?
let frag_selections =
normalize_selection_set(&frag_def.selection_set, fragment_definitions, type_name);
let frag_selections = normalize_selection_set(
&frag_def.selection_set,
fragment_definitions,
type_name,
variables,
);
match frag_selections {
Ok(sels) => selections.extend(sels.iter()),
Err(err) => return Err(err),
Expand All @@ -93,6 +213,7 @@ where
&inline_fragment.selection_set,
fragment_definitions,
type_name,
variables,
)?;
selections.extend(infrag_selections.iter());
}
Expand Down
2 changes: 2 additions & 0 deletions src/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ where
&selection_set,
&fragment_definitions,
&query_type.name().unwrap(),
variables,
) {
Ok(selections) => selections,
Err(err) => {
Expand Down Expand Up @@ -328,6 +329,7 @@ where
&selection_set,
&fragment_definitions,
&mutation_type.name().unwrap(),
variables,
) {
Ok(selections) => selections,
Err(err) => {
Expand Down
Loading

0 comments on commit 1c15518

Please sign in to comment.