From c8f525933550ce272250744fa51fcba1c854d4fa Mon Sep 17 00:00:00 2001 From: Yannick Utard Date: Wed, 14 Jun 2023 20:00:57 +0200 Subject: [PATCH 01/19] Add support for @composeDirective --- ...derated_document_from_schema_definition.rb | 2 +- lib/apollo-federation/schema.rb | 56 +++++++++++--- .../service_field_v2_spec.rb | 75 ++++++++++++++++--- 3 files changed, 112 insertions(+), 21 deletions(-) diff --git a/lib/apollo-federation/federated_document_from_schema_definition.rb b/lib/apollo-federation/federated_document_from_schema_definition.rb index 8d2f13d7d..cb58fabeb 100644 --- a/lib/apollo-federation/federated_document_from_schema_definition.rb +++ b/lib/apollo-federation/federated_document_from_schema_definition.rb @@ -101,7 +101,7 @@ def merge_directives(node, type) def directive_name(directive) if schema.federation_2? && !Schema::IMPORTED_DIRECTIVES.include?(directive[:name]) - "#{schema.link_namespace}__#{directive[:name]}" + "#{schema.default_link_namespace}__#{directive[:name]}" else directive[:name] end diff --git a/lib/apollo-federation/schema.rb b/lib/apollo-federation/schema.rb index ba7e6d24e..917ced53a 100644 --- a/lib/apollo-federation/schema.rb +++ b/lib/apollo-federation/schema.rb @@ -8,6 +8,7 @@ module ApolloFederation module Schema IMPORTED_DIRECTIVES = ['inaccessible', 'tag'].freeze + IMPORTED_DIRECTIVES_V2_1 = ['composeDirective'].freeze def self.included(klass) klass.extend(CommonMethods) @@ -16,9 +17,16 @@ def self.included(klass) module CommonMethods DEFAULT_LINK_NAMESPACE = 'federation' - def federation(version: '1.0', link: {}) + def federation(version: '1.0', default_link_namespace: nil, links: [], compose_directives: []) @federation_version = version - @link = { as: DEFAULT_LINK_NAMESPACE }.merge(link) + @default_link_namespace = default_link_namespace + @links = links + + if !federation_2_1? && compose_directives.any? + raise ArgumentError, 'composeDirective is available in Federation 2.1 and later' + end + + @compose_directives = compose_directives end def federation_version @@ -29,6 +37,10 @@ def federation_2? Gem::Version.new(federation_version.to_s) >= Gem::Version.new('2.0.0') end + def federation_2_1? + Gem::Version.new(federation_version.to_s) >= Gem::Version.new('2.1.0') + end + def federation_sdl(context: nil) document_from_schema = FederatedDocumentFromSchemaDefinition.new(self, context: context) @@ -37,8 +49,8 @@ def federation_sdl(context: nil) output end - def link_namespace - @link ? @link[:as] : find_inherited_value(:link_namespace) + def default_link_namespace + @default_link_namespace || find_inherited_value(:default_link_namespace, DEFAULT_LINK_NAMESPACE) end def query(new_query_object = nil) @@ -62,14 +74,40 @@ def original_query @orig_query_object || find_inherited_value(:original_query) end + def compose_directives + @compose_directives || find_inherited_value(:compose_directives, []) + end + + def links + @links || find_inherited_value(:links, []) + end + + def all_links + imported_directives = IMPORTED_DIRECTIVES + imported_directives += IMPORTED_DIRECTIVES_V2_1 if federation_2_1? + default_link = { + url: 'https://specs.apollo.dev/federation/v2.3', + import: imported_directives, + } + default_link[:as] = default_link_namespace if default_link_namespace != DEFAULT_LINK_NAMESPACE + [default_link, *links] + end + def federation_2_prefix - federation_namespace = ", as: \"#{link_namespace}\"" if link_namespace != DEFAULT_LINK_NAMESPACE + schema = "extend schema\n" - <<~SCHEMA - extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3"#{federation_namespace}, import: [#{(IMPORTED_DIRECTIVES.map { |directive| "\"@#{directive}\"" }).join(', ')}]) + all_links.each do |link| + schema += " @link(url: \"#{link[:url]}\"" + schema += ", as: \"#{link[:as]}\"" if link[:as] + schema += ", import: [#{link[:import].map { |d| "\"@#{d}\"" }.join(', ')}]" if link[:import] + schema += ")\n" + end + + compose_directives.each do |directive| + schema += " @composeDirective(name: \"@#{directive}\")\n" + end - SCHEMA + schema + "\n" end def schema_entities diff --git a/spec/apollo-federation/service_field_v2_spec.rb b/spec/apollo-federation/service_field_v2_spec.rb index bb7fd83f1..6c4963fb0 100644 --- a/spec/apollo-federation/service_field_v2_spec.rb +++ b/spec/apollo-federation/service_field_v2_spec.rb @@ -128,6 +128,59 @@ def execute_sdl(schema) ) end + it 'returns the federation SDL with compose directives for the schema' do + complexity_directive = Class.new(GraphQL::Schema::Directive) do + graphql_name 'complexity' + argument :fixed, Integer + description 'complexity of the field' + locations GraphQL::Schema::Directive::FIELD_DEFINITION + end + + product = Class.new(base_object) do + graphql_name 'Product' + + field :upc, String, null: false + end + + query_obj = Class.new(base_object) do + graphql_name 'Query' + + field :product, product, null: true, directives: { complexity_directive => { fixed: 1 } } + end + + schema = Class.new(base_schema) do + query query_obj + federation version: '2.3', + links: [{ + url: 'https://specs.example.com/federation/v2.3', + import: [complexity_directive.graphql_name], + }], + compose_directives: [complexity_directive.graphql_name] + end + + expect(execute_sdl(schema)).to match_sdl( + <<~GRAPHQL, + extend schema + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) + @link(url: "https://specs.example.com/federation/v2.3", import: ["@complexity"]) + @composeDirective(name: "@complexity") + + """ + complexity of the field + """ + directive @complexity(fixed: Int!) on FIELD_DEFINITION + + type Product { + upc: String! + } + + type Query { + product: Product @complexity(fixed: 1) + } + GRAPHQL + ) + end + it 'returns valid SDL for type extensions' do product = Class.new(base_object) do graphql_name 'Product' @@ -291,7 +344,7 @@ def execute_sdl(schema) schema = Class.new(base_schema) do query query_obj - federation version: '2.0', link: { as: 'fed2' } + federation version: '2.0', default_link_namespace: 'fed2' end expect(execute_sdl(schema)).to match_sdl( @@ -327,7 +380,7 @@ def execute_sdl(schema) schema = Class.new(base_schema) do query query_obj - federation version: '2.0', link: { as: 'fed2' } + federation version: '2.0', default_link_namespace: 'fed2' end expect(execute_sdl(schema)).to match_sdl( @@ -364,7 +417,7 @@ def execute_sdl(schema) schema = Class.new(base_schema) do query query_obj - federation version: '2.0', link: { as: 'fed2' } + federation version: '2.0', default_link_namespace: 'fed2' end expect(execute_sdl(schema)).to match_sdl( @@ -401,7 +454,7 @@ def execute_sdl(schema) schema = Class.new(base_schema) do query query_obj - federation version: '2.0', link: { as: 'fed2' } + federation version: '2.0', default_link_namespace: 'fed2' end expect(execute_sdl(schema)).to match_sdl( @@ -437,7 +490,7 @@ def execute_sdl(schema) schema = Class.new(base_schema) do query query_obj - federation version: '2.0', link: { as: 'fed2' } + federation version: '2.0', default_link_namespace: 'fed2' end expect(execute_sdl(schema)).to match_sdl( @@ -468,7 +521,7 @@ def execute_sdl(schema) schema = Class.new(base_schema) do orphan_types product - federation version: '2.0', link: { as: 'fed2' } + federation version: '2.0', default_link_namespace: 'fed2' end expect(execute_sdl(schema)).to match_sdl( @@ -495,13 +548,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do orphan_types product - federation version: '2.3', link: { as: 'fed2' } + federation version: '2.3', default_link_namespace: 'fed2' end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", as: "fed2", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", as: "fed2", import: ["@inaccessible", "@tag", "@composeDirective"]) type Product @fed2__interfaceObject @fed2__key(fields: "id") { id: ID! @@ -1421,7 +1474,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) type Product @federation__interfaceObject @federation__key(fields: "id") { id: ID! @@ -1973,7 +2026,7 @@ def self.visible?(context) end new_base_schema = Class.new(base_schema) do - federation version: '2.0', link: { as: 'fed2' } + federation version: '2.0', default_link_namespace: 'fed2' end schema = Class.new(new_base_schema) do @@ -2011,7 +2064,7 @@ def self.visible?(context) end new_base_schema = Class.new(base_schema) do - federation version: '2.0', link: { as: 'fed2' } + federation version: '2.0', default_link_namespace: 'fed2' query query_obj end From 2f19d5e5cc707cd77f87afa1e42e026bc0f0cdc2 Mon Sep 17 00:00:00 2001 From: Yannick Utard Date: Thu, 15 Jun 2023 10:01:22 +0200 Subject: [PATCH 02/19] Use schema.join --- lib/apollo-federation/schema.rb | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/lib/apollo-federation/schema.rb b/lib/apollo-federation/schema.rb index 917ced53a..73f718de6 100644 --- a/lib/apollo-federation/schema.rb +++ b/lib/apollo-federation/schema.rb @@ -94,20 +94,24 @@ def all_links end def federation_2_prefix - schema = "extend schema\n" + schema = ["extend schema"] all_links.each do |link| - schema += " @link(url: \"#{link[:url]}\"" - schema += ", as: \"#{link[:as]}\"" if link[:as] - schema += ", import: [#{link[:import].map { |d| "\"@#{d}\"" }.join(', ')}]" if link[:import] - schema += ")\n" + link_str = " @link(url: \"#{link[:url]}\"" + link_str += ", as: \"#{link[:as]}\"" if link[:as] + link_str += ", import: [#{link[:import].map { |d| "\"@#{d}\"" }.join(', ')}]" if link[:import] + link_str += ")" + schema << link_str end compose_directives.each do |directive| - schema += " @composeDirective(name: \"@#{directive}\")\n" + schema << " @composeDirective(name: \"@#{directive}\")" end - schema + "\n" + schema << '' + schema << '' + + schema.join("\n") end def schema_entities From 66d060d60c70bc40bf2a17f55df467095992110d Mon Sep 17 00:00:00 2001 From: Yannick Utard Date: Fri, 16 Jun 2023 10:30:44 +0200 Subject: [PATCH 03/19] Lint, add links test --- lib/apollo-federation/schema.rb | 4 +- .../service_field_v2_spec.rb | 39 +++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/lib/apollo-federation/schema.rb b/lib/apollo-federation/schema.rb index 73f718de6..893db3809 100644 --- a/lib/apollo-federation/schema.rb +++ b/lib/apollo-federation/schema.rb @@ -94,13 +94,13 @@ def all_links end def federation_2_prefix - schema = ["extend schema"] + schema = ['extend schema'] all_links.each do |link| link_str = " @link(url: \"#{link[:url]}\"" link_str += ", as: \"#{link[:as]}\"" if link[:as] link_str += ", import: [#{link[:import].map { |d| "\"@#{d}\"" }.join(', ')}]" if link[:import] - link_str += ")" + link_str += ')' schema << link_str end diff --git a/spec/apollo-federation/service_field_v2_spec.rb b/spec/apollo-federation/service_field_v2_spec.rb index 6c4963fb0..4c0ff67ca 100644 --- a/spec/apollo-federation/service_field_v2_spec.rb +++ b/spec/apollo-federation/service_field_v2_spec.rb @@ -128,6 +128,45 @@ def execute_sdl(schema) ) end + it 'returns the federation SDL with multiple links for the schema' do + product = Class.new(base_object) do + graphql_name 'Product' + + field :upc, String, null: false + end + + query_obj = Class.new(base_object) do + graphql_name 'Query' + + field :product, product, null: true + end + + schema = Class.new(base_schema) do + query query_obj + federation version: '2.3', + links: [{ + url: 'https://specs.example.com/federation/v2.3', + import: ['test'], + }] + end + + expect(execute_sdl(schema)).to match_sdl( + <<~GRAPHQL, + extend schema + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) + @link(url: "https://specs.example.com/federation/v2.3", import: ["@test"]) + + type Product { + upc: String! + } + + type Query { + product: Product + } + GRAPHQL + ) + end + it 'returns the federation SDL with compose directives for the schema' do complexity_directive = Class.new(GraphQL::Schema::Directive) do graphql_name 'complexity' From ede329be00ad5d1a3db1db40c87eed1e6e6efd80 Mon Sep 17 00:00:00 2001 From: Yannick Utard Date: Fri, 16 Jun 2023 10:43:01 +0200 Subject: [PATCH 04/19] Add required to argument --- spec/apollo-federation/service_field_v2_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/apollo-federation/service_field_v2_spec.rb b/spec/apollo-federation/service_field_v2_spec.rb index 4c0ff67ca..3ba30f0a2 100644 --- a/spec/apollo-federation/service_field_v2_spec.rb +++ b/spec/apollo-federation/service_field_v2_spec.rb @@ -170,7 +170,7 @@ def execute_sdl(schema) it 'returns the federation SDL with compose directives for the schema' do complexity_directive = Class.new(GraphQL::Schema::Directive) do graphql_name 'complexity' - argument :fixed, Integer + argument :fixed, Integer, required: true description 'complexity of the field' locations GraphQL::Schema::Directive::FIELD_DEFINITION end From 96ec2ca64f96f60d0ce0372b9cd0a206fad377b5 Mon Sep 17 00:00:00 2001 From: Yannick Utard Date: Tue, 1 Aug 2023 11:51:56 +0200 Subject: [PATCH 05/19] Drop minor version check --- lib/apollo-federation/schema.rb | 13 +-- .../service_field_v2_spec.rb | 96 +++++++++---------- 2 files changed, 49 insertions(+), 60 deletions(-) diff --git a/lib/apollo-federation/schema.rb b/lib/apollo-federation/schema.rb index 893db3809..5827684f3 100644 --- a/lib/apollo-federation/schema.rb +++ b/lib/apollo-federation/schema.rb @@ -7,8 +7,7 @@ module ApolloFederation module Schema - IMPORTED_DIRECTIVES = ['inaccessible', 'tag'].freeze - IMPORTED_DIRECTIVES_V2_1 = ['composeDirective'].freeze + IMPORTED_DIRECTIVES = ['inaccessible', 'tag', 'composeDirective'].freeze def self.included(klass) klass.extend(CommonMethods) @@ -21,11 +20,6 @@ def federation(version: '1.0', default_link_namespace: nil, links: [], compose_d @federation_version = version @default_link_namespace = default_link_namespace @links = links - - if !federation_2_1? && compose_directives.any? - raise ArgumentError, 'composeDirective is available in Federation 2.1 and later' - end - @compose_directives = compose_directives end @@ -37,10 +31,6 @@ def federation_2? Gem::Version.new(federation_version.to_s) >= Gem::Version.new('2.0.0') end - def federation_2_1? - Gem::Version.new(federation_version.to_s) >= Gem::Version.new('2.1.0') - end - def federation_sdl(context: nil) document_from_schema = FederatedDocumentFromSchemaDefinition.new(self, context: context) @@ -84,7 +74,6 @@ def links def all_links imported_directives = IMPORTED_DIRECTIVES - imported_directives += IMPORTED_DIRECTIVES_V2_1 if federation_2_1? default_link = { url: 'https://specs.apollo.dev/federation/v2.3', import: imported_directives, diff --git a/spec/apollo-federation/service_field_v2_spec.rb b/spec/apollo-federation/service_field_v2_spec.rb index 3ba30f0a2..fa3112e90 100644 --- a/spec/apollo-federation/service_field_v2_spec.rb +++ b/spec/apollo-federation/service_field_v2_spec.rb @@ -115,7 +115,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) type Product { upc: String! @@ -242,7 +242,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) type Product @federation__extends { upc: String! @@ -278,7 +278,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) type Position @federation__shareable { x: Int! @@ -315,7 +315,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) type Position @inaccessible { x: Int! @@ -352,7 +352,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) type Position @tag(name: "private") { x: Int! @@ -389,7 +389,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", as: "fed2", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", as: "fed2", import: ["@inaccessible", "@tag", "@composeDirective"]) type Product @fed2__extends { upc: String! @@ -425,7 +425,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", as: "fed2", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", as: "fed2", import: ["@inaccessible", "@tag", "@composeDirective"]) type Position @fed2__shareable { x: Int! @@ -462,7 +462,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", as: "fed2", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", as: "fed2", import: ["@inaccessible", "@tag", "@composeDirective"]) type Position @inaccessible { x: Int! @@ -499,7 +499,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", as: "fed2", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", as: "fed2", import: ["@inaccessible", "@tag", "@composeDirective"]) type Position @tag(name: "private") { x: Int! @@ -535,7 +535,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", as: "fed2", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", as: "fed2", import: ["@inaccessible", "@tag", "@composeDirective"]) type Product @fed2__key(fields: "upc") { upc: String! @@ -566,7 +566,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", as: "fed2", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", as: "fed2", import: ["@inaccessible", "@tag", "@composeDirective"]) type Product @fed2__extends @fed2__key(fields: "upc") { price: Int @@ -642,7 +642,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) type Book implements Product { upc: String! @@ -694,7 +694,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) type Book implements Product { upc: String! @@ -757,7 +757,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) type Book implements Product @federation__extends @federation__key(fields: "upc") { upc: String! @federation__external @@ -801,7 +801,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) type Book { upc: String! @@ -839,7 +839,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) type Book { upc: String! @@ -877,7 +877,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) enum ProductType @tag(name: "private") { BOOK @@ -914,7 +914,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) enum ProductType @inaccessible { BOOK @@ -955,7 +955,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) type Product { upc: UPC! @@ -1001,7 +1001,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) type Product { upc: UPC! @@ -1051,7 +1051,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) """ Autogenerated return type of CreateProduct. @@ -1106,7 +1106,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) """ Autogenerated return type of CreateProduct. @@ -1161,7 +1161,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) """ Autogenerated return type of CreateProduct. @@ -1216,7 +1216,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) """ Autogenerated return type of CreateProduct. @@ -1265,7 +1265,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) """ Autogenerated return type of CreateProduct. @@ -1310,7 +1310,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) """ Autogenerated return type of CreateProduct. @@ -1349,7 +1349,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) type Product @federation__key(fields: "upc") { upc: String! @@ -1380,7 +1380,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) type Product @federation__key(fields: "upc") { upc: String! @@ -1408,7 +1408,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) type Product @federation__key(fields: "upc") @federation__key(fields: "name") { name: String @@ -1434,7 +1434,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) type Product @federation__key(fields: "upc", resolvable: false) { upc: String! @@ -1459,7 +1459,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) type Product @federation__key(fields: "upc") { upc: String! @@ -1486,7 +1486,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) type Product @federation__extends @federation__key(fields: "upc") { price: Int @@ -1544,7 +1544,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) type Position { x: Int! @federation__shareable @@ -1580,7 +1580,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) type Position { x: Int! @inaccessible @@ -1616,7 +1616,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) type Position { x: Int! @tag(name: "private") @@ -1652,7 +1652,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) type Position { x: Int! @tag(name: "private") @tag(name: "protected") @@ -1704,7 +1704,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) type Product { type: ProductType! @@ -1760,7 +1760,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) type Product { type: ProductType! @@ -1816,7 +1816,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) type Product { type: ProductType! @@ -1852,7 +1852,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) type Product @federation__extends @federation__key(fields: "id") { id: ID! @@ -1888,7 +1888,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) type Product @federation__extends @federation__key(fields: "upc") { price: Int @@ -1923,7 +1923,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) type Product @federation__extends @federation__key(fields: "upc") { price: Int @federation__external @@ -1952,7 +1952,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) type Product @federation__key(fields: "productId") { productId: String! @@ -1980,7 +1980,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) type Product @federation__extends @federation__key(fields: "product_id") { options: [String!]! @federation__requires(fields: "my_id") @@ -2013,7 +2013,7 @@ def self.visible?(context) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) type Product @federation__extends @federation__key(fields: "upc") { upc: String! @federation__external @@ -2041,7 +2041,7 @@ def self.visible?(context) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) type Product @federation__key(fields: "id") { id: ID! @@ -2075,7 +2075,7 @@ def self.visible?(context) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", as: "fed2", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", as: "fed2", import: ["@inaccessible", "@tag", "@composeDirective"]) type Product @fed2__extends { upc: String! @@ -2112,7 +2112,7 @@ def self.visible?(context) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", as: "fed2", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", as: "fed2", import: ["@inaccessible", "@tag", "@composeDirective"]) type Product @fed2__extends { upc: String! From d373b7aee41df02653fb1e7779187b21565f5b60 Mon Sep 17 00:00:00 2001 From: Yannick Utard Date: Tue, 1 Aug 2023 12:07:16 +0200 Subject: [PATCH 06/19] Make federation_2_prefix easier to read --- lib/apollo-federation/schema.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/apollo-federation/schema.rb b/lib/apollo-federation/schema.rb index 5827684f3..658479125 100644 --- a/lib/apollo-federation/schema.rb +++ b/lib/apollo-federation/schema.rb @@ -86,9 +86,13 @@ def federation_2_prefix schema = ['extend schema'] all_links.each do |link| - link_str = " @link(url: \"#{link[:url]}\"" + link_str = " @link(" + link_str += "url: \"#{link[:url]}\"" link_str += ", as: \"#{link[:as]}\"" if link[:as] - link_str += ", import: [#{link[:import].map { |d| "\"@#{d}\"" }.join(', ')}]" if link[:import] + if link[:import] + imported_directives = link[:import].map { |d| "\"@#{d}\"" }.join(', ') + link_str += ", import: [#{imported_directives}]" + end link_str += ')' schema << link_str end From 0701bff63e50cf91e57f0eef528b2c0d31bd6127 Mon Sep 17 00:00:00 2001 From: Yannick Utard Date: Tue, 1 Aug 2023 12:12:56 +0200 Subject: [PATCH 07/19] Lint --- lib/apollo-federation/schema.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/apollo-federation/schema.rb b/lib/apollo-federation/schema.rb index 658479125..e06a79d43 100644 --- a/lib/apollo-federation/schema.rb +++ b/lib/apollo-federation/schema.rb @@ -86,7 +86,7 @@ def federation_2_prefix schema = ['extend schema'] all_links.each do |link| - link_str = " @link(" + link_str = ' @link(' link_str += "url: \"#{link[:url]}\"" link_str += ", as: \"#{link[:as]}\"" if link[:as] if link[:import] From 3cf158ffd24a182e68b395c8b6a91b0030e35a6a Mon Sep 17 00:00:00 2001 From: Adam Rutkowski Date: Thu, 31 Oct 2024 13:37:31 +0100 Subject: [PATCH 08/19] Add @policy and use federation_version in link --- lib/apollo-federation/schema.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/apollo-federation/schema.rb b/lib/apollo-federation/schema.rb index 0e076796d..009e243bb 100644 --- a/lib/apollo-federation/schema.rb +++ b/lib/apollo-federation/schema.rb @@ -7,7 +7,7 @@ module ApolloFederation module Schema - IMPORTED_DIRECTIVES = ['inaccessible', 'tag'].freeze + IMPORTED_DIRECTIVES = ['inaccessible', 'tag', 'policy'].freeze def self.included(klass) klass.extend(CommonMethods) @@ -64,7 +64,7 @@ def federation_2_prefix <<~SCHEMA extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3"#{federation_namespace}, import: [#{(IMPORTED_DIRECTIVES.map { |directive| "\"@#{directive}\"" }).join(', ')}]) + @link(url: "https://specs.apollo.dev/federation/v#{federation_version}"#{federation_namespace}, import: [#{(IMPORTED_DIRECTIVES.map { |directive| "\"@#{directive}\"" }).join(', ')}]) SCHEMA end From 8e410287fade18e9529dd440b98201ba14c8d987 Mon Sep 17 00:00:00 2001 From: Adam Rutkowski Date: Thu, 31 Oct 2024 13:39:13 +0100 Subject: [PATCH 09/19] Improve ignores --- .gitignore | 2 ++ .stignore | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 .stignore diff --git a/.gitignore b/.gitignore index f52119848..1269e8137 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,5 @@ node_modules/ .circleci/processed-config.yml .DS_Store + +/.idea diff --git a/.stignore b/.stignore new file mode 100644 index 000000000..bf7eb05f2 --- /dev/null +++ b/.stignore @@ -0,0 +1,47 @@ +*.gem +*.rbc +.idea +coverage +InstalledFiles +pkg +spec/reports +spec/examples.txt +test/tmp +test/version_tmp +tmp + +# Ignore Byebug command history file. +.byebug_history + +## Specific to RubyMotion: +.dat* +.repl_history +build +*.bridgesupport +build-iPhoneOS +build-iPhoneSimulator + +## Specific to RubyMotion (use of CocoaPods): +# +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +# +# vendor/Pods/ + +## Documentation cache and generated files: +.yardoc +_yardoc + +## Environment normalization: +vendor/bundle +lib/bundler/man + +# for a library or gem, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +Gemfile.lock +.ruby-version +.ruby-gemset + +# unless supporting rvm < 1.11.0 or doing something fancy, ignore this: +.rvmrc From 5ab53ba7ca8486214cfa98868c18259ae42a99b9 Mon Sep 17 00:00:00 2001 From: Adam Rutkowski Date: Wed, 13 Nov 2024 09:15:20 +0100 Subject: [PATCH 10/19] Add policy directive --- .bundle/config | 3 + Gemfile.lock | 9 +- README.md | 14 + lib/apollo-federation/enum.rb | 10 + lib/apollo-federation/field.rb | 14 +- lib/apollo-federation/interface.rb | 10 + lib/apollo-federation/object.rb | 10 + lib/apollo-federation/scalar.rb | 10 + lib/apollo-federation/schema.rb | 2 +- spec/apollo-federation/schema_spec.rb | 6 +- .../service_field_v2_spec.rb | 403 +++++++++++++----- 11 files changed, 380 insertions(+), 111 deletions(-) create mode 100644 .bundle/config diff --git a/.bundle/config b/.bundle/config new file mode 100644 index 000000000..47be22513 --- /dev/null +++ b/.bundle/config @@ -0,0 +1,3 @@ +--- +BUNDLE_BUILD__JARO_WINKLER: "--with-cflags=-Wno-error=incompatible-function-pointer-types" +BUNDLE_BUILD__DEBASE: "--with-cflags=-Wno-error=incompatible-function-pointer-types" diff --git a/Gemfile.lock b/Gemfile.lock index 9b943c579..9f0890cfa 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -47,9 +47,7 @@ GEM debase-ruby_core_source (3.2.0) diff-lcs (1.5.0) erubi (1.12.0) - google-protobuf (3.22.2-arm64-darwin) - google-protobuf (3.22.2-x86_64-darwin) - google-protobuf (3.22.2-x86_64-linux) + google-protobuf (3.22.2) graphql (2.0.19) i18n (1.12.0) concurrent-ruby (~> 1.0) @@ -58,7 +56,11 @@ GEM crass (~> 1.0.2) nokogiri (>= 1.5.9) method_source (1.0.0) + mini_portile2 (2.8.7) minitest (5.18.0) + nokogiri (1.14.2) + mini_portile2 (~> 2.8.0) + racc (~> 1.4) nokogiri (1.14.2-arm64-darwin) racc (~> 1.4) nokogiri (1.14.2-x86_64-darwin) @@ -119,6 +121,7 @@ GEM PLATFORMS arm64-darwin-21 arm64-darwin-22 + ruby x86_64-darwin-20 x86_64-darwin-21 x86_64-darwin-22 diff --git a/README.md b/README.md index d293492f7..a8c0d1c4d 100644 --- a/README.md +++ b/README.md @@ -302,6 +302,20 @@ class User < BaseObject end ``` +### The `@policy` directive (Apollo Federation v2) + +[Apollo documentation](https://www.apollographql.com/docs/federation/federated-types/federated-directives/#policy) + +Call `policy` within your class definition or pass the `policy:` option to your field definition: + +```ruby +class Product < BaseObject + policy policies: [["stock:read"]] + field :id, ID, null: false + field :inStock, Boolean, null: false, policy: { policies: [["stock:read"]] } +end +``` + ### Field set syntax Field sets can be either strings encoded with the Apollo Field Set [syntax]((https://www.apollographql.com/docs/apollo-server/federation/federation-spec/#scalar-_fieldset)) or arrays, hashes and snake case symbols that follow the graphql-ruby conventions: diff --git a/lib/apollo-federation/enum.rb b/lib/apollo-federation/enum.rb index b5b810f00..780f7d199 100644 --- a/lib/apollo-federation/enum.rb +++ b/lib/apollo-federation/enum.rb @@ -18,6 +18,16 @@ def tag(name:) def inaccessible add_directive(name: 'inaccessible') end + + def policy(policies) + add_directive( + name: "policy", + arguments: [ + name: 'policies', + values: policies, + ] + ) + end end end end diff --git a/lib/apollo-federation/field.rb b/lib/apollo-federation/field.rb index 6945b2672..c1a2d692a 100644 --- a/lib/apollo-federation/field.rb +++ b/lib/apollo-federation/field.rb @@ -8,7 +8,7 @@ module Field include HasDirectives VERSION_1_DIRECTIVES = %i[external requires provides].freeze - VERSION_2_DIRECTIVES = %i[shareable inaccessible override tags].freeze + VERSION_2_DIRECTIVES = %i[shareable inaccessible override policy tags].freeze def initialize(*args, **kwargs, &block) add_v1_directives(**kwargs) @@ -59,7 +59,7 @@ def add_v1_directives(external: nil, requires: nil, provides: nil, **_kwargs) nil end - def add_v2_directives(shareable: nil, inaccessible: nil, override: nil, tags: [], **_kwargs) + def add_v2_directives(shareable: nil, inaccessible: nil, override: nil, tags: [], policy: nil, **_kwargs) if shareable add_directive(name: 'shareable') end @@ -78,6 +78,16 @@ def add_v2_directives(shareable: nil, inaccessible: nil, override: nil, tags: [] ) end + if policy + add_directive( + name: "policy", + arguments: [ + name: 'policies', + values: policy[:policies] + ] + ) + end + tags.each do |tag| add_directive( name: 'tag', diff --git a/lib/apollo-federation/interface.rb b/lib/apollo-federation/interface.rb index 4a8503bbd..70899ab76 100644 --- a/lib/apollo-federation/interface.rb +++ b/lib/apollo-federation/interface.rb @@ -26,6 +26,16 @@ def tag(name:) add_directive(name: 'tag', arguments: [name: 'name', values: name]) end + def policy(policies) + add_directive( + name: "policy", + arguments: [ + name: 'policies', + values: policies, + ] + ) + end + def key(fields:, camelize: true) add_directive( name: 'key', diff --git a/lib/apollo-federation/object.rb b/lib/apollo-federation/object.rb index ea54dee5f..13502ad8f 100644 --- a/lib/apollo-federation/object.rb +++ b/lib/apollo-federation/object.rb @@ -32,6 +32,16 @@ def tag(name:) add_directive(name: 'tag', arguments: [name: 'name', values: name]) end + def policy(policies) + add_directive( + name: "policy", + arguments: [ + name: 'policies', + values: policies, + ] + ) + end + def key(fields:, camelize: true, resolvable: true) arguments = [ name: 'fields', diff --git a/lib/apollo-federation/scalar.rb b/lib/apollo-federation/scalar.rb index 443d57104..768c95719 100644 --- a/lib/apollo-federation/scalar.rb +++ b/lib/apollo-federation/scalar.rb @@ -18,6 +18,16 @@ def tag(name:) def inaccessible add_directive(name: 'inaccessible') end + + def policy(policies) + add_directive( + name: "policy", + arguments: [ + name: 'policies', + values: policies, + ] + ) + end end end end diff --git a/lib/apollo-federation/schema.rb b/lib/apollo-federation/schema.rb index 009e243bb..1151154c7 100644 --- a/lib/apollo-federation/schema.rb +++ b/lib/apollo-federation/schema.rb @@ -7,7 +7,7 @@ module ApolloFederation module Schema - IMPORTED_DIRECTIVES = ['inaccessible', 'tag', 'policy'].freeze + IMPORTED_DIRECTIVES = ['inaccessible', 'policy', 'tag'].freeze def self.included(klass) klass.extend(CommonMethods) diff --git a/spec/apollo-federation/schema_spec.rb b/spec/apollo-federation/schema_spec.rb index 1c369f3b9..b4ba89cff 100644 --- a/spec/apollo-federation/schema_spec.rb +++ b/spec/apollo-federation/schema_spec.rb @@ -15,13 +15,13 @@ expect(schema.federation_version).to eq('1.0') end - it 'returns the specified version when set to 2.0' do + it 'returns the specified version when set to 2.6' do schema = Class.new(GraphQL::Schema) do include ApolloFederation::Schema - federation version: '2.0' + federation version: '2.6' end - expect(schema.federation_version).to eq('2.0') + expect(schema.federation_version).to eq('2.6') end it 'returns the specified version when set to 2.3' do diff --git a/spec/apollo-federation/service_field_v2_spec.rb b/spec/apollo-federation/service_field_v2_spec.rb index bb7fd83f1..d36f12a9f 100644 --- a/spec/apollo-federation/service_field_v2_spec.rb +++ b/spec/apollo-federation/service_field_v2_spec.rb @@ -40,7 +40,7 @@ def execute_sdl(schema) schema = Class.new(base_schema) do query query_obj - federation version: '2.0' + federation version: '2.6' end expect(schema.to_definition).to match_sdl( @@ -109,13 +109,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do query query_obj - federation version: '2.0' + federation version: '2.6' end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) type Product { upc: String! @@ -144,13 +144,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do query query_obj - federation version: '2.0' + federation version: '2.6' end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) type Product @federation__extends { upc: String! @@ -180,13 +180,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do query query_obj - federation version: '2.0' + federation version: '2.6' end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) type Position @federation__shareable { x: Int! @@ -217,13 +217,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do query query_obj - federation version: '2.0' + federation version: '2.6' end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) type Position @inaccessible { x: Int! @@ -254,13 +254,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do query query_obj - federation version: '2.0' + federation version: '2.6' end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) type Position @tag(name: "private") { x: Int! @@ -274,6 +274,43 @@ def execute_sdl(schema) ) end + it 'returns valid SDL for types with policy' do + position = Class.new(base_object) do + graphql_name 'Position' + policy [['private']] + + field :x, Integer, null: false + field :y, Integer, null: false + end + + query_obj = Class.new(base_object) do + graphql_name 'Query' + + field :position, position, null: true + end + + schema = Class.new(base_schema) do + query query_obj + federation version: '2.6' + end + + expect(execute_sdl(schema)).to match_sdl( + <<~GRAPHQL, + extend schema + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) + + type Position @policy(policies: [["private"]]) { + x: Int! + y: Int! + } + + type Query { + position: Position + } + GRAPHQL + ) + end + context 'with a custom link namespace provided' do it 'returns valid SDL for type extensions with custom namespace' do product = Class.new(base_object) do @@ -291,13 +328,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do query query_obj - federation version: '2.0', link: { as: 'fed2' } + federation version: '2.6', link: { as: 'fed2' } end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", as: "fed2", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", as: "fed2", import: ["@inaccessible", "@policy", "@tag"]) type Product @fed2__extends { upc: String! @@ -327,13 +364,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do query query_obj - federation version: '2.0', link: { as: 'fed2' } + federation version: '2.6', link: { as: 'fed2' } end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", as: "fed2", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", as: "fed2", import: ["@inaccessible", "@policy", "@tag"]) type Position @fed2__shareable { x: Int! @@ -364,13 +401,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do query query_obj - federation version: '2.0', link: { as: 'fed2' } + federation version: '2.6', link: { as: 'fed2' } end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", as: "fed2", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", as: "fed2", import: ["@inaccessible", "@policy", "@tag"]) type Position @inaccessible { x: Int! @@ -401,13 +438,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do query query_obj - federation version: '2.0', link: { as: 'fed2' } + federation version: '2.6', link: { as: 'fed2' } end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", as: "fed2", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", as: "fed2", import: ["@inaccessible", "@policy", "@tag"]) type Position @tag(name: "private") { x: Int! @@ -437,13 +474,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do query query_obj - federation version: '2.0', link: { as: 'fed2' } + federation version: '2.6', link: { as: 'fed2' } end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", as: "fed2", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", as: "fed2", import: ["@inaccessible", "@policy", "@tag"]) type Product @fed2__key(fields: "upc") { upc: String! @@ -468,13 +505,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do orphan_types product - federation version: '2.0', link: { as: 'fed2' } + federation version: '2.6', link: { as: 'fed2' } end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", as: "fed2", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", as: "fed2", import: ["@inaccessible", "@policy", "@tag"]) type Product @fed2__extends @fed2__key(fields: "upc") { price: Int @@ -501,7 +538,7 @@ def execute_sdl(schema) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", as: "fed2", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.3", as: "fed2", import: ["@inaccessible", "@policy", "@tag"]) type Product @fed2__interfaceObject @fed2__key(fields: "id") { id: ID! @@ -544,13 +581,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do orphan_types book - federation version: '2.0' + federation version: '2.6' end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) type Book implements Product { upc: String! @@ -596,13 +633,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do orphan_types book - federation version: '2.0' + federation version: '2.6' end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) type Book implements Product { upc: String! @@ -615,6 +652,58 @@ def execute_sdl(schema) ) end + it 'returns valid SDL for interface types with policy' do + base_field = Class.new(GraphQL::Schema::Field) do + include ApolloFederation::Field + end + + base_interface = Module.new do + include GraphQL::Schema::Interface + include ApolloFederation::Interface + + # graphql_name 'Interface' + field_class base_field + end + + product = Module.new do + include base_interface + + graphql_name 'Product' + + policy [['private']] + + field :upc, String, null: false + end + + book = Class.new(base_object) do + implements product + + graphql_name 'Book' + + field :upc, String, null: false + end + + schema = Class.new(base_schema) do + orphan_types book + federation version: '2.6' + end + + expect(execute_sdl(schema)).to match_sdl( + <<~GRAPHQL, + extend schema + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) + + type Book implements Product { + upc: String! + } + + interface Product @policy(policies: [["private"]]) { + upc: String! + } + GRAPHQL + ) + end + it 'returns valid SDL for interface types' do base_field = Class.new(GraphQL::Schema::Field) do include ApolloFederation::Field @@ -659,13 +748,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do orphan_types book, pen - federation version: '2.0' + federation version: '2.6' end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) type Book implements Product @federation__extends @federation__key(fields: "upc") { upc: String! @federation__external @@ -703,13 +792,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do orphan_types book, product - federation version: '2.0' + federation version: '2.6' end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) type Book { upc: String! @@ -741,13 +830,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do orphan_types book, product - federation version: '2.0' + federation version: '2.6' end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) type Book { upc: String! @@ -765,7 +854,7 @@ def execute_sdl(schema) product_type = Class.new(base_enum) do graphql_name 'ProductType' - tag name: 'private' + policy [['private']] value 'BOOK' value 'PEN' @@ -779,15 +868,15 @@ def execute_sdl(schema) schema = Class.new(base_schema) do orphan_types product_type, product - federation version: '2.0' + federation version: '2.6' end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) - enum ProductType @tag(name: "private") { + enum ProductType @policy(policies: [["private"]]) { BOOK PEN } @@ -795,6 +884,43 @@ def execute_sdl(schema) ) end + it 'returns valid SDL for enum types with policy' do + base_enum = Class.new(GraphQL::Schema::Enum) do + include ApolloFederation::Enum + end + + product_type = Class.new(base_enum) do + graphql_name 'ProductType' + tag name: 'private' + + value 'BOOK' + value 'PEN' + end + + product = Class.new(base_object) do + graphql_name 'Product' + + field :type, product_type, null: false + end + + schema = Class.new(base_schema) do + orphan_types product_type, product + federation version: '2.6' + end + + expect(execute_sdl(schema)).to match_sdl( + <<~GRAPHQL, + extend schema + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) + + enum ProductType @tag(name: "private") { + BOOK + PEN + } + GRAPHQL + ) + end + it 'returns valid SDL for inaccessible enum types' do base_enum = Class.new(GraphQL::Schema::Enum) do include ApolloFederation::Enum @@ -816,13 +942,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do orphan_types product_type, product - federation version: '2.0' + federation version: '2.6' end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) enum ProductType @inaccessible { BOOK @@ -857,13 +983,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do query query_obj - federation version: '2.0' + federation version: '2.6' end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) type Product { upc: UPC! @@ -878,6 +1004,52 @@ def execute_sdl(schema) ) end + it 'returns valid SDL for scalar types with policy' do + base_scalar = Class.new(GraphQL::Schema::Scalar) do + include ApolloFederation::Scalar + end + + upc = Class.new(base_scalar) do + graphql_name 'UPC' + + policy [['private']] + end + + product = Class.new(base_object) do + graphql_name 'Product' + + field :upc, upc, null: false + end + + query_obj = Class.new(base_object) do + graphql_name 'Query' + + field :product, product, null: true + end + + schema = Class.new(base_schema) do + query query_obj + federation version: '2.6' + end + + expect(execute_sdl(schema)).to match_sdl( + <<~GRAPHQL, + extend schema + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) + + type Product { + upc: UPC! + } + + type Query { + product: Product + } + + scalar UPC @policy(policies: [["private"]]) + GRAPHQL + ) + end + it 'returns valid SDL for inaccessible scalar types' do base_scalar = Class.new(GraphQL::Schema::Scalar) do include ApolloFederation::Scalar @@ -903,13 +1075,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do query query_obj - federation version: '2.0' + federation version: '2.6' end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) type Product { upc: UPC! @@ -953,13 +1125,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do mutation mutations - federation version: '2.0' + federation version: '2.6' end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) """ Autogenerated return type of CreateProduct. @@ -1008,13 +1180,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do mutation mutations - federation version: '2.0' + federation version: '2.6' end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) """ Autogenerated return type of CreateProduct. @@ -1063,13 +1235,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do mutation mutations - federation version: '2.0' + federation version: '2.6' end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) """ Autogenerated return type of CreateProduct. @@ -1118,13 +1290,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do mutation mutations - federation version: '2.0' + federation version: '2.6' end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) """ Autogenerated return type of CreateProduct. @@ -1167,13 +1339,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do mutation mutations - federation version: '2.0' + federation version: '2.6' end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) """ Autogenerated return type of CreateProduct. @@ -1212,13 +1384,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do mutation mutations - federation version: '2.0' + federation version: '2.6' end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) """ Autogenerated return type of CreateProduct. @@ -1251,13 +1423,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do query query_obj - federation version: '2.0' + federation version: '2.6' end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) type Product @federation__key(fields: "upc") { upc: String! @@ -1282,13 +1454,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do orphan_types product - federation version: '2.0' + federation version: '2.6' end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) type Product @federation__key(fields: "upc") { upc: String! @@ -1310,13 +1482,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do orphan_types product - federation version: '2.0' + federation version: '2.6' end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) type Product @federation__key(fields: "upc") @federation__key(fields: "name") { name: String @@ -1336,13 +1508,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do orphan_types product - federation version: '2.0' + federation version: '2.6' end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) type Product @federation__key(fields: "upc", resolvable: false) { upc: String! @@ -1361,13 +1533,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do orphan_types product - federation version: '2.0' + federation version: '2.6' end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) type Product @federation__key(fields: "upc") { upc: String! @@ -1388,13 +1560,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do orphan_types product - federation version: '2.0' + federation version: '2.6' end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) type Product @federation__extends @federation__key(fields: "upc") { price: Int @@ -1404,6 +1576,33 @@ def execute_sdl(schema) ) end + it 'returns valid SDL for @policy directive' do + product = Class.new(base_object) do + graphql_name 'Product' + key fields: :id + + field :id, 'ID', null: false + field :isStock, 'Boolean', null: false, policy: { policies: [['private']] } + end + + schema = Class.new(base_schema) do + orphan_types product + federation version: '2.6' + end + + expect(execute_sdl(schema)).to match_sdl( + <<~GRAPHQL, + extend schema + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) + + type Product @federation__key(fields: "id") { + id: ID! + isStock: Boolean! @policy(policies: [["private"]]) + } + GRAPHQL + ) + end + it 'returns valid SDL for @interfaceObject directives' do product = Class.new(base_object) do graphql_name 'Product' @@ -1415,13 +1614,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do orphan_types product - federation version: '2.3' + federation version: '2.6' end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) type Product @federation__interfaceObject @federation__key(fields: "id") { id: ID! @@ -1446,13 +1645,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do query query_obj - federation version: '2.0' + federation version: '2.6' end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) type Position { x: Int! @federation__shareable @@ -1482,13 +1681,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do query query_obj - federation version: '2.0' + federation version: '2.6' end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) type Position { x: Int! @inaccessible @@ -1518,13 +1717,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do query query_obj - federation version: '2.0' + federation version: '2.6' end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) type Position { x: Int! @tag(name: "private") @@ -1554,13 +1753,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do query query_obj - federation version: '2.0' + federation version: '2.6' end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) type Position { x: Int! @tag(name: "private") @tag(name: "protected") @@ -1606,13 +1805,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do query query_obj - federation version: '2.0' + federation version: '2.6' end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) type Product { type: ProductType! @@ -1662,13 +1861,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do query query_obj - federation version: '2.0' + federation version: '2.6' end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) type Product { type: ProductType! @@ -1718,13 +1917,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do query query_obj - federation version: '2.0' + federation version: '2.6' end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) type Product { type: ProductType! @@ -1754,13 +1953,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do orphan_types product - federation version: '2.0' + federation version: '2.6' end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) type Product @federation__extends @federation__key(fields: "id") { id: ID! @@ -1790,13 +1989,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do orphan_types product, review - federation version: '2.0' + federation version: '2.6' end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) type Product @federation__extends @federation__key(fields: "upc") { price: Int @@ -1825,13 +2024,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do orphan_types product - federation version: '2.0' + federation version: '2.6' end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) type Product @federation__extends @federation__key(fields: "upc") { price: Int @federation__external @@ -1854,13 +2053,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do orphan_types product - federation version: '2.0' + federation version: '2.6' end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) type Product @federation__key(fields: "productId") { productId: String! @@ -1882,13 +2081,13 @@ def execute_sdl(schema) schema = Class.new(base_schema) do orphan_types product - federation version: '2.0' + federation version: '2.6' end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) type Product @federation__extends @federation__key(fields: "product_id") { options: [String!]! @federation__requires(fields: "my_id") @@ -1915,13 +2114,13 @@ def self.visible?(context) schema = Class.new(base_schema) do orphan_types product - federation version: '2.0' + federation version: '2.6' end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) type Product @federation__extends @federation__key(fields: "upc") { upc: String! @federation__external @@ -1942,14 +2141,14 @@ def self.visible?(context) end schema = Class.new(base_schema) do - federation version: '2.0' + federation version: '2.6' orphan_types product end expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@inaccessible", "@policy", "@tag"]) type Product @federation__key(fields: "id") { id: ID! @@ -1973,7 +2172,7 @@ def self.visible?(context) end new_base_schema = Class.new(base_schema) do - federation version: '2.0', link: { as: 'fed2' } + federation version: '2.6', link: { as: 'fed2' } end schema = Class.new(new_base_schema) do @@ -1983,7 +2182,7 @@ def self.visible?(context) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", as: "fed2", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", as: "fed2", import: ["@inaccessible", "@policy", "@tag"]) type Product @fed2__extends { upc: String! @@ -2011,7 +2210,7 @@ def self.visible?(context) end new_base_schema = Class.new(base_schema) do - federation version: '2.0', link: { as: 'fed2' } + federation version: '2.6', link: { as: 'fed2' } query query_obj end @@ -2020,7 +2219,7 @@ def self.visible?(context) expect(execute_sdl(schema)).to match_sdl( <<~GRAPHQL, extend schema - @link(url: "https://specs.apollo.dev/federation/v2.3", as: "fed2", import: ["@inaccessible", "@tag"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", as: "fed2", import: ["@inaccessible", "@policy", "@tag"]) type Product @fed2__extends { upc: String! From 27de46e939319c1b66b9900a95ab2556636f1ee4 Mon Sep 17 00:00:00 2001 From: Adam Rutkowski Date: Thu, 14 Nov 2024 11:44:04 +0100 Subject: [PATCH 11/19] chore: create Github CI config --- .bundle/config | 3 - .github/actions/appraisal-install/action.yml | 23 +++++ .github/workflows/ci.yml | 96 ++++++++++++++++++++ .github/workflows/semantic.yml | 29 ++++++ release.config.js | 1 + 5 files changed, 149 insertions(+), 3 deletions(-) delete mode 100644 .bundle/config create mode 100644 .github/actions/appraisal-install/action.yml create mode 100644 .github/workflows/ci.yml diff --git a/.bundle/config b/.bundle/config deleted file mode 100644 index 47be22513..000000000 --- a/.bundle/config +++ /dev/null @@ -1,3 +0,0 @@ ---- -BUNDLE_BUILD__JARO_WINKLER: "--with-cflags=-Wno-error=incompatible-function-pointer-types" -BUNDLE_BUILD__DEBASE: "--with-cflags=-Wno-error=incompatible-function-pointer-types" diff --git a/.github/actions/appraisal-install/action.yml b/.github/actions/appraisal-install/action.yml new file mode 100644 index 000000000..0abda4f0f --- /dev/null +++ b/.github/actions/appraisal-install/action.yml @@ -0,0 +1,23 @@ +name: Appraisal install +inputs: + appraisal: + description: "Appraisal version to install" + required: true + +runs: + using: "composite" + steps: + - name: Copy lockfile + shell: bash + run: bundle exec ${{ inputs.appraisal }} "cp $BUNDLE_GEMFILE.lock current_appraisal.gemfile.lock" + - name: Appraisal package cache + uses: actions/cache@v4 + with: + key: v6-gem-cache-${{ runner.os }}-${{ github.ref }}-${{ hashFiles('current_appraisal.gemfile.lock') }} + path: vendor/bundle + restore-keys: | + v6-gem-cache-${{ runner.os }}-${{ github.ref }}- + v6-gem-cache-${{ runner.os }}- + - name: Install appraisal dependencies + shell: bash + run: bundle exec appraisal ${{ inputs.appraisal }} bundle install --jobs=1 --retry=3 --path=../vendor/bundle \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..ac1e65c71 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,96 @@ +name: Main +on: + push: + branches: + - main + pull_request: + +jobs: + ruby-lint: + runs-on: ubuntu-latest + env: + BUNDLE_APP_CONFIG: .bundle + steps: + - uses: actions/checkout@v4 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.2' + bundler-cache: true + - run: bundle exec rubocop + ruby-test: + runs-on: ubuntu-latest + env: + BUNDLE_APP_CONFIG: .bundle + strategy: + fail-fast: false + matrix: + appraisal: [ 'graphql-1.10', 'graphql-1.11', 'graphql-1.12', 'graphql-1.13', 'graphql-2.0' ] + ruby: [ '2.7', '3.0', '3.1', '3.2' ] + exclude: + - appraisal: 'graphql-1.10' + ruby: '3.2' + - appraisal: 'graphql-1.11' + ruby: '3.2' + - appraisal: 'graphql-1.12' + ruby: '3.2' + steps: + - uses: actions/checkout@v4 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + - uses: ./.github/actions/appraisal-install + with: + appraisal: ${{ matrix.appraisal }} + - run: bundle config list + - run: bundle exec appraisal ${{ matrix.appraisal }} bundle config list + - run: bundle exec appraisal ${{ matrix.appraisal }} rake + + integration-tests: + runs-on: ubuntu-latest + env: + BUNDLE_APP_CONFIG: .bundle + strategy: + fail-fast: false + matrix: + appraisal: [ 'graphql-1.10', 'graphql-1.11', 'graphql-1.12', 'graphql-1.13', 'graphql-2.0' ] + ruby: [ '2.7', '3.0', '3.1', '3.2' ] + exclude: + - appraisal: 'graphql-1.10' + ruby: '3.2' + - appraisal: 'graphql-1.11' + ruby: '3.2' + - appraisal: 'graphql-1.12' + ruby: '3.2' + steps: + - uses: actions/checkout@v4 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + - uses: ./.github/actions/appraisal-install + with: + appraisal: ${{ matrix.appraisal }} + - name: Run install + uses: borales/actions-yarn@v4 + with: + cmd: install + - run: bundle exec appraisal ${{ matrix.appraisal }} yarn test + - run: yarn lint + + release: + runs-on: ubuntu-latest + needs: [ ruby-lint, ruby-test, integration-tests ] + env: + BUNDLE_APP_CONFIG: .bundle + steps: + - uses: actions/checkout@v4 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.2' + bundler-cache: true + - name: Run install + uses: borales/actions-yarn@v4 + with: + cmd: install + - run: npx semantic-release diff --git a/.github/workflows/semantic.yml b/.github/workflows/semantic.yml index ab63e3bda..55669e978 100644 --- a/.github/workflows/semantic.yml +++ b/.github/workflows/semantic.yml @@ -6,6 +6,10 @@ on: - opened - edited - synchronize + - reopened + +permissions: + pull-requests: read jobs: main: @@ -13,5 +17,30 @@ jobs: runs-on: ubuntu-latest steps: - uses: amannn/action-semantic-pull-request@v5 + id: lint_pr_title env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - uses: marocchino/sticky-pull-request-comment@v2 + # When the previous steps fails, the workflow would stop. By adding this + # condition you can continue the execution with the populated error message. + if: always() && (steps.lint_pr_title.outputs.error_message != null) + with: + header: pr-title-lint-error + message: | + Hey there and thank you for opening this pull request! 👋🏼 + + We require pull request titles to follow the [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/) and it looks like your proposed title needs to be adjusted. + + Details: + + ``` + ${{ steps.lint_pr_title.outputs.error_message }} + ``` + + # Delete a previous comment when the issue has been resolved + - if: ${{ steps.lint_pr_title.outputs.error_message == null }} + uses: marocchino/sticky-pull-request-comment@v2 + with: + header: pr-title-lint-error + delete: true diff --git a/release.config.js b/release.config.js index 701796fc6..421824a18 100644 --- a/release.config.js +++ b/release.config.js @@ -13,6 +13,7 @@ module.exports = { 'semantic-release-rubygem', { updateGemfileLock: 'bundle install', + gemPublish: false, }, ], [ From e14052a15c4cd6ba8373174e55d957142d4b26e2 Mon Sep 17 00:00:00 2001 From: Adam Rutkowski Date: Thu, 14 Nov 2024 14:06:51 +0100 Subject: [PATCH 12/19] Upgrade rubocop, tweak rules & remove problematic package --- .github/actions/appraisal-install/action.yml | 2 +- .github/workflows/ci.yml | 8 ++-- .github/workflows/semantic.yml | 2 +- .rubocop.yml | 30 +++++++++++---- Gemfile.lock | 39 ++++++++++---------- apollo-federation.gemspec | 7 ++-- lib/apollo-federation/entities_field.rb | 2 + lib/apollo-federation/schema.rb | 2 +- lib/apollo-federation/tracing/node_map.rb | 1 + 9 files changed, 57 insertions(+), 36 deletions(-) diff --git a/.github/actions/appraisal-install/action.yml b/.github/actions/appraisal-install/action.yml index 0abda4f0f..5670d3944 100644 --- a/.github/actions/appraisal-install/action.yml +++ b/.github/actions/appraisal-install/action.yml @@ -9,7 +9,7 @@ runs: steps: - name: Copy lockfile shell: bash - run: bundle exec ${{ inputs.appraisal }} "cp $BUNDLE_GEMFILE.lock current_appraisal.gemfile.lock" + run: bundle exec appraisal ${{ inputs.appraisal }} "cp \$BUNDLE_GEMFILE.lock current_appraisal.gemfile.lock" - name: Appraisal package cache uses: actions/cache@v4 with: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ac1e65c71..5e164f0f8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,7 +39,8 @@ jobs: with: ruby-version: ${{ matrix.ruby }} bundler-cache: true - - uses: ./.github/actions/appraisal-install + - name: Run appraisal install + uses: ./.github/actions/appraisal-install with: appraisal: ${{ matrix.appraisal }} - run: bundle config list @@ -54,7 +55,7 @@ jobs: fail-fast: false matrix: appraisal: [ 'graphql-1.10', 'graphql-1.11', 'graphql-1.12', 'graphql-1.13', 'graphql-2.0' ] - ruby: [ '2.7', '3.0', '3.1', '3.2' ] + ruby: [ '3.0', '3.1', '3.2' ] exclude: - appraisal: 'graphql-1.10' ruby: '3.2' @@ -68,7 +69,8 @@ jobs: with: ruby-version: ${{ matrix.ruby }} bundler-cache: true - - uses: ./.github/actions/appraisal-install + - name: Run appraisal install + uses: ./.github/actions/appraisal-install with: appraisal: ${{ matrix.appraisal }} - name: Run install diff --git a/.github/workflows/semantic.yml b/.github/workflows/semantic.yml index 55669e978..bf3f95d9d 100644 --- a/.github/workflows/semantic.yml +++ b/.github/workflows/semantic.yml @@ -9,7 +9,7 @@ on: - reopened permissions: - pull-requests: read + pull-requests: write jobs: main: diff --git a/.rubocop.yml b/.rubocop.yml index 49ebbb784..ce3c5c60d 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -3,11 +3,14 @@ require: rubocop-rspec AllCops: TargetRubyVersion: 2.3 DisplayCopNames: true + NewCops: disable Exclude: - 'node_modules/**/*' - 'gemfiles/**/*' - 'vendor/**/*' +Gemspec/DevelopmentDependencies: + Enabled: false # # We prefer trailing commas so that the git diff is reduced when adding elements # @@ -20,8 +23,6 @@ Style/TrailingCommaInArrayLiteral: Style/TrailingCommaInHashLiteral: Enabled: true EnforcedStyleForMultiline: consistent_comma -Style/BracesAroundHashParameters: - EnforcedStyle: context_dependent Style/ConditionalAssignment: EnforcedStyle: assign_inside_condition IncludeTernaryExpressions: false @@ -37,7 +38,12 @@ Style/NegatedIf: Enabled: false Style/Documentation: Enabled: false - +Style/HashAsLastArrayItem: + Enabled: false +Naming/VariableNumber: + Enabled: false +Lint/ConstantDefinitionInBlock: + Enabled: false Metrics/AbcSize: Enabled: false Metrics/BlockLength: @@ -49,11 +55,10 @@ Metrics/ModuleLength: Metrics/ParameterLists: Max: 5 CountKeywordArgs: false -Metrics/LineLength: +Layout/LineLength: Max: 120 Exclude: - lib/apollo-federation/tracing/proto/apollo_pb.rb - Naming/FileName: Exclude: - 'lib/apollo-federation.rb' @@ -63,10 +68,21 @@ RSpec/ExampleLength: Enabled: false RSpec/NestedGroups: Enabled: false -RSpec/FilePath: +RSpec/SpecFilePathFormat: CustomTransform: ApolloFederation: apollo-federation - +RSpec/RepeatedExampleGroupDescription: + Enabled: false +RSpec/LeakyConstantDeclaration: + Enabled: false +RSpec/MultipleMemoizedHelpers: + Enabled: false +RSpec/IndexedLet: + Enabled: false +RSpec/EmptyLineAfterExample: + Enabled: false +RSpec/MatchArray: + Enabled: false # Disabled so we can support Ruby 2.2 # Rubocop only supports >= 2.3 Gemspec/RequiredRubyVersion: diff --git a/Gemfile.lock b/Gemfile.lock index 9f0890cfa..d3283783d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -42,16 +42,14 @@ GEM coderay (1.1.3) concurrent-ruby (1.2.2) crass (1.0.6) - debase (0.2.5.beta2) - debase-ruby_core_source (>= 0.10.12) - debase-ruby_core_source (3.2.0) diff-lcs (1.5.0) erubi (1.12.0) google-protobuf (3.22.2) graphql (2.0.19) i18n (1.12.0) concurrent-ruby (~> 1.0) - jaro_winkler (1.5.4) + json (2.8.1) + language_server-protocol (3.17.0.3) loofah (2.19.1) crass (~> 1.0.2) nokogiri (>= 1.5.9) @@ -67,16 +65,17 @@ GEM racc (~> 1.4) nokogiri (1.14.2-x86_64-linux) racc (~> 1.4) - parallel (1.22.1) - parser (3.2.1.1) + parallel (1.26.3) + parser (3.3.6.0) ast (~> 2.4.1) + racc pry (0.14.2) coderay (~> 1.1) method_source (~> 1.0) pry-byebug (3.10.1) byebug (~> 11.0) pry (>= 0.13, < 0.15) - racc (1.6.2) + racc (1.8.1) rack (2.2.6.4) rack-test (2.1.0) rack (>= 1.3) @@ -87,6 +86,7 @@ GEM loofah (~> 2.19, >= 2.19.1) rainbow (3.1.1) rake (13.0.6) + regexp_parser (2.9.2) rspec (3.12.0) rspec-core (~> 3.12.0) rspec-expectations (~> 3.12.0) @@ -100,22 +100,25 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.12.0) rspec-support (3.12.0) - rubocop (0.75.1) - jaro_winkler (~> 1.5.1) + rubocop (1.68.0) + json (~> 2.3) + language_server-protocol (>= 3.17.0) parallel (~> 1.10) - parser (>= 2.6) + parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 2.4, < 3.0) + rubocop-ast (>= 1.32.2, < 2.0) ruby-progressbar (~> 1.7) - unicode-display_width (>= 1.4.0, < 1.7) - rubocop-rspec (1.33.0) - rubocop (>= 0.60.0) - ruby-debug-ide (0.7.3) - rake (>= 0.8.1) + unicode-display_width (>= 2.4.0, < 3.0) + rubocop-ast (1.36.1) + parser (>= 3.3.1.0) + rubocop-rspec (3.2.0) + rubocop (~> 1.61) ruby-progressbar (1.13.0) thor (1.2.1) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - unicode-display_width (1.6.1) + unicode-display_width (2.6.0) webrick (1.8.1) PLATFORMS @@ -131,15 +134,13 @@ DEPENDENCIES actionpack apollo-federation! appraisal! - debase (= 0.2.5.beta2) graphql (~> 2.0.0) pry-byebug rack rake rspec - rubocop (~> 0.75.0) + rubocop (~> 1.68.0) rubocop-rspec - ruby-debug-ide webrick BUNDLED WITH diff --git a/apollo-federation.gemspec b/apollo-federation.gemspec index ccc18f04d..e32268c48 100644 --- a/apollo-federation.gemspec +++ b/apollo-federation.gemspec @@ -22,23 +22,22 @@ Gem::Specification.new do |spec| 'changelog_uri' => 'https://github.com/Gusto/apollo-federation-ruby/releases', 'source_code_uri' => 'https://github.com/Gusto/apollo-federation-ruby', 'bug_tracker_uri' => 'https://github.com/Gusto/apollo-federation-ruby/issues', + 'rubygems_mfa_required' => 'true', } spec.files = `git ls-files bin lib *.md LICENSE`.split("\n") spec.add_dependency 'graphql', '>= 1.10.14' - spec.add_runtime_dependency 'google-protobuf', '~> 3.22' + spec.add_dependency 'google-protobuf', '~> 3.22' spec.add_development_dependency 'actionpack' spec.add_development_dependency 'appraisal' - spec.add_development_dependency 'debase', '0.2.5.beta2' spec.add_development_dependency 'pry-byebug' spec.add_development_dependency 'rack' spec.add_development_dependency 'rake' spec.add_development_dependency 'rspec' - spec.add_development_dependency 'rubocop', '~> 0.75.0' + spec.add_development_dependency 'rubocop', '~> 1.68.0' spec.add_development_dependency 'rubocop-rspec' - spec.add_development_dependency 'ruby-debug-ide' spec.add_development_dependency 'webrick' end diff --git a/lib/apollo-federation/entities_field.rb b/lib/apollo-federation/entities_field.rb index b9825dd19..d72372352 100644 --- a/lib/apollo-federation/entities_field.rb +++ b/lib/apollo-federation/entities_field.rb @@ -26,6 +26,7 @@ def define_entities_field(possible_entities) end end + # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity def _entities(representations:) final_result = Array.new(representations.size) grouped_references_with_indices = @@ -85,6 +86,7 @@ def _entities(representations:) final_result end end + # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity private diff --git a/lib/apollo-federation/schema.rb b/lib/apollo-federation/schema.rb index 1151154c7..eeb18b205 100644 --- a/lib/apollo-federation/schema.rb +++ b/lib/apollo-federation/schema.rb @@ -3,7 +3,7 @@ require 'apollo-federation/entities_field' require 'apollo-federation/service_field' require 'apollo-federation/entity' -require 'apollo-federation/federated_document_from_schema_definition.rb' +require 'apollo-federation/federated_document_from_schema_definition' module ApolloFederation module Schema diff --git a/lib/apollo-federation/tracing/node_map.rb b/lib/apollo-federation/tracing/node_map.rb index e298a7116..94c7e7c79 100644 --- a/lib/apollo-federation/tracing/node_map.rb +++ b/lib/apollo-federation/tracing/node_map.rb @@ -16,6 +16,7 @@ class NodeMap ROOT_KEY = '' attr_reader :nodes + def initialize @nodes = { ROOT_KEY => ApolloFederation::Tracing::Node.new, From 60dabf87b7a26099ae458fd59d800adcb4263d8e Mon Sep 17 00:00:00 2001 From: Adam Rutkowski Date: Tue, 19 Nov 2024 11:11:22 +0100 Subject: [PATCH 13/19] Fix tests --- .github/workflows/ci.yml | 2 +- example/graphql_server.rb | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5e164f0f8..b55ba5c68 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: fail-fast: false matrix: appraisal: [ 'graphql-1.10', 'graphql-1.11', 'graphql-1.12', 'graphql-1.13', 'graphql-2.0' ] - ruby: [ '2.7', '3.0', '3.1', '3.2' ] + ruby: [ '3.0', '3.1', '3.2' ] exclude: - appraisal: 'graphql-1.10' ruby: '3.2' diff --git a/example/graphql_server.rb b/example/graphql_server.rb index ff579e8a3..4b0ed50e4 100644 --- a/example/graphql_server.rb +++ b/example/graphql_server.rb @@ -2,6 +2,7 @@ require 'rack' require 'json' +require 'ostruct' require 'graphql' require 'pry-byebug' require 'apollo-federation' From af6265a795033e664ed110cd910f2b13381c6645 Mon Sep 17 00:00:00 2001 From: Adam Rutkowski Date: Tue, 19 Nov 2024 11:16:38 +0100 Subject: [PATCH 14/19] Don't run semantic release in PRs --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b55ba5c68..dc9430850 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -83,6 +83,7 @@ jobs: release: runs-on: ubuntu-latest needs: [ ruby-lint, ruby-test, integration-tests ] + if: github.ref == 'refs/heads/main' env: BUNDLE_APP_CONFIG: .bundle steps: From 0057d5430c414abfecae5b8db0a44373bde8cff0 Mon Sep 17 00:00:00 2001 From: Adam Rutkowski Date: Wed, 20 Nov 2024 12:32:56 +0100 Subject: [PATCH 15/19] ci: split workflows --- .github/workflows/semantic-pr-title.yml | 48 +++++++++++++++++++ ...emantic.yml => semantic-release-notes.yml} | 35 +------------- 2 files changed, 49 insertions(+), 34 deletions(-) create mode 100644 .github/workflows/semantic-pr-title.yml rename .github/workflows/{semantic.yml => semantic-release-notes.yml} (69%) diff --git a/.github/workflows/semantic-pr-title.yml b/.github/workflows/semantic-pr-title.yml new file mode 100644 index 000000000..66f4260ef --- /dev/null +++ b/.github/workflows/semantic-pr-title.yml @@ -0,0 +1,48 @@ +name: "Semantic PR Title" + +on: + pull_request: + branches: + - main + types: + - opened + - edited + - synchronize + - reopened + +permissions: + pull-requests: write + +jobs: + main: + name: Validate PR title + runs-on: ubuntu-latest + steps: + - uses: amannn/action-semantic-pull-request@v5 + id: lint_pr_title + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - uses: marocchino/sticky-pull-request-comment@v2 + # When the previous steps fails, the workflow would stop. By adding this + # condition you can continue the execution with the populated error message. + if: always() && (steps.lint_pr_title.outputs.error_message != null) + with: + header: pr-title-lint-error + message: | + Hey there and thank you for opening this pull request! 👋🏼 + + We require pull request titles to follow the [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/) and it looks like your proposed title needs to be adjusted. + + Details: + + ``` + ${{ steps.lint_pr_title.outputs.error_message }} + ``` + + # Delete a previous comment when the issue has been resolved + - if: ${{ steps.lint_pr_title.outputs.error_message == null }} + uses: marocchino/sticky-pull-request-comment@v2 + with: + header: pr-title-lint-error + delete: true \ No newline at end of file diff --git a/.github/workflows/semantic.yml b/.github/workflows/semantic-release-notes.yml similarity index 69% rename from .github/workflows/semantic.yml rename to .github/workflows/semantic-release-notes.yml index 1cde66065..91c61ad03 100644 --- a/.github/workflows/semantic.yml +++ b/.github/workflows/semantic-release-notes.yml @@ -1,4 +1,4 @@ -name: "Semantic Pull Request" +name: "Semantic Release Notes" on: pull_request: @@ -10,39 +10,6 @@ permissions: jobs: main: - name: Validate PR title - runs-on: ubuntu-latest - steps: - - uses: amannn/action-semantic-pull-request@v5 - id: lint_pr_title - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - uses: marocchino/sticky-pull-request-comment@v2 - # When the previous steps fails, the workflow would stop. By adding this - # condition you can continue the execution with the populated error message. - if: always() && (steps.lint_pr_title.outputs.error_message != null) - with: - header: pr-title-lint-error - message: | - Hey there and thank you for opening this pull request! 👋🏼 - - We require pull request titles to follow the [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/) and it looks like your proposed title needs to be adjusted. - - Details: - - ``` - ${{ steps.lint_pr_title.outputs.error_message }} - ``` - - # Delete a previous comment when the issue has been resolved - - if: ${{ steps.lint_pr_title.outputs.error_message == null }} - uses: marocchino/sticky-pull-request-comment@v2 - with: - header: pr-title-lint-error - delete: true - - semantic-versioning: name: Report semantic changelog runs-on: ubuntu-latest steps: From b8d7d2f444290e2a74f7285b851579d810e0e11f Mon Sep 17 00:00:00 2001 From: Adam Rutkowski Date: Wed, 20 Nov 2024 12:34:53 +0100 Subject: [PATCH 16/19] delete semantic release notes (won't work) --- .github/workflows/semantic-release-notes.yml | 83 -------------------- 1 file changed, 83 deletions(-) delete mode 100644 .github/workflows/semantic-release-notes.yml diff --git a/.github/workflows/semantic-release-notes.yml b/.github/workflows/semantic-release-notes.yml deleted file mode 100644 index 91c61ad03..000000000 --- a/.github/workflows/semantic-release-notes.yml +++ /dev/null @@ -1,83 +0,0 @@ -name: "Semantic Release Notes" - -on: - pull_request: - branches: - - main - -permissions: - pull-requests: write - -jobs: - main: - name: Report semantic changelog - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Run install - uses: borales/actions-yarn@v4 - with: - cmd: install - - name: Check semantic versioning - id: semantic-release - run: | - GITHUB_REF=${{ github.head_ref }} - npx semantic-release --no-ci --dry-run --plugins @semantic-release/commit-analyzer,@semantic-release/release-notes-generator --branches ${{ github.head_ref }} > output.txt - OUTPUT=$(cat output.txt | base64 -w 0) - echo "::set-output name=releaseNote::$OUTPUT" - - - name: Report semantic versioning - uses: actions/github-script@v3 - if: ${{ steps.semantic-release.outputs.releaseNote != '' }} - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - // build release note - const semanticReleaseOutput = Buffer.from('${{ steps.semantic-release.outputs.releaseNote }}', 'base64').toString('utf8'); - const semanticReleaseLogMatch = /^[[0-9:\sAMPM]+\]\s\[semantic-release\].*$/; - const lines = semanticReleaseOutput.split('\n'); - const lastSemanticReleaseLogIndex = [...lines] - .reverse() - .findIndex((line) => line.match(semanticReleaseLogMatch)); - - const releaseNoteIndex = lines.length - lastSemanticReleaseLogIndex; - const releaseNote = lines.slice(releaseNoteIndex); - - let res = releaseNote.join('\n'); - if (!releaseNote.length || !res) { - res = '### No release note would be generated.'; - } - - const SEMANTIC_RELEASE_BODY_HEADER = '## 📝 Semantic Release Report'; - const body = [SEMANTIC_RELEASE_BODY_HEADER, res].join('\n'); - - // get last comment - const comments = await github.issues.listComments({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo - }); - - // find comments to delete - const commentsToDelete = comments.data.filter((comment) => - comment.body.startsWith(SEMANTIC_RELEASE_BODY_HEADER) - ); - - // delete comments - const prms = commentsToDelete.map((comment) => - github.issues.deleteComment({ - comment_id: comment.id, - owner: context.repo.owner, - repo: context.repo.repo - }) - ); - - await Promise.all(prms); - - // create new comment for release note - github.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body - }); \ No newline at end of file From e4f59789331353d3dad7e7a5272afb4402660a1f Mon Sep 17 00:00:00 2001 From: Adam Rutkowski Date: Wed, 20 Nov 2024 15:23:06 +0100 Subject: [PATCH 17/19] Check all links for imported directive --- .../federated_document_from_schema_definition.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/apollo-federation/federated_document_from_schema_definition.rb b/lib/apollo-federation/federated_document_from_schema_definition.rb index cb58fabeb..d5acfa004 100644 --- a/lib/apollo-federation/federated_document_from_schema_definition.rb +++ b/lib/apollo-federation/federated_document_from_schema_definition.rb @@ -100,7 +100,7 @@ def merge_directives(node, type) end def directive_name(directive) - if schema.federation_2? && !Schema::IMPORTED_DIRECTIVES.include?(directive[:name]) + if schema.federation_2? && !schema.all_links.any? { |link| link[:import]&.include?(directive[:name]) } "#{schema.default_link_namespace}__#{directive[:name]}" else directive[:name] From 4a497b8fde636fdc9b5d52f8e198d89d189b4e2e Mon Sep 17 00:00:00 2001 From: Adam Rutkowski Date: Wed, 20 Nov 2024 15:26:43 +0100 Subject: [PATCH 18/19] Make functions public --- lib/apollo-federation/schema.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/apollo-federation/schema.rb b/lib/apollo-federation/schema.rb index b5a88cfb7..0b484c9a0 100644 --- a/lib/apollo-federation/schema.rb +++ b/lib/apollo-federation/schema.rb @@ -55,12 +55,6 @@ def query(new_query_object = nil) federation_query_object end - private - - def original_query - @orig_query_object || find_inherited_value(:original_query) - end - def compose_directives @compose_directives || find_inherited_value(:compose_directives, []) end @@ -79,6 +73,12 @@ def all_links [default_link, *links] end + private + + def original_query + @orig_query_object || find_inherited_value(:original_query) + end + def federation_2_prefix schema = ['extend schema'] From a59c8e07b9a73154165a6c8c39a5a66d56b637a4 Mon Sep 17 00:00:00 2001 From: Adam Rutkowski Date: Wed, 20 Nov 2024 16:04:37 +0100 Subject: [PATCH 19/19] Invert logic to satisfy RuboCop --- .../federated_document_from_schema_definition.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/apollo-federation/federated_document_from_schema_definition.rb b/lib/apollo-federation/federated_document_from_schema_definition.rb index d5acfa004..f3ecd6bd9 100644 --- a/lib/apollo-federation/federated_document_from_schema_definition.rb +++ b/lib/apollo-federation/federated_document_from_schema_definition.rb @@ -100,7 +100,7 @@ def merge_directives(node, type) end def directive_name(directive) - if schema.federation_2? && !schema.all_links.any? { |link| link[:import]&.include?(directive[:name]) } + if schema.federation_2? && schema.all_links.none? { |link| link[:import]&.include?(directive[:name]) } "#{schema.default_link_namespace}__#{directive[:name]}" else directive[:name]