From 84fd0f3b60083aab10213c5ec9de2f52e2f7face Mon Sep 17 00:00:00 2001 From: Greg MacWilliam Date: Fri, 21 Mar 2025 08:18:51 -0400 Subject: [PATCH 1/2] to extensions definition. --- lib/shopify_custom_data_graphql/client.rb | 2 +- .../schema_composer.rb | 24 ++++++--- ...ensions_document_from_schema_definition.rb | 54 +++++++++++++++++++ .../extensions_test.rb | 21 ++++++++ .../response_transformer_test.rb | 4 +- test/test_helper.rb | 7 +-- 6 files changed, 96 insertions(+), 16 deletions(-) create mode 100644 lib/shopify_custom_data_graphql/schema_composer/extensions_document_from_schema_definition.rb create mode 100644 test/shopify_custom_data_graphql/extensions_test.rb diff --git a/lib/shopify_custom_data_graphql/client.rb b/lib/shopify_custom_data_graphql/client.rb index d3e04b7..54cd47c 100644 --- a/lib/shopify_custom_data_graphql/client.rb +++ b/lib/shopify_custom_data_graphql/client.rb @@ -93,7 +93,7 @@ def schema(reload_custom_schema: false, reload_admin_schema: false) prefixed_namespaces: @prefixed_namespaces, ) - SchemaComposer.new(admin_schema, catalog).perform + SchemaComposer.new(admin_schema, catalog).schema end custom_schema = GraphQL::Schema.from_definition(custom_schema) if custom_schema.is_a?(String) diff --git a/lib/shopify_custom_data_graphql/schema_composer.rb b/lib/shopify_custom_data_graphql/schema_composer.rb index 35af9fd..65d2fc9 100644 --- a/lib/shopify_custom_data_graphql/schema_composer.rb +++ b/lib/shopify_custom_data_graphql/schema_composer.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require_relative "schema_composer/extensions_document_from_schema_definition" + module ShopifyCustomDataGraphQL class SchemaComposer class MetafieldDirective < GraphQL::Schema::Directive @@ -20,13 +22,14 @@ class MetaobjectDirective < GraphQL::Schema::Directive def initialize(base_schema, catalog) @base_schema = base_schema @catalog = catalog - - introspection_names = @base_schema.introspection_system.types.keys.to_set - @schema_types = @base_schema.types.reject! { |k, v| introspection_names.include?(k) } + @schema = nil + @schema_types = @base_schema.types.reject { |k, v| v.introspection? } @metaobject_unions = {} end - def perform + def schema + return @schema unless @schema.nil? + query_name = @base_schema.query.graphql_name mutation_name = @base_schema.mutation.graphql_name @@ -50,7 +53,7 @@ def perform end builder = self - schema = Class.new(GraphQL::Schema) do + built_schema = Class.new(GraphQL::Schema) do use(GraphQL::Schema::Visibility) directive(MetafieldDirective) directive(MetaobjectDirective) @@ -69,11 +72,16 @@ def perform argument :id, builder.schema_types["ID"], required: true end - schema.directive(app_directive) - schema.schema_directive(app_directive, id: @catalog.app_id) + built_schema.directive(app_directive) + built_schema.schema_directive(app_directive, id: @catalog.app_id) end - schema + @schema = built_schema + end + + def to_extensions_definition + document = ExtensionsDocumentFromSchemaDefinition.new(schema, @base_schema).document + GraphQL::Language::Printer.new.print(document) end def type_for_metafield_definition(field_def) diff --git a/lib/shopify_custom_data_graphql/schema_composer/extensions_document_from_schema_definition.rb b/lib/shopify_custom_data_graphql/schema_composer/extensions_document_from_schema_definition.rb new file mode 100644 index 0000000..a8c7fe9 --- /dev/null +++ b/lib/shopify_custom_data_graphql/schema_composer/extensions_document_from_schema_definition.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +module ShopifyCustomDataGraphQL + class SchemaComposer + class ExtensionsDocumentFromSchemaDefinition < GraphQL::Language::DocumentFromSchemaDefinition + def initialize(schema, base_schema) + super(schema) + @base_schema = base_schema + end + + def build_directive_nodes(all_directives) + super(all_directives.reject { @base_schema.directives.key?(_1.graphql_name) }) + end + + def build_type_definition_nodes(all_types) + types_with_extensions = [@types.query_root] + .concat(@types.possible_types(@types.type("HasMetafields"))) + .to_set + + all_types = all_types + .reject { |type| type.introspection? } + .reject { |type| type.kind.scalar? && type.default_scalar? } + .reject { |type| @base_schema.get_type(type.graphql_name) && !types_with_extensions.include?(type) } + + definitions = all_types.filter_map do |type| + if types_with_extensions.include?(type) + extension_field = @types.fields(type).find { _1.graphql_name == "extensions" } + if extension_field + GraphQL::Language::Nodes::ObjectTypeExtension.new( + name: type.graphql_name, + interfaces: [], + fields: build_field_nodes([extension_field]), + ) + end + else + build_type_definition_node(type) + end + end + + if @schema.schema_directives.any? + definitions << GraphQL::Language::Nodes::SchemaExtension.new( + directives: definition_directives(@schema, :schema_directives), + ) + end + + definitions + end + + def include_schema_node? + false + end + end + end +end diff --git a/test/shopify_custom_data_graphql/extensions_test.rb b/test/shopify_custom_data_graphql/extensions_test.rb new file mode 100644 index 0000000..8372a8f --- /dev/null +++ b/test/shopify_custom_data_graphql/extensions_test.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +require "test_helper" + +describe "ExtensionsSchema" do + def test_it_works + # puts ShopifyCustomDataGraphQL::SchemaComposer.new( + # load_base_admin_schema, + # load_shop_fixtures_catalog(app_id: nil), + # ).to_extensions_definition + + # ext = GraphQL::Language::Printer.new.print(doc) + # base = File.read("#{__dir__}/../fixtures/admin_2025_01_public.graphql") + # puts ext + + # schema = GraphQL::Schema.from_definition("#{base}\n#{ext}") + + # assert schema + assert true + end +end diff --git a/test/shopify_custom_data_graphql/response_transformer_test.rb b/test/shopify_custom_data_graphql/response_transformer_test.rb index e63cac2..da76142 100644 --- a/test/shopify_custom_data_graphql/response_transformer_test.rb +++ b/test/shopify_custom_data_graphql/response_transformer_test.rb @@ -793,9 +793,9 @@ def test_transforms_errors_with_list_paths private - SCALAR_VALIDATORS = { + SCALAR_VALIDATORS = GraphQL::ResponseValidator::SCALAR_VALIDATORS.merge({ "JSON" => -> (value) { true } - }.freeze + }).freeze def fetch(fixture, document, variables: {}, operation_name: nil, schema: nil, expect_valid_response: true) query = GraphQL::Query.new( diff --git a/test/test_helper.rb b/test/test_helper.rb index b51a919..82d8d5f 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -30,13 +30,10 @@ def load_shop_fixtures_catalog(app_id: nil) end def load_shop_fixtures_schema(app_id: nil) - schema = ShopifyCustomDataGraphQL::SchemaComposer.new( + ShopifyCustomDataGraphQL::SchemaComposer.new( load_base_admin_schema, load_shop_fixtures_catalog(app_id: app_id), - ).perform - - # File.write("#{__dir__}/fixtures/admin_2025_01_#{app_id ? "app#{app_id}" : "shop"}.graphql", schema.to_definition) - schema + ).schema end $base_schema = nil From 7192f51dca12ac11fc68767e4cb9b4ce1526753c Mon Sep 17 00:00:00 2001 From: Greg MacWilliam Date: Tue, 27 May 2025 22:42:45 -0400 Subject: [PATCH 2/2] cleanups. --- .../custom_data_catalog.rb | 8 +- .../fetch.rb | 0 .../metafield_definition.rb | 0 .../metaobject_definition.rb | 0 .../metaobject_union.rb | 0 ...ensions_document_from_schema_definition.rb | 82 +++++++++---------- .../extensions_test.rb | 52 +++++++++--- 7 files changed, 86 insertions(+), 56 deletions(-) rename lib/shopify_custom_data_graphql/{schema_catalog => custom_data_catalog}/fetch.rb (100%) rename lib/shopify_custom_data_graphql/{schema_catalog => custom_data_catalog}/metafield_definition.rb (100%) rename lib/shopify_custom_data_graphql/{schema_catalog => custom_data_catalog}/metaobject_definition.rb (100%) rename lib/shopify_custom_data_graphql/{schema_catalog => custom_data_catalog}/metaobject_union.rb (100%) diff --git a/lib/shopify_custom_data_graphql/custom_data_catalog.rb b/lib/shopify_custom_data_graphql/custom_data_catalog.rb index 6a417ea..37120a6 100644 --- a/lib/shopify_custom_data_graphql/custom_data_catalog.rb +++ b/lib/shopify_custom_data_graphql/custom_data_catalog.rb @@ -1,9 +1,9 @@ # frozen_string_literal: true -require_relative "schema_catalog/metafield_definition" -require_relative "schema_catalog/metaobject_definition" -require_relative "schema_catalog/metaobject_union" -require_relative "schema_catalog/fetch" +require_relative "custom_data_catalog/metafield_definition" +require_relative "custom_data_catalog/metaobject_definition" +require_relative "custom_data_catalog/metaobject_union" +require_relative "custom_data_catalog/fetch" module ShopifyCustomDataGraphQL class CustomDataCatalog diff --git a/lib/shopify_custom_data_graphql/schema_catalog/fetch.rb b/lib/shopify_custom_data_graphql/custom_data_catalog/fetch.rb similarity index 100% rename from lib/shopify_custom_data_graphql/schema_catalog/fetch.rb rename to lib/shopify_custom_data_graphql/custom_data_catalog/fetch.rb diff --git a/lib/shopify_custom_data_graphql/schema_catalog/metafield_definition.rb b/lib/shopify_custom_data_graphql/custom_data_catalog/metafield_definition.rb similarity index 100% rename from lib/shopify_custom_data_graphql/schema_catalog/metafield_definition.rb rename to lib/shopify_custom_data_graphql/custom_data_catalog/metafield_definition.rb diff --git a/lib/shopify_custom_data_graphql/schema_catalog/metaobject_definition.rb b/lib/shopify_custom_data_graphql/custom_data_catalog/metaobject_definition.rb similarity index 100% rename from lib/shopify_custom_data_graphql/schema_catalog/metaobject_definition.rb rename to lib/shopify_custom_data_graphql/custom_data_catalog/metaobject_definition.rb diff --git a/lib/shopify_custom_data_graphql/schema_catalog/metaobject_union.rb b/lib/shopify_custom_data_graphql/custom_data_catalog/metaobject_union.rb similarity index 100% rename from lib/shopify_custom_data_graphql/schema_catalog/metaobject_union.rb rename to lib/shopify_custom_data_graphql/custom_data_catalog/metaobject_union.rb diff --git a/lib/shopify_custom_data_graphql/schema_composer/extensions_document_from_schema_definition.rb b/lib/shopify_custom_data_graphql/schema_composer/extensions_document_from_schema_definition.rb index a8c7fe9..91cfe95 100644 --- a/lib/shopify_custom_data_graphql/schema_composer/extensions_document_from_schema_definition.rb +++ b/lib/shopify_custom_data_graphql/schema_composer/extensions_document_from_schema_definition.rb @@ -1,54 +1,54 @@ # frozen_string_literal: true module ShopifyCustomDataGraphQL - class SchemaComposer - class ExtensionsDocumentFromSchemaDefinition < GraphQL::Language::DocumentFromSchemaDefinition - def initialize(schema, base_schema) - super(schema) - @base_schema = base_schema - end + class SchemaComposer + class ExtensionsDocumentFromSchemaDefinition < GraphQL::Language::DocumentFromSchemaDefinition + def initialize(schema, base_schema) + super(schema) + @base_schema = base_schema + end def build_directive_nodes(all_directives) super(all_directives.reject { @base_schema.directives.key?(_1.graphql_name) }) end - def build_type_definition_nodes(all_types) - types_with_extensions = [@types.query_root] - .concat(@types.possible_types(@types.type("HasMetafields"))) - .to_set - - all_types = all_types - .reject { |type| type.introspection? } - .reject { |type| type.kind.scalar? && type.default_scalar? } - .reject { |type| @base_schema.get_type(type.graphql_name) && !types_with_extensions.include?(type) } - - definitions = all_types.filter_map do |type| - if types_with_extensions.include?(type) - extension_field = @types.fields(type).find { _1.graphql_name == "extensions" } - if extension_field - GraphQL::Language::Nodes::ObjectTypeExtension.new( + def build_type_definition_nodes(all_types) + types_with_extensions = [@types.query_root] + .concat(@types.possible_types(@types.type("HasMetafields"))) + .to_set + + all_types = all_types + .reject { |type| type.introspection? } + .reject { |type| type.kind.scalar? && type.default_scalar? } + .reject { |type| @base_schema.get_type(type.graphql_name) && !types_with_extensions.include?(type) } + + definitions = all_types.filter_map do |type| + if types_with_extensions.include?(type) + extension_field = @types.fields(type).find { _1.graphql_name == "extensions" } + if extension_field + GraphQL::Language::Nodes::ObjectTypeExtension.new( name: type.graphql_name, interfaces: [], fields: build_field_nodes([extension_field]), ) - end - else - build_type_definition_node(type) - end - end - - if @schema.schema_directives.any? - definitions << GraphQL::Language::Nodes::SchemaExtension.new( - directives: definition_directives(@schema, :schema_directives), - ) - end - - definitions - end - - def include_schema_node? - false - end - end - end + end + else + build_type_definition_node(type) + end + end + + if @schema.schema_directives.any? + definitions << GraphQL::Language::Nodes::SchemaExtension.new( + directives: definition_directives(@schema, :schema_directives), + ) + end + + definitions + end + + def include_schema_node? + false + end + end + end end diff --git a/test/shopify_custom_data_graphql/extensions_test.rb b/test/shopify_custom_data_graphql/extensions_test.rb index 8372a8f..ecdebd7 100644 --- a/test/shopify_custom_data_graphql/extensions_test.rb +++ b/test/shopify_custom_data_graphql/extensions_test.rb @@ -3,19 +3,49 @@ require "test_helper" describe "ExtensionsSchema" do - def test_it_works - # puts ShopifyCustomDataGraphQL::SchemaComposer.new( - # load_base_admin_schema, - # load_shop_fixtures_catalog(app_id: nil), - # ).to_extensions_definition + def setup + @catalog = ShopifyCustomDataGraphQL::CustomDataCatalog.new + @catalog.add_metafield({ + "key" => "metaobject_reference", + "namespace" => "custom", + "type" => { "name" => "metaobject_reference" }, + "validations" => [{ + "name" => "metaobject_definition_id", + "value" => "gid://shopify/MetaobjectDefinition/Taco" + }], + "ownerType" => "PRODUCT" + }) + @catalog.add_metaobject({ + "id" => "gid://shopify/MetaobjectDefinition/Taco", + "name" => "Taco", + "type" => "taco", + "fieldDefinitions" => [{ + "key" => "name", + "required" => false, + "type" => { "name" => "single_line_text_field" }, + "validations" => [] + }] + }) + end - # ext = GraphQL::Language::Printer.new.print(doc) - # base = File.read("#{__dir__}/../fixtures/admin_2025_01_public.graphql") - # puts ext + def test_to_extensions_definition_builds_minimal_extensions_document + doc = ShopifyCustomDataGraphQL::SchemaComposer.new( + load_base_admin_schema, + @catalog, + ).to_extensions_definition - # schema = GraphQL::Schema.from_definition("#{base}\n#{ext}") + expected = %| + directive @metafield(key: String!, type: String!) on FIELD_DEFINITION + directive @metaobject(type: String!) on OBJECT + extend type Product { extensions: ProductExtensions! } + type ProductExtensions { metaobjectReference: TacoMetaobject @metafield(key: "custom.metaobject_reference", type: "metaobject_reference") } + extend type QueryRoot { extensions: QueryRootExtensions! } + type QueryRootExtensions { tacoMetaobjects(after: String, before: String, first: Int, last: Int): TacoMetaobjectConnection } + type TacoMetaobject @metaobject(type: "taco") { handle: String! id: ID! name: String @metafield(key: "name", type: "single_line_text_field") system: Metaobject! } + type TacoMetaobjectConnection { edges: [TacoMetaobjectEdge!]! nodes: [TacoMetaobject!]! pageInfo: PageInfo! } + type TacoMetaobjectEdge { cursor: String! node: TacoMetaobject } + | - # assert schema - assert true + assert_equal expected.squish, doc.gsub(/"""[^"]+"""/, "").squish end end