Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/shopify_custom_data_graphql/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
8 changes: 4 additions & 4 deletions lib/shopify_custom_data_graphql/custom_data_catalog.rb
Original file line number Diff line number Diff line change
@@ -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
Expand Down
24 changes: 16 additions & 8 deletions lib/shopify_custom_data_graphql/schema_composer.rb
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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

Expand All @@ -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)
Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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
51 changes: 51 additions & 0 deletions test/shopify_custom_data_graphql/extensions_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# frozen_string_literal: true

require "test_helper"

describe "ExtensionsSchema" do
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

def test_to_extensions_definition_builds_minimal_extensions_document
doc = ShopifyCustomDataGraphQL::SchemaComposer.new(
load_base_admin_schema,
@catalog,
).to_extensions_definition

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_equal expected.squish, doc.gsub(/"""[^"]+"""/, "").squish
end
end
4 changes: 2 additions & 2 deletions test/shopify_custom_data_graphql/response_transformer_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
7 changes: 2 additions & 5 deletions test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down