Skip to content

Commit

Permalink
Merge pull request #55 from willlockwood/lockwood/schema-root-nonbrea…
Browse files Browse the repository at this point in the history
…king-changes

Improve support for root operation type changes
  • Loading branch information
swalkinshaw authored Oct 18, 2023
2 parents 55336f0 + 681315e commit 212f1f1
Show file tree
Hide file tree
Showing 3 changed files with 168 additions and 50 deletions.
132 changes: 86 additions & 46 deletions lib/graphql/schema_comparator/changes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -201,21 +201,93 @@ def path
end
end

class SchemaQueryTypeChanged < AbstractChange
attr_reader :old_schema, :new_schema, :criticality
class RootOperationTypeAdded < AbstractChange
attr_reader :new_schema, :operation_type, :criticality

def initialize(old_schema, new_schema)
def initialize(new_schema:, operation_type:)
@new_schema = new_schema
@operation_type = operation_type
@criticality = Changes::Criticality.non_breaking(
reason: "Adding a schema #{operation_type} root is considered non-breaking."
)
end

def message
"Schema #{operation_type} root `#{operation_type_name}` was added"
end

def path
operation_type_name
end

def operation_type_name
case operation_type
when :query
new_schema.query.graphql_name
when :mutation
new_schema.mutation.graphql_name
when :subscription
new_schema.subscription.graphql_name
end
end
end

class RootOperationTypeChanged < AbstractChange
attr_reader :old_schema, :new_schema, :operation_type, :criticality

def initialize(old_schema:, new_schema:, operation_type:)
@old_schema = old_schema
@new_schema = new_schema
@operation_type = operation_type
@criticality = Changes::Criticality.breaking
end

def message
"Schema query root has changed from `#{old_schema.query.graphql_name}` to `#{new_schema.query.graphql_name}`"
"Schema #{operation_type} root has changed from `#{operation_type_name(old_schema)}` to `#{operation_type_name(new_schema)}`"
end

def path
# TODO
operation_type_name(old_schema)
end

def operation_type_name(schema)
case operation_type
when :query
schema.query.graphql_name
when :mutation
schema.mutation.graphql_name
when :subscription
schema.subscription.graphql_name
end
end
end

class RootOperationTypeRemoved < AbstractChange
attr_reader :old_schema, :operation_type, :criticality

def initialize(old_schema:, operation_type:)
@old_schema = old_schema
@operation_type = operation_type
@criticality = Changes::Criticality.breaking
end

def message
"Schema #{operation_type} root `#{operation_type_name}` was removed"
end

def path
operation_type_name
end

def operation_type_name
case operation_type
when :query
old_schema.query.graphql_name
when :mutation
old_schema.mutation.graphql_name
when :subscription
old_schema.subscription.graphql_name
end
end
end

Expand Down Expand Up @@ -404,42 +476,6 @@ def path
end
end

class SchemaMutationTypeChanged < AbstractChange
attr_reader :old_schema, :new_schema, :criticality

def initialize(old_schema, new_schema)
@old_schema = old_schema
@new_schema = new_schema
@criticality = Changes::Criticality.breaking
end

def message
"Schema mutation root has changed from `#{old_schema.mutation}` to `#{new_schema.mutation}`"
end

def path
# TODO
end
end

class SchemaSubscriptionTypeChanged < AbstractChange
attr_reader :old_schema, :new_schema, :criticality

def initialize(old_schema, new_schema)
@old_schema = old_schema
@new_schema = new_schema
@criticality = Changes::Criticality.breaking
end

def message
"Schema subscription type has changed from `#{old_schema.subscription}` to `#{new_schema.subscription}`"
end

def path
# TODO
end
end

# Dangerous Changes

class FieldArgumentDefaultChanged < AbstractChange
Expand All @@ -457,11 +493,11 @@ def initialize(type, field, old_argument, new_argument)
end

def message
if old_argument.default_value.nil? || old_argument.default_value == :__no_default__
"Default value `#{new_argument.default_value}` was added to argument `#{new_argument.graphql_name}` on field `#{field.path}`"
else
if old_argument.default_value?
"Default value for argument `#{new_argument.graphql_name}` on field `#{field.path}` changed"\
" from `#{old_argument.default_value}` to `#{new_argument.default_value}`"
else
"Default value `#{new_argument.default_value}` was added to argument `#{new_argument.graphql_name}` on field `#{field.path}`"
end
end

Expand Down Expand Up @@ -507,8 +543,12 @@ def initialize(directive, old_argument, new_argument)
end

def message
"Default value for argument `#{new_argument.graphql_name}` on directive `#{directive.graphql_name}` changed"\
" from `#{old_argument.default_value}` to `#{new_argument.default_value}`"
if old_argument.default_value?
"Default value for argument `#{new_argument.graphql_name}` on directive `#{directive.graphql_name}` changed"\
" from `#{old_argument.default_value}` to `#{new_argument.default_value}`"
else
"Default value `#{new_argument.default_value}` was added to argument `#{new_argument.graphql_name}` on directive `#{directive.graphql_name}`"
end
end

def path
Expand Down
24 changes: 21 additions & 3 deletions lib/graphql/schema_comparator/diff/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,33 @@ def changes_in_schema
changes = []

if old_schema.query&.graphql_name != new_schema.query&.graphql_name
changes << Changes::SchemaQueryTypeChanged.new(old_schema, new_schema)
if old_schema.query.nil?
changes << Changes::RootOperationTypeAdded.new(new_schema: new_schema, operation_type: :query)
elsif new_schema.query.nil?
changes << Changes::RootOperationTypeRemoved.new(old_schema: old_schema, operation_type: :query)
else
changes << Changes::RootOperationTypeChanged.new(old_schema: old_schema, new_schema: new_schema, operation_type: :query)
end
end

if old_schema.mutation&.graphql_name != new_schema.mutation&.graphql_name
changes << Changes::SchemaMutationTypeChanged.new(old_schema, new_schema)
if old_schema.mutation.nil?
changes << Changes::RootOperationTypeAdded.new(new_schema: new_schema, operation_type: :mutation)
elsif new_schema.mutation.nil?
changes << Changes::RootOperationTypeRemoved.new(old_schema: old_schema, operation_type: :mutation)
else
changes << Changes::RootOperationTypeChanged.new(old_schema: old_schema, new_schema: new_schema, operation_type: :mutation)
end
end

if old_schema.subscription&.graphql_name != new_schema.subscription&.graphql_name
changes << Changes::SchemaSubscriptionTypeChanged.new(old_schema, new_schema)
if old_schema.subscription.nil?
changes << Changes::RootOperationTypeAdded.new(new_schema: new_schema, operation_type: :subscription)
elsif new_schema.subscription.nil?
changes << Changes::RootOperationTypeRemoved.new(old_schema: old_schema, operation_type: :subscription)
else
changes << Changes::RootOperationTypeChanged.new(old_schema: old_schema, new_schema: new_schema, operation_type: :subscription)
end
end

changes
Expand Down
62 changes: 61 additions & 1 deletion test/lib/graphql/schema_comparator/diff/schema_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ def setup
@old_schema = <<~SCHEMA
schema {
query: Query
mutation: OldMutation
}
input AInput {
# a
Expand Down Expand Up @@ -67,13 +68,17 @@ def setup
type WillBeRemoved {
a: String
}
type OldMutation {
a: String!
}
directive @willBeRemoved on FIELD
SCHEMA

@new_schema =<<~SCHEMA
schema {
query: Query
mutation: Mutation
}
input AInput {
# changed
Expand Down Expand Up @@ -139,6 +144,10 @@ def setup
# Included when true.
someArg: String!
) on FIELD
type Mutation {
a: String!
}
SCHEMA

@differ = GraphQL::SchemaComparator::Diff::Schema.new(
Expand Down Expand Up @@ -168,7 +177,7 @@ def test_changes_kitchensink
"Deprecation reason on field `CType.a` has changed from `whynot` to `cuz`",
"Argument `arg: Int` added to field `CType.a`",
"Default value `10` was added to argument `arg` on field `CType.d`",
"Default value for argument `anotherArg` on directive `yolo` changed from `__no_default__` to `Test`",
"Default value `Test` was added to argument `anotherArg` on directive `yolo`",
"Union member `BType` was removed from Union type `MyUnion`",
"Union member `DType` was added to Union type `MyUnion`",
"Field `anotherInterfaceField` was removed from object type `AnotherInterface`",
Expand All @@ -192,6 +201,9 @@ def test_changes_kitchensink
"Argument `willBeRemoved` was removed from directive `yolo`",
"Description for argument `someArg` on directive `yolo` changed from `Included when true.` to `someArg does stuff`",
"Type for argument `someArg` on directive `yolo` changed from `Boolean!` to `String!`",
"Type `Mutation` was added",
"Type `OldMutation` was removed",
"Schema mutation root has changed from `OldMutation` to `Mutation`",
].sort, @differ.diff.map(&:message).sort

assert_equal [
Expand All @@ -214,8 +226,11 @@ def test_changes_kitchensink
"CType.a.arg",
"CType.d.arg",
"CType.interfaceField",
"Mutation",
"MyUnion",
"MyUnion",
"OldMutation",
"OldMutation",
"AnotherInterface.anotherInterfaceField",
"AnotherInterface.b",
"WithInterfaces",
Expand All @@ -241,6 +256,51 @@ def test_changes_kitchensink
].sort, @differ.diff.map(&:path).sort
end

def test_schema_root_changes
old_schema = <<~SCHEMA
schema {
query: OldQuery
mutation: Mutation
}
type OldQuery {
a: String!
}
type Mutation {
a: String!
}
SCHEMA

new_schema = <<~SCHEMA
schema {
query: Query
subscription: Subscription
}
type Query {
a: String!
}
type Subscription {
a: String!
}
SCHEMA

expected_changes = [
{ path: "Mutation", message: "Schema mutation root `Mutation` was removed", level: 3 },
{ path: "Mutation", message: "Type `Mutation` was removed", level: 3 },
{ path: "OldQuery", message: "Schema query root has changed from `OldQuery` to `Query`", level: 3 },
{ path: "OldQuery", message: "Type `OldQuery` was removed", level: 3 },
{ path: "Query", message: "Type `Query` was added", level: 1 },
{ path: "Subscription", message: "Schema subscription root `Subscription` was added", level: 1 },
{ path: "Subscription", message: "Type `Subscription` was added", level: 1 },
]

actual_changes = schema_diff(old_schema, new_schema)
assert_equal(normalize_schema_diff(expected_changes), normalize_schema_diff(actual_changes))
end

def test_enum_value_changes
old_schema = <<~SCHEMA
schema {
Expand Down

0 comments on commit 212f1f1

Please sign in to comment.