diff --git a/compiler/crates/graphql-ir/src/errors.rs b/compiler/crates/graphql-ir/src/errors.rs index 69b06c2a56ddb..2dad6c926c5a2 100644 --- a/compiler/crates/graphql-ir/src/errors.rs +++ b/compiler/crates/graphql-ir/src/errors.rs @@ -46,24 +46,6 @@ pub enum ValidationMessage { #[error("Expected operation to have a name (e.g. 'query ')")] ExpectedOperationName(), - #[error( - "{pluralized_string} in graphql tags must start with the module name ('{module_name}') and end with '{operation_type_suffix}'. Got '{operation_name}' instead." - )] - InvalidOperationName { - pluralized_string: String, - module_name: String, - operation_type_suffix: String, - operation_name: String, - }, - - #[error( - "Fragments in graphql tags must start with the module name ('{module_name}'). Got '{fragment_name}' instead." - )] - InvalidFragmentName { - module_name: String, - fragment_name: String, - }, - #[error("The schema does not support '{0}' operations")] UnsupportedOperation(OperationKind), @@ -714,121 +696,8 @@ pub enum ValidationMessage { )] RequiredRawResponseTypeOnNoInline { fragment_name: StringKey }, - // Updatable queries and fragments - #[error("The @updatable directive is yet allowed on fragments.")] - UpdatableNotAllowedOnFragments, - - #[error( - "The @{disallowed_directive_name} directive is not allowed in updatable {outer_type_plural}." - )] - UpdatableDisallowOtherDirectives { - disallowed_directive_name: StringKey, - outer_type_plural: &'static str, - }, - - #[error( - "Only fragments decorated with the @assignable directive can be spread within updatable {outer_type_plural}. You can try adding the @assignable directive to the fragment {fragment_name}." - )] - UpdatableOnlyAssignableFragmentSpreads { - outer_type_plural: &'static str, - fragment_name: StringKey, - }, - - #[error( - "Within updatable {outer_type_plural}, if an assignable fragment is spread on a linked field, the fragment's type (`{fragment_type}`) must be equal to or a subtype of the field's type (`{field_type}`)." - )] - UpdatableSpreadOfAssignableFragmentMustBeEqualToOrSubtypeOfOuterField { - outer_type_plural: &'static str, - fragment_type: StringKey, - field_type: StringKey, - }, - - // Note: conditions do not have a location, hence this awkward phrasing - #[error( - "Within updatable {outer_type_plural}, the directives @include and @skip are not allowed. The directive was found in {operation_or_fragment_name}." - )] - UpdatableNoConditions { - outer_type_plural: &'static str, - operation_or_fragment_name: StringKey, - }, - - #[error( - "Within updatable {outer_type_plural}, if a linked field contains an inline fragment spread, it must contain only inline fragment spreads." - )] - UpdatableOnlyInlineFragments { outer_type_plural: &'static str }, - - #[error( - "Within updatable {outer_type_plural}, inline fragments are only allowed on interfaces or unions, not on concrete types. In updatable queries, each inline fragment must have a type conditions, so no inline fragment would make sense here." - )] - UpdatableInlineFragmentsOnlyOnInterfacesOrUnions { outer_type_plural: &'static str }, - - #[error( - "Within updatable {outer_type_plural}, each inline fragment spread must have a type condition. An inline fragment without a type condition was among the selections of {parent_field_type}." - )] - UpdatableInlineFragmentsRequireTypeConditions { - outer_type_plural: &'static str, - parent_field_type: StringKey, - }, - - #[error( - "Within updatable {outer_type_plural}, each inline fragment spread must have a type condition narrowing the type to a unique concrete type. `{type_condition}` is not a concrete type." - )] - UpdatableInlineFragmentsTypeConditionsMustBeConcrete { - outer_type_plural: &'static str, - type_condition: StringKey, - }, - - #[error( - "Within updatable {outer_type_plural}, a single linked field cannot have multiple inline fragments with the same type condition. However, within {parent_field_alias_or_name}, there were multiple inline fragments narrowing the type to `{type_condition}`." - )] - UpdatablePreviouslyEncounteredTypeCondition { - outer_type_plural: &'static str, - type_condition: StringKey, - parent_field_alias_or_name: StringKey, - }, - - #[error( - "Within updatable {outer_type_plural}, each inline fragment spread must contain an unaliased typename field. However, within {parent_field_alias_or_name}, there are inline fragments without typename fields." - )] - UpdatableInlineFragmentsMustHaveTypenameFields { - outer_type_plural: &'static str, - parent_field_alias_or_name: StringKey, - }, - - #[error( - "Within updatable {outer_type_plural}, an inline fragment cannot occur immediately within another inline fragment. Found within {operation_or_fragment_name}. This is because all inline fragments must have type conditions and narrow the type from an abstract type to a concrete type." - )] - UpdatableNoNestedInlineFragments { - outer_type_plural: &'static str, - operation_or_fragment_name: StringKey, - }, - - // Assignable fragments - #[error( - "Assignable fragments should contain only a single, unaliased __typename field with no directives." - )] - AssignableOnlyUnaliasedTypenameFieldWithNoDirectives, - - #[error("The @{disallowed_directive_name} directive is not allowed on assignable fragments.")] - AssignableDisallowOtherDirectives { - disallowed_directive_name: StringKey, - }, - #[error("No fields can have an alias that start with two underscores.")] NoDoubleUnderscoreAlias, - - #[error("Top-level spreads of assignable fragments are not supported.")] - AssignableNoTopLevelFragmentSpreads, - - #[error( - "The @{disallowed_directive_name} directive is not allowed on assignable fragment spreads." - )] - AssignableFragmentSpreadNoOtherDirectives { - disallowed_directive_name: StringKey, - }, - - #[error("Assignable fragments cannot appear within inline fragments")] - AssignableFragmentSpreadNotWithinInlineFragment, } #[derive(Clone, Debug, Error, Eq, PartialEq, Ord, PartialOrd, Hash)] diff --git a/compiler/crates/js-config-loader/src/error.rs b/compiler/crates/js-config-loader/src/error.rs index 18e4ab93b0d05..09c9518ba9c45 100644 --- a/compiler/crates/js-config-loader/src/error.rs +++ b/compiler/crates/js-config-loader/src/error.rs @@ -10,7 +10,6 @@ use std::process::Output; use thiserror::Error; -/// Fixed set of validation errors with custom display messages #[derive(Debug)] pub struct ConfigError { pub path: PathBuf, diff --git a/compiler/crates/relay-transforms/src/assignable_fragment_spread/errors.rs b/compiler/crates/relay-transforms/src/assignable_fragment_spread/errors.rs new file mode 100644 index 0000000000000..63a2dc1808dc4 --- /dev/null +++ b/compiler/crates/relay-transforms/src/assignable_fragment_spread/errors.rs @@ -0,0 +1,124 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +use intern::string_key::StringKey; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum ValidationMessage { + #[error( + "The @{disallowed_directive_name} directive is not allowed on assignable fragment spreads." + )] + AssignableFragmentSpreadNoOtherDirectives { + disallowed_directive_name: StringKey, + }, + + #[error("Assignable fragments cannot appear within inline fragments")] + AssignableFragmentSpreadNotWithinInlineFragment, + + #[error("Top-level spreads of assignable fragments are not supported.")] + AssignableNoTopLevelFragmentSpreads, + + #[error( + "Assignable fragments should contain only a single, unaliased __typename field with no directives." + )] + AssignableOnlyUnaliasedTypenameFieldWithNoDirectives, + + #[error("The @{disallowed_directive_name} directive is not allowed on assignable fragments.")] + AssignableDisallowOtherDirectives { + disallowed_directive_name: StringKey, + }, + + #[error( + "Only fragments decorated with the @assignable directive can be spread within updatable {outer_type_plural}. You can try adding the @assignable directive to the fragment {fragment_name}." + )] + UpdatableOnlyAssignableFragmentSpreads { + outer_type_plural: &'static str, + fragment_name: StringKey, + }, + + #[error( + "Within updatable {outer_type_plural}, if an assignable fragment is spread on a linked field, the fragment's type (`{fragment_type}`) must be equal to or a subtype of the field's type (`{field_type}`)." + )] + UpdatableSpreadOfAssignableFragmentMustBeEqualToOrSubtypeOfOuterField { + outer_type_plural: &'static str, + fragment_type: StringKey, + field_type: StringKey, + }, + + // Updatable queries and fragments + #[error("The @updatable directive is yet allowed on fragments.")] + UpdatableNotAllowedOnFragments, + + #[error( + "The @{disallowed_directive_name} directive is not allowed in updatable {outer_type_plural}." + )] + UpdatableDisallowOtherDirectives { + disallowed_directive_name: StringKey, + outer_type_plural: &'static str, + }, + + // Note: conditions do not have a location, hence this awkward phrasing + #[error( + "Within updatable {outer_type_plural}, the directives @include and @skip are not allowed. The directive was found in {operation_or_fragment_name}." + )] + UpdatableNoConditions { + outer_type_plural: &'static str, + operation_or_fragment_name: StringKey, + }, + + #[error( + "Within updatable {outer_type_plural}, if a linked field contains an inline fragment spread, it must contain only inline fragment spreads." + )] + UpdatableOnlyInlineFragments { outer_type_plural: &'static str }, + + #[error( + "Within updatable {outer_type_plural}, inline fragments are only allowed on interfaces or unions, not on concrete types. In updatable queries, each inline fragment must have a type conditions, so no inline fragment would make sense here." + )] + UpdatableInlineFragmentsOnlyOnInterfacesOrUnions { outer_type_plural: &'static str }, + + #[error( + "Within updatable {outer_type_plural}, each inline fragment spread must have a type condition. An inline fragment without a type condition was among the selections of {parent_field_type}." + )] + UpdatableInlineFragmentsRequireTypeConditions { + outer_type_plural: &'static str, + parent_field_type: StringKey, + }, + + #[error( + "Within updatable {outer_type_plural}, each inline fragment spread must have a type condition narrowing the type to a unique concrete type. `{type_condition}` is not a concrete type." + )] + UpdatableInlineFragmentsTypeConditionsMustBeConcrete { + outer_type_plural: &'static str, + type_condition: StringKey, + }, + + #[error( + "Within updatable {outer_type_plural}, a single linked field cannot have multiple inline fragments with the same type condition. However, within {parent_field_alias_or_name}, there were multiple inline fragments narrowing the type to `{type_condition}`." + )] + UpdatablePreviouslyEncounteredTypeCondition { + outer_type_plural: &'static str, + type_condition: StringKey, + parent_field_alias_or_name: StringKey, + }, + + #[error( + "Within updatable {outer_type_plural}, each inline fragment spread must contain an unaliased typename field. However, within {parent_field_alias_or_name}, there are inline fragments without typename fields." + )] + UpdatableInlineFragmentsMustHaveTypenameFields { + outer_type_plural: &'static str, + parent_field_alias_or_name: StringKey, + }, + + #[error( + "Within updatable {outer_type_plural}, an inline fragment cannot occur immediately within another inline fragment. Found within {operation_or_fragment_name}. This is because all inline fragments must have type conditions and narrow the type from an abstract type to a concrete type." + )] + UpdatableNoNestedInlineFragments { + outer_type_plural: &'static str, + operation_or_fragment_name: StringKey, + }, +} diff --git a/compiler/crates/relay-transforms/src/assignable_fragment_spread/mod.rs b/compiler/crates/relay-transforms/src/assignable_fragment_spread/mod.rs index 3ead2730b0eaf..0a4711f330a8c 100644 --- a/compiler/crates/relay-transforms/src/assignable_fragment_spread/mod.rs +++ b/compiler/crates/relay-transforms/src/assignable_fragment_spread/mod.rs @@ -5,15 +5,22 @@ * LICENSE file in the root directory of this source tree. */ +mod errors; +mod validate_assignable_directive; +mod validate_updatable_directive; + +use self::errors::ValidationMessage; use common::{Diagnostic, DiagnosticsResult, Location, NamedItem, WithLocation}; use graphql_ir::{ Condition, FragmentSpread, InlineFragment, LinkedField, OperationDefinition, Program, - ScalarField, Selection, Transformed, Transformer, ValidationMessage, + ScalarField, Selection, Transformed, Transformer, }; use intern::string_key::{Intern, StringKey}; use lazy_static::lazy_static; use schema::{FieldID, Schema}; use std::sync::Arc; +pub use validate_assignable_directive::validate_assignable_directive; +pub use validate_updatable_directive::{validate_updatable_directive, UPDATABLE_DIRECTIVE_NAME}; lazy_static! { static ref ASSIGNABLE_DIRECTIVE: StringKey = "assignable".intern(); diff --git a/compiler/crates/relay-transforms/src/validations/validate_assignable_directive/mod.rs b/compiler/crates/relay-transforms/src/assignable_fragment_spread/validate_assignable_directive.rs similarity index 98% rename from compiler/crates/relay-transforms/src/validations/validate_assignable_directive/mod.rs rename to compiler/crates/relay-transforms/src/assignable_fragment_spread/validate_assignable_directive.rs index 9b650e0d79221..b347bf476be62 100644 --- a/compiler/crates/relay-transforms/src/validations/validate_assignable_directive/mod.rs +++ b/compiler/crates/relay-transforms/src/assignable_fragment_spread/validate_assignable_directive.rs @@ -5,8 +5,9 @@ * LICENSE file in the root directory of this source tree. */ +use super::ValidationMessage; use common::{Diagnostic, DiagnosticsResult, NamedItem}; -use graphql_ir::{FragmentDefinition, Program, Selection, ValidationMessage, Validator}; +use graphql_ir::{FragmentDefinition, Program, Selection, Validator}; use intern::string_key::{Intern, StringKey}; use lazy_static::lazy_static; use schema::Schema; diff --git a/compiler/crates/relay-transforms/src/validations/validate_updatable_directive/mod.rs b/compiler/crates/relay-transforms/src/assignable_fragment_spread/validate_updatable_directive.rs similarity index 98% rename from compiler/crates/relay-transforms/src/validations/validate_updatable_directive/mod.rs rename to compiler/crates/relay-transforms/src/assignable_fragment_spread/validate_updatable_directive.rs index edc567477d515..d827dec4ef9a4 100644 --- a/compiler/crates/relay-transforms/src/validations/validate_updatable_directive/mod.rs +++ b/compiler/crates/relay-transforms/src/assignable_fragment_spread/validate_updatable_directive.rs @@ -7,12 +7,13 @@ use std::collections::HashSet; -use super::validate_assignable_directive::ASSIGNABLE_DIRECTIVE_NAME; +use super::ValidationMessage; +use crate::assignable_fragment_spread::validate_assignable_directive::ASSIGNABLE_DIRECTIVE_NAME; use common::{Diagnostic, DiagnosticsResult, Location, NamedItem}; use errors::{validate, validate_map}; use graphql_ir::{ Condition, Directive, Field, FragmentDefinition, FragmentSpread, InlineFragment, LinkedField, - OperationDefinition, Program, Selection, ValidationMessage, Validator, + OperationDefinition, Program, Selection, Validator, }; use intern::string_key::{Intern, StringKey}; use lazy_static::lazy_static; diff --git a/compiler/crates/relay-transforms/src/lib.rs b/compiler/crates/relay-transforms/src/lib.rs index 817411c9f8c1d..2de2840f077c2 100644 --- a/compiler/crates/relay-transforms/src/lib.rs +++ b/compiler/crates/relay-transforms/src/lib.rs @@ -78,7 +78,10 @@ pub use crate::errors::ValidationMessage; pub use applied_fragment_name::get_applied_fragment_name; pub use apply_fragment_arguments::apply_fragment_arguments; pub use apply_transforms::{apply_transforms, Programs}; -pub use assignable_fragment_spread::transform_assignable_fragment_spreads; +pub use assignable_fragment_spread::{ + transform_assignable_fragment_spreads, validate_assignable_directive, + validate_updatable_directive, UPDATABLE_DIRECTIVE_NAME, +}; pub use client_edges::{ client_edges, preserve_client_edge_backing_ids, preserve_client_edge_selections, ClientEdgeMetadata, CLIENT_EDGE_GENERATED_FRAGMENT_KEY, CLIENT_EDGE_METADATA_KEY, diff --git a/compiler/crates/relay-transforms/src/validations/mod.rs b/compiler/crates/relay-transforms/src/validations/mod.rs index fdc01f1287a92..7fa652e59cce9 100644 --- a/compiler/crates/relay-transforms/src/validations/mod.rs +++ b/compiler/crates/relay-transforms/src/validations/mod.rs @@ -9,7 +9,6 @@ mod deprecated_fields; mod disallow_circular_no_inline_fragments; mod disallow_reserved_aliases; mod disallow_typename_on_root; -pub(crate) mod validate_assignable_directive; mod validate_connections; mod validate_global_variables; mod validate_module_names; @@ -21,13 +20,11 @@ mod validate_selection_conflict; mod validate_server_only_directives; mod validate_unused_fragment_variables; mod validate_unused_variables; -mod validate_updatable_directive; pub use deprecated_fields::{deprecated_fields, deprecated_fields_for_executable_definition}; pub use disallow_circular_no_inline_fragments::disallow_circular_no_inline_fragments; pub use disallow_reserved_aliases::disallow_reserved_aliases; pub use disallow_typename_on_root::disallow_typename_on_root; -pub use validate_assignable_directive::validate_assignable_directive; pub use validate_connections::validate_connections; pub use validate_global_variables::validate_global_variables; pub use validate_module_names::{extract_module_name, validate_module_names}; @@ -39,4 +36,3 @@ pub use validate_selection_conflict::validate_selection_conflict; pub use validate_server_only_directives::validate_server_only_directives; pub use validate_unused_fragment_variables::validate_unused_fragment_variables; pub use validate_unused_variables::validate_unused_variables; -pub use validate_updatable_directive::{validate_updatable_directive, UPDATABLE_DIRECTIVE_NAME}; diff --git a/compiler/crates/relay-transforms/src/validations/validate_module_names/mod.rs b/compiler/crates/relay-transforms/src/validations/validate_module_names/mod.rs index f000720db5f46..55b411d03aa32 100644 --- a/compiler/crates/relay-transforms/src/validations/validate_module_names/mod.rs +++ b/compiler/crates/relay-transforms/src/validations/validate_module_names/mod.rs @@ -5,11 +5,12 @@ * LICENSE file in the root directory of this source tree. */ +mod extract_module_name; + use common::{Diagnostic, DiagnosticsResult}; -use graphql_ir::{FragmentDefinition, OperationDefinition, Program, ValidationMessage, Validator}; +use graphql_ir::{FragmentDefinition, OperationDefinition, Program, Validator}; use graphql_syntax::OperationKind; - -mod extract_module_name; +use thiserror::Error; pub fn validate_module_names(program: &Program) -> DiagnosticsResult<()> { (ValidateModuleNames {}).validate_program(program) @@ -74,3 +75,24 @@ impl Validator for ValidateModuleNames { Ok(()) } } + +#[derive(Debug, Error)] +pub enum ValidationMessage { + #[error( + "{pluralized_string} in graphql tags must start with the module name ('{module_name}') and end with '{operation_type_suffix}'. Got '{operation_name}' instead." + )] + InvalidOperationName { + pluralized_string: String, + module_name: String, + operation_type_suffix: String, + operation_name: String, + }, + + #[error( + "Fragments in graphql tags must start with the module name ('{module_name}'). Got '{fragment_name}' instead." + )] + InvalidFragmentName { + module_name: String, + fragment_name: String, + }, +}