Repository: apollographql/apollo-rs
Pull request #253 (2022-07-13 17:11:03)

fix(apollo-compiler): custom scalars with specifiedBy directive is an advice
This fixes "specifiedBy" directive in a scalar definition diagnostic into an "advice" rather than a "error".

Also renames test_dir "err" into "diagnostic".


Patch:
diff --git a/crates/apollo-compiler/src/diagnostics.rs b/crates/apollo-compiler/src/diagnostics.rs
index f4bcd38..9716bb3 100644
--- a/crates/apollo-compiler/src/diagnostics.rs
+++ b/crates/apollo-compiler/src/diagnostics.rs
@@ -242,19 +242,19 @@ pub struct BuiltInScalarDefinition {
 }
 
 #[derive(Diagnostic, Debug, Error, Clone, Hash, PartialEq, Eq)]
-#[error("Custom scalars must provide a scalar specification URL via the @specifiedBy directive")]
-#[diagnostic(code("apollo-compiler validation error"))]
+#[error("Custom scalars should provide a scalar specification URL via the @specifiedBy directive")]
+#[diagnostic(code("apollo-compiler validation advice"), severity(advice))]
 pub struct ScalarSpecificationURL {
     #[source_code]
     pub src: String,
 
-    #[label("add a @specifiedBy directive to this scalar definition")]
+    #[label("consider adding a @specifiedBy directive to this scalar definition")]
     pub scalar: SourceSpan,
 }
 
 #[derive(Diagnostic, Debug, Error, Clone, Hash, PartialEq, Eq)]
 #[error("values in an Enum Definition should be capitalized")]
-#[diagnostic(code("apollo-compiler validation error"), severity(warning))]
+#[diagnostic(code("apollo-compiler validation warning"), severity(warning))]
 pub struct CapitalizedValue {
     pub ty: String,
 
@@ -266,7 +266,7 @@ pub struct CapitalizedValue {
 }
 #[derive(Diagnostic, Debug, Error, Clone, Hash, PartialEq, Eq)]
 #[error("unused variable: `{}`", self.ty)]
-#[diagnostic(code("apollo-compiler validation error"), severity(warning))]
+#[diagnostic(code("apollo-compiler validation warning"), severity(warning))]
 pub struct UnusedVariable {
     pub ty: String,
 
diff --git a/crates/apollo-compiler/src/lib.rs b/crates/apollo-compiler/src/lib.rs
index f7c3317..4be027a 100644
--- a/crates/apollo-compiler/src/lib.rs
+++ b/crates/apollo-compiler/src/lib.rs
@@ -434,4 +434,97 @@ input Point2D {
 
         assert_eq!(fields, ["x", "y"]);
     }
+
+    #[test]
+    fn it_accesses_object_defitions() {
+        let input = r#"
+schema
+  @core(feature: "https://specs.apollo.dev/core/v0.1"),
+  @core(feature: "https://specs.apollo.dev/join/v0.1")
+{
+  query: Query
+  mutation: Mutation
+}
+
+directive @core(feature: String!) repeatable on SCHEMA
+
+directive @join__field(graph: join__Graph, requires: join__FieldSet, provides: join__FieldSet) on FIELD_DEFINITION
+
+directive @join__type(graph: join__Graph!, key: join__FieldSet) repeatable on OBJECT | INTERFACE
+
+directive @join__owner(graph: join__Graph!) on OBJECT | INTERFACE
+
+directive @join__graph(name: String!, url: String!) on ENUM_VALUE
+
+scalar join__FieldSet
+
+enum join__Graph {
+  ACCOUNTS @join__graph(name: "accounts" url: "http://localhost:4001")
+  INVENTORY @join__graph(name: "inventory" url: "http://localhost:4004")
+  PRODUCTS @join__graph(name: "products" url: "http://localhost:4003")
+  REVIEWS @join__graph(name: "reviews" url: "http://localhost:4002")
+}
+
+type Mutation {
+  createProduct(name: String, upc: ID!): Product @join__field(graph: PRODUCTS)
+  createReview(body: String, id: ID!, upc: ID!): Review @join__field(graph: REVIEWS)
+}
+
+type Product
+  @join__owner(graph: PRODUCTS)
+  @join__type(graph: PRODUCTS, key: "upc")
+  @join__type(graph: INVENTORY, key: "upc")
+  @join__type(graph: REVIEWS, key: "upc")
+{
+  inStock: Boolean @join__field(graph: INVENTORY)
+  name: String @join__field(graph: PRODUCTS)
+  price: Int @join__field(graph: PRODUCTS)
+  reviews: [Review] @join__field(graph: REVIEWS)
+  reviewsForAuthor(authorID: ID!): [Review] @join__field(graph: REVIEWS)
+  shippingEstimate: Int @join__field(graph: INVENTORY, requires: "price weight")
+  upc: String! @join__field(graph: PRODUCTS)
+  weight: Int @join__field(graph: PRODUCTS)
+}
+
+type Query {
+  me: User @join__field(graph: ACCOUNTS)
+  topProducts(first: Int = 5): [Product] @join__field(graph: PRODUCTS)
+}
+
+type Review
+  @join__owner(graph: REVIEWS)
+  @join__type(graph: REVIEWS, key: "id")
+{
+  author: User @join__field(graph: REVIEWS, provides: "username")
+  body: String @join__field(graph: REVIEWS)
+  id: ID! @join__field(graph: REVIEWS)
+  product: Product @join__field(graph: REVIEWS)
+}
+
+type User
+  @join__owner(graph: ACCOUNTS)
+  @join__type(graph: ACCOUNTS, key: "id")
+  @join__type(graph: REVIEWS, key: "id")
+{
+  id: ID! @join__field(graph: ACCOUNTS)
+  name: String @join__field(graph: ACCOUNTS)
+  reviews: [Review] @join__field(graph: REVIEWS)
+  username: String @join__field(graph: ACCOUNTS)
+}
+"#;
+
+        let ctx = ApolloCompiler::new(input);
+        let diagnostics = ctx.validate();
+
+        // the scalar warning diagnostic
+        assert_eq!(diagnostics.len(), 1);
+
+        for diagnostic in diagnostics {
+            println!("{}", diagnostic);
+        }
+
+        let object_types = ctx.object_types();
+        let object_names: Vec<_> = object_types.iter().map(|op| op.name()).collect();
+        assert_eq!(["Mutation", "Product", "Query", "Review", "User"], object_names.as_slice());
+    }
 }
diff --git a/crates/apollo-compiler/src/validation/scalars.rs b/crates/apollo-compiler/src/validation/scalars.rs
index 8f52c47..e090b66 100644
--- a/crates/apollo-compiler/src/validation/scalars.rs
+++ b/crates/apollo-compiler/src/validation/scalars.rs
@@ -39,4 +39,4 @@ pub fn check(db: &dyn SourceDatabase) -> Vec<ApolloDiagnostic> {
     }
 
     diagnostics
-}
+}
\ No newline at end of file


Test patch:
diff --git a/crates/apollo-compiler/src/tests.rs b/crates/apollo-compiler/src/tests.rs
index 0cb5784..fb3073c 100644
--- a/crates/apollo-compiler/src/tests.rs
+++ b/crates/apollo-compiler/src/tests.rs
@@ -28,31 +28,31 @@ fn compiler_tests() {
         let ctx = ApolloCompiler::new(text);
         let errors = ctx.validate();
         let ast = ctx.parse();
-        assert_errors_are_absent(&errors, path);
+        assert_diagnostics_are_absent(&errors, path);
         format!("{:?}", ast)
     });
 
-    dir_tests(&test_data_dir(), &["err"], "txt", |text, path| {
+    dir_tests(&test_data_dir(), &["diagnostics"], "txt", |text, path| {
         let ctx = ApolloCompiler::new(text);
         let diagnostics = ctx.validate();
-        assert_errors_are_present(&diagnostics, path);
+        assert_diagnostics_are_present(&diagnostics, path);
         format!("{:#?}", diagnostics)
     });
 }
 
-fn assert_errors_are_present(errors: &[ApolloDiagnostic], path: &Path) {
+fn assert_diagnostics_are_present(errors: &[ApolloDiagnostic], path: &Path) {
     assert!(
         !errors.is_empty(),
-        "There should be errors in the file {:?}",
+        "There should be diagnostics in the file {:?}",
         path.display()
     );
 }
 
-fn assert_errors_are_absent(errors: &[ApolloDiagnostic], path: &Path) {
+fn assert_diagnostics_are_absent(errors: &[ApolloDiagnostic], path: &Path) {
     if !errors.is_empty() {
         let formatted: Vec<String> = errors.iter().map(|e| format!("{:?}", e)).collect();
         println!("{:?}", formatted.join("\n"));
-        panic!("There should be no errors in the file {:?}", path.display(),);
+        panic!("There should be no diagnostics in the file {:?}", path.display(),);
     }
 }