Skip to content

Commit

Permalink
Validate against using Relay Resolvers in typesafe updaters
Browse files Browse the repository at this point in the history
Reviewed By: davidmccabe

Differential Revision: D41325936

fbshipit-source-id: 3b8c6e339ad91823ebdd4d7b89e0971ab47e4df4
  • Loading branch information
captbaritone authored and facebook-github-bot committed Nov 16, 2022
1 parent 11d22f0 commit 98e7700
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 1 deletion.
Expand Up @@ -59,6 +59,11 @@ pub enum ValidationMessage {
outer_type_plural: &'static str,
},

#[error(
"Fields defined using Relay Resolvers are not not allowed within @updatable operations."
)]
UpdatableDisallowRealyResolvers,

#[error("The directives @include and @skip are not allowed within {outer_type_plural}.")]
UpdatableNoConditions { outer_type_plural: &'static str },

Expand Down
Expand Up @@ -23,6 +23,7 @@ use graphql_ir::InlineFragment;
use graphql_ir::LinkedField;
use graphql_ir::OperationDefinition;
use graphql_ir::Program;
use graphql_ir::ScalarField;
use graphql_ir::Selection;
use graphql_ir::Validator;
use intern::string_key::Intern;
Expand All @@ -33,6 +34,7 @@ use schema::Schema;
use super::ValidationMessage;
use super::ASSIGNABLE_DIRECTIVE;
use super::UPDATABLE_DIRECTIVE;
use crate::RELAY_RESOLVER_DIRECTIVE_NAME;

lazy_static! {
static ref ALLOW_LISTED_DIRECTIVES: Vec<DirectiveName> = vec![
Expand Down Expand Up @@ -309,10 +311,43 @@ impl<'a> Validator for UpdatableDirective<'a> {
}
}

fn validate_scalar_field(&mut self, field: &ScalarField) -> DiagnosticsResult<()> {
let field_def = self.program.schema.field(field.definition.item);
if field_def
.directives
.named(*RELAY_RESOLVER_DIRECTIVE_NAME)
.is_some()
{
return Err(vec![
Diagnostic::error(
ValidationMessage::UpdatableDisallowRealyResolvers,
field.definition.location,
)
.annotate("The field is defined here:", field_def.name.location),
]);
}
self.default_validate_scalar_field(field)
}

fn validate_linked_field(&mut self, linked_field: &LinkedField) -> DiagnosticsResult<()> {
let fragment_spreads = filter_fragment_spreads(linked_field.selections.iter());
let inline_fragments = filter_inline_fragments(linked_field.selections.iter());

let field_def = self.program.schema.field(linked_field.definition.item);
if field_def
.directives
.named(*RELAY_RESOLVER_DIRECTIVE_NAME)
.is_some()
{
return Err(vec![
Diagnostic::error(
ValidationMessage::UpdatableDisallowRealyResolvers,
linked_field.definition.location,
)
.annotate("The field is defined here:", field_def.name.location),
]);
}

validate!(
self.validate_fragment_spreads_with_parent(linked_field, fragment_spreads),
self.validate_inline_fragments_with_parent(linked_field, inline_fragments.collect()),
Expand Down
@@ -0,0 +1,29 @@
==================================== INPUT ====================================
# expected-to-throw
query resolverQuery @updatable {
resolver_field {
id
}
}

# %extensions%

extend type Query {
resolver_field: User @relay_resolver(import_path: "ResolverModule")
}
==================================== ERROR ====================================
✖︎ Fields defined using Relay Resolvers are not not allowed within @updatable operations.

resolver-linked.invalid.graphql:3:3
2 │ query resolverQuery @updatable {
3 │ resolver_field {
│ ^^^^^^^^^^^^^^
4 │ id

ℹ︎ The field is defined here:

<generated>:2:5
1 │ # expected-to-throw
2 │ query resolverQuery @updatable {
│ ^^^^^^^^^^^^^^
3 │ resolver_field {
@@ -0,0 +1,12 @@
# expected-to-throw
query resolverQuery @updatable {
resolver_field {
id
}
}

# %extensions%

extend type Query {
resolver_field: User @relay_resolver(import_path: "ResolverModule")
}
@@ -0,0 +1,27 @@
==================================== INPUT ====================================
# expected-to-throw
query resolverQuery @updatable {
resolver_field
}

# %extensions%

extend type Query {
resolver_field: Boolean @relay_resolver(import_path: "ResolverModule")
}
==================================== ERROR ====================================
✖︎ Fields defined using Relay Resolvers are not not allowed within @updatable operations.

resolver-scalar.invalid.graphql:3:3
2 │ query resolverQuery @updatable {
3 │ resolver_field
│ ^^^^^^^^^^^^^^
4 │ }

ℹ︎ The field is defined here:

<generated>:2:5
1 │ # expected-to-throw
2 │ query resolverQuery @updatable {
│ ^^^^^^^^^^^^^^
3 │ resolver_field
@@ -0,0 +1,10 @@
# expected-to-throw
query resolverQuery @updatable {
resolver_field
}

# %extensions%

extend type Query {
resolver_field: Boolean @relay_resolver(import_path: "ResolverModule")
}
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<cbb760c92a070de1f59e662694d57dbb>>
* @generated SignedSource<<e30e35197db61c49dd28d620b5bd0b15>>
*/

mod updatable_directive;
Expand Down Expand Up @@ -124,6 +124,20 @@ fn non_assignable_fragment_spreads_invalid() {
test_fixture(transform_fixture, "non-assignable-fragment-spreads.invalid.graphql", "updatable_directive/fixtures/non-assignable-fragment-spreads.invalid.expected", input, expected);
}

#[test]
fn resolver_linked_invalid() {
let input = include_str!("updatable_directive/fixtures/resolver-linked.invalid.graphql");
let expected = include_str!("updatable_directive/fixtures/resolver-linked.invalid.expected");
test_fixture(transform_fixture, "resolver-linked.invalid.graphql", "updatable_directive/fixtures/resolver-linked.invalid.expected", input, expected);
}

#[test]
fn resolver_scalar_invalid() {
let input = include_str!("updatable_directive/fixtures/resolver-scalar.invalid.graphql");
let expected = include_str!("updatable_directive/fixtures/resolver-scalar.invalid.expected");
test_fixture(transform_fixture, "resolver-scalar.invalid.graphql", "updatable_directive/fixtures/resolver-scalar.invalid.expected", input, expected);
}

#[test]
fn skip_invalid() {
let input = include_str!("updatable_directive/fixtures/skip.invalid.graphql");
Expand Down

0 comments on commit 98e7700

Please sign in to comment.