graphql-schema_comparator 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +88 -0
- data/Rakefile +17 -0
- data/bin/console +14 -0
- data/bin/graphql-schema +53 -0
- data/bin/setup +8 -0
- data/graphql-schema_comparator.gemspec +31 -0
- data/lib/graphql/schema_comparator.rb +29 -0
- data/lib/graphql/schema_comparator/changes.rb +608 -0
- data/lib/graphql/schema_comparator/diff/argument.rb +44 -0
- data/lib/graphql/schema_comparator/diff/enum.rb +58 -0
- data/lib/graphql/schema_comparator/diff/field.rb +77 -0
- data/lib/graphql/schema_comparator/diff/input_field.rb +39 -0
- data/lib/graphql/schema_comparator/diff/input_object.rb +51 -0
- data/lib/graphql/schema_comparator/diff/interface.rb +51 -0
- data/lib/graphql/schema_comparator/diff/object_type.rb +74 -0
- data/lib/graphql/schema_comparator/diff/schema.rb +117 -0
- data/lib/graphql/schema_comparator/diff/union.rb +38 -0
- data/lib/graphql/schema_comparator/result.rb +23 -0
- data/lib/graphql/schema_comparator/version.rb +5 -0
- data/schema.graphql +1 -0
- metadata +154 -0
@@ -0,0 +1,44 @@
|
|
1
|
+
module GraphQL
|
2
|
+
module SchemaComparator
|
3
|
+
module Diff
|
4
|
+
class Argument
|
5
|
+
def initialize(type, field, old_arg, new_arg)
|
6
|
+
@type = type
|
7
|
+
@field = field
|
8
|
+
|
9
|
+
@old_arg = old_arg
|
10
|
+
@new_arg = new_arg
|
11
|
+
end
|
12
|
+
|
13
|
+
def diff
|
14
|
+
changes = []
|
15
|
+
|
16
|
+
if old_arg.description != new_arg.description
|
17
|
+
changes << Changes::FieldArgumentDescriptionChanged.new(type, field, old_arg, new_arg)
|
18
|
+
end
|
19
|
+
|
20
|
+
if old_arg.default_value != new_arg.default_value
|
21
|
+
changes << Changes::FieldArgumentDefaultChanged.new(type, field, old_arg, new_arg)
|
22
|
+
end
|
23
|
+
|
24
|
+
if old_arg.type != new_arg.type
|
25
|
+
changes << Changes::FieldArgumentTypeChanged.new(type, field, old_arg, new_arg)
|
26
|
+
end
|
27
|
+
|
28
|
+
# TODO directives
|
29
|
+
|
30
|
+
changes
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
attr_reader(
|
36
|
+
:type,
|
37
|
+
:field,
|
38
|
+
:new_arg,
|
39
|
+
:old_arg
|
40
|
+
)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module GraphQL
|
2
|
+
module SchemaComparator
|
3
|
+
module Diff
|
4
|
+
class Enum
|
5
|
+
def initialize(old_type, new_type)
|
6
|
+
@old_type = old_type
|
7
|
+
@new_type = new_type
|
8
|
+
|
9
|
+
@old_values = old_type.values
|
10
|
+
@new_values = new_type.values
|
11
|
+
end
|
12
|
+
|
13
|
+
def diff
|
14
|
+
changes = []
|
15
|
+
|
16
|
+
changes += removed_values.map { |value| Changes::EnumValueRemoved.new(old_type, value) }
|
17
|
+
changes += added_values.map { |value| Changes::EnumValueAdded.new(new_type, value) }
|
18
|
+
|
19
|
+
each_common_value do |old_value, new_value|
|
20
|
+
# TODO: Add Directive Stuff
|
21
|
+
|
22
|
+
if old_value.description != new_value.description
|
23
|
+
changes += Changes::EnumValueDescriptionChanged.new(old_value, new_value)
|
24
|
+
end
|
25
|
+
|
26
|
+
if old_value.deprecation_reason != new_value.deprecation_reason
|
27
|
+
changes += Changes::EnumValueDeprecated.new(old_value, new_value)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
changes
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
attr_reader :old_type, :new_type, :old_values, :new_values
|
37
|
+
|
38
|
+
def each_common_value(&block)
|
39
|
+
intersection = old_values.keys & new_values.keys
|
40
|
+
intersection.each do |common_value|
|
41
|
+
old_value = old_type.values[common_value]
|
42
|
+
new_value = new_type.values[common_value]
|
43
|
+
|
44
|
+
block.call(old_value, new_value)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def removed_values
|
49
|
+
(old_values.keys - new_values.keys).map { |removed| old_type.values[removed] }
|
50
|
+
end
|
51
|
+
|
52
|
+
def added_values
|
53
|
+
(new_values.keys - old_values.keys).map { |added| new_type.values[added] }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module GraphQL
|
2
|
+
module SchemaComparator
|
3
|
+
module Diff
|
4
|
+
class Field
|
5
|
+
def initialize(old_type, new_type, old_field, new_field)
|
6
|
+
@old_type = old_type
|
7
|
+
@new_type = new_type
|
8
|
+
|
9
|
+
@old_field = old_field
|
10
|
+
@new_field = new_field
|
11
|
+
|
12
|
+
@old_arguments = old_field.arguments
|
13
|
+
@new_arguments = new_field.arguments
|
14
|
+
end
|
15
|
+
|
16
|
+
def diff
|
17
|
+
changes = []
|
18
|
+
|
19
|
+
if old_field.description != new_field.description
|
20
|
+
changes << Changes::FieldDescriptionChanged.new(new_type, old_field, new_field)
|
21
|
+
end
|
22
|
+
|
23
|
+
if old_field.deprecation_reason != new_field.deprecation_reason
|
24
|
+
changes << Changes::FieldDeprecationChanged.new(new_type, old_field, new_field)
|
25
|
+
end
|
26
|
+
|
27
|
+
if old_field.type != new_field.type
|
28
|
+
changes << Changes::FieldTypeChanged.new(new_type, old_field, new_field)
|
29
|
+
end
|
30
|
+
|
31
|
+
changes += arg_removals
|
32
|
+
|
33
|
+
changes += arg_additions
|
34
|
+
|
35
|
+
each_common_argument do |old_arg, new_arg|
|
36
|
+
changes += Diff::Argument.new(new_type, new_field, old_arg, new_arg).diff
|
37
|
+
end
|
38
|
+
|
39
|
+
# TODO: directives
|
40
|
+
|
41
|
+
changes
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
attr_reader(
|
47
|
+
:old_type,
|
48
|
+
:new_type,
|
49
|
+
:new_field,
|
50
|
+
:old_field,
|
51
|
+
:old_arguments,
|
52
|
+
:new_arguments
|
53
|
+
)
|
54
|
+
|
55
|
+
def arg_removals
|
56
|
+
removed = old_arguments.values.select { |arg| !new_arguments[arg.name] }
|
57
|
+
removed.map { |arg| Changes::FieldArgumentRemoved.new(new_type, old_field, arg) }
|
58
|
+
end
|
59
|
+
|
60
|
+
def arg_additions
|
61
|
+
removed = new_arguments.values.select { |arg| !old_arguments[arg.name] }
|
62
|
+
removed.map { |arg| Changes::FieldArgumentAdded.new(new_type, new_field, arg) }
|
63
|
+
end
|
64
|
+
|
65
|
+
def each_common_argument(&block)
|
66
|
+
intersection = old_arguments.keys & new_arguments.keys
|
67
|
+
intersection.each do |common_arg|
|
68
|
+
old_arg = old_field.arguments[common_arg]
|
69
|
+
new_arg = new_field.arguments[common_arg]
|
70
|
+
|
71
|
+
block.call(old_arg, new_arg)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module GraphQL
|
2
|
+
module SchemaComparator
|
3
|
+
module Diff
|
4
|
+
class InputField
|
5
|
+
def initialize(old_type, new_type, old_field, new_field)
|
6
|
+
@old_type = old_type
|
7
|
+
@new_type = new_type
|
8
|
+
|
9
|
+
@old_field = old_field
|
10
|
+
@new_field = new_field
|
11
|
+
end
|
12
|
+
|
13
|
+
def diff
|
14
|
+
changes = []
|
15
|
+
|
16
|
+
if old_field.description != new_field.description
|
17
|
+
changes << Changes::InputFieldDescriptionChanged.new(old_type, old_field, new_field)
|
18
|
+
end
|
19
|
+
|
20
|
+
if old_field.default_value != new_field.default_value
|
21
|
+
changes << Changes::InputFieldDefaultChanged.new(old_type, old_field, new_field)
|
22
|
+
end
|
23
|
+
|
24
|
+
if old_field.type != new_field.type
|
25
|
+
changes << Changes::InputFieldTypeChanged.new(old_type, old_field, new_field)
|
26
|
+
end
|
27
|
+
|
28
|
+
# TODO: directives
|
29
|
+
|
30
|
+
changes
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
attr_reader :old_type, :new_type, :new_field, :old_field
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module GraphQL
|
2
|
+
module SchemaComparator
|
3
|
+
module Diff
|
4
|
+
class InputObject
|
5
|
+
def initialize(old_type, new_type)
|
6
|
+
@old_type = old_type
|
7
|
+
@new_type = new_type
|
8
|
+
|
9
|
+
@old_fields = old_type.arguments
|
10
|
+
@new_fields = new_type.arguments
|
11
|
+
end
|
12
|
+
|
13
|
+
def diff
|
14
|
+
changes = []
|
15
|
+
|
16
|
+
changes += removed_fields.map { |field| Changes::InputFieldRemoved.new(old_type, field) }
|
17
|
+
changes += added_fields.map { |field| Changes::InputFieldAdded.new(new_type, field) }
|
18
|
+
|
19
|
+
each_common_field do |old_field, new_field|
|
20
|
+
# TODO: Add Directive Stuff
|
21
|
+
changes += InputField.new(old_type, new_type, old_field, new_field).diff
|
22
|
+
end
|
23
|
+
|
24
|
+
changes
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
attr_reader :old_type, :new_type, :old_fields, :new_fields
|
30
|
+
|
31
|
+
def each_common_field(&block)
|
32
|
+
intersection = old_fields.keys & new_fields.keys
|
33
|
+
intersection.each do |common_field|
|
34
|
+
old_field = old_type.arguments[common_field]
|
35
|
+
new_field = new_type.arguments[common_field]
|
36
|
+
|
37
|
+
block.call(old_field, new_field)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def removed_fields
|
42
|
+
old_fields.values.select { |field| !new_fields[field.name] }
|
43
|
+
end
|
44
|
+
|
45
|
+
def added_fields
|
46
|
+
new_fields.values.select { |field| !old_fields[field.name] }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module GraphQL
|
2
|
+
module SchemaComparator
|
3
|
+
module Diff
|
4
|
+
class Interface
|
5
|
+
def initialize(old_iface, new_iface)
|
6
|
+
@old_iface = old_iface
|
7
|
+
@new_iface = new_iface
|
8
|
+
|
9
|
+
@old_fields = old_iface.fields
|
10
|
+
@new_fields = new_iface.fields
|
11
|
+
end
|
12
|
+
|
13
|
+
def diff
|
14
|
+
changes = []
|
15
|
+
changes += field_removals
|
16
|
+
changes += field_additions
|
17
|
+
|
18
|
+
each_common_field do |old_field, new_field|
|
19
|
+
changes += Diff::Field.new(old_iface, new_iface, old_field, new_field).diff
|
20
|
+
end
|
21
|
+
|
22
|
+
changes
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
attr_reader :old_iface, :new_iface, :old_fields, :new_fields
|
28
|
+
|
29
|
+
def field_removals
|
30
|
+
removed = old_fields.values.select { |field| !new_fields[field.name] }
|
31
|
+
removed.map { |field| Changes::FieldRemoved.new(old_iface, field) }
|
32
|
+
end
|
33
|
+
|
34
|
+
def field_additions
|
35
|
+
added = new_fields.values.select { |field| !old_fields[field.name] }
|
36
|
+
added.map { |field| Changes::FieldAdded.new(new_iface, field) }
|
37
|
+
end
|
38
|
+
|
39
|
+
def each_common_field(&block)
|
40
|
+
intersection = old_fields.keys & new_fields.keys
|
41
|
+
intersection.each do |common_field|
|
42
|
+
old_field = old_iface.fields[common_field]
|
43
|
+
new_field = new_iface.fields[common_field]
|
44
|
+
|
45
|
+
block.call(old_field, new_field)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module GraphQL
|
2
|
+
module SchemaComparator
|
3
|
+
module Diff
|
4
|
+
class ObjectType
|
5
|
+
def initialize(old_type, new_type)
|
6
|
+
@old_type = old_type
|
7
|
+
@new_type = new_type
|
8
|
+
|
9
|
+
@old_fields = old_type.fields
|
10
|
+
@new_fields = new_type.fields
|
11
|
+
|
12
|
+
@old_interfaces = old_type.interfaces
|
13
|
+
@new_interfaces = new_type.interfaces
|
14
|
+
end
|
15
|
+
|
16
|
+
def diff
|
17
|
+
changes = []
|
18
|
+
|
19
|
+
changes += interface_additions
|
20
|
+
changes += interface_removals
|
21
|
+
changes += field_removals
|
22
|
+
changes += field_additions
|
23
|
+
|
24
|
+
each_common_field do |old_field, new_field|
|
25
|
+
changes += Diff::Field.new(old_type, new_type, old_field, new_field).diff
|
26
|
+
end
|
27
|
+
|
28
|
+
changes
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
attr_reader(
|
34
|
+
:old_type,
|
35
|
+
:new_type,
|
36
|
+
:old_fields,
|
37
|
+
:new_fields,
|
38
|
+
:old_interfaces,
|
39
|
+
:new_interfaces
|
40
|
+
)
|
41
|
+
|
42
|
+
def interface_removals
|
43
|
+
removed = old_interfaces.select { |iface| !new_interfaces.include?(iface) }
|
44
|
+
removed.map { |iface| Changes::ObjectTypeInterfaceRemoved.new(iface, old_type) }
|
45
|
+
end
|
46
|
+
|
47
|
+
def interface_additions
|
48
|
+
added = new_interfaces.select { |iface| !old_interfaces.include?(iface) }
|
49
|
+
added.map { |iface| Changes::ObjectTypeInterfaceAdded.new(iface, new_type) }
|
50
|
+
end
|
51
|
+
|
52
|
+
def field_removals
|
53
|
+
removed = old_fields.values.select { |field| !new_fields[field.name] }
|
54
|
+
removed.map { |field| Changes::FieldRemoved.new(old_type, field) }
|
55
|
+
end
|
56
|
+
|
57
|
+
def field_additions
|
58
|
+
added = new_fields.values.select { |field| !old_fields[field.name] }
|
59
|
+
added.map { |field| Changes::FieldAdded.new(new_type, field) }
|
60
|
+
end
|
61
|
+
|
62
|
+
def each_common_field(&block)
|
63
|
+
intersection = old_fields.keys & new_fields.keys
|
64
|
+
intersection.each do |common_field|
|
65
|
+
old_field = old_type.fields[common_field]
|
66
|
+
new_field = new_type.fields[common_field]
|
67
|
+
|
68
|
+
block.call(old_field, new_field)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require "graphql/schema_comparator/diff/enum"
|
2
|
+
require "graphql/schema_comparator/diff/union"
|
3
|
+
require "graphql/schema_comparator/diff/input_object"
|
4
|
+
require "graphql/schema_comparator/diff/input_field"
|
5
|
+
require "graphql/schema_comparator/diff/object_type"
|
6
|
+
require "graphql/schema_comparator/diff/interface"
|
7
|
+
require "graphql/schema_comparator/diff/field"
|
8
|
+
require "graphql/schema_comparator/diff/argument"
|
9
|
+
|
10
|
+
module GraphQL
|
11
|
+
module SchemaComparator
|
12
|
+
module Diff
|
13
|
+
class Schema
|
14
|
+
def initialize(old_schema, new_schema)
|
15
|
+
@old_schema = old_schema
|
16
|
+
@new_schema = new_schema
|
17
|
+
|
18
|
+
@old_types = old_schema.types
|
19
|
+
@new_types = new_schema.types
|
20
|
+
end
|
21
|
+
|
22
|
+
def diff
|
23
|
+
changes = []
|
24
|
+
|
25
|
+
# Removed and Added Types
|
26
|
+
changes += removed_types.map { |type| Changes::TypeRemoved.new(type) }
|
27
|
+
changes += added_types.map { |type| Changes::TypeAdded.new(type) }
|
28
|
+
|
29
|
+
# Type Diff for common types
|
30
|
+
each_common_type do |old_type, new_type|
|
31
|
+
changes += changes_in_type(old_type, new_type)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Diff Schemas
|
35
|
+
changes += changes_in_schema
|
36
|
+
|
37
|
+
# Diff Directives
|
38
|
+
changes += changes_in_directives
|
39
|
+
|
40
|
+
changes
|
41
|
+
end
|
42
|
+
|
43
|
+
def changes_in_type(old_type, new_type)
|
44
|
+
changes = []
|
45
|
+
|
46
|
+
if old_type.kind != new_type.kind
|
47
|
+
changes << Changes::TypeKindChanged.new(old_type, new_type)
|
48
|
+
else
|
49
|
+
case old_type
|
50
|
+
when GraphQL::EnumType
|
51
|
+
changes += Diff::Enum.new(old_type, new_type).diff
|
52
|
+
when GraphQL::UnionType
|
53
|
+
changes += Diff::Union.new(old_type, new_type).diff
|
54
|
+
when GraphQL::InputObjectType
|
55
|
+
changes += Diff::InputObject.new(old_type, new_type).diff
|
56
|
+
when GraphQL::ObjectType
|
57
|
+
changes += Diff::ObjectType.new(old_type, new_type).diff
|
58
|
+
when GraphQL::InterfaceType
|
59
|
+
changes += Diff::Interface.new(old_type, new_type).diff
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
if old_type.description != new_type.description
|
64
|
+
changes << Changes::TypeDescriptionChanged.new(old_type, new_type)
|
65
|
+
end
|
66
|
+
|
67
|
+
changes
|
68
|
+
end
|
69
|
+
|
70
|
+
def changes_in_schema
|
71
|
+
changes = []
|
72
|
+
|
73
|
+
if old_schema.query != new_schema.query
|
74
|
+
changes << Changes::SchemaQueryTypeChanged.new(old_schema, new_schema)
|
75
|
+
end
|
76
|
+
|
77
|
+
if old_schema.mutation != new_schema.mutation
|
78
|
+
changes << Changes::SchemaMutationTypeChanged.new(old_schema, new_schema)
|
79
|
+
end
|
80
|
+
|
81
|
+
if old_schema.subscription != new_schema.subscription
|
82
|
+
changes << Changes::SchemaSubscriptionTypeChanged.new(old_schema, new_schema)
|
83
|
+
end
|
84
|
+
|
85
|
+
changes
|
86
|
+
end
|
87
|
+
|
88
|
+
def changes_in_directives
|
89
|
+
# TODO
|
90
|
+
[]
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def each_common_type(&block)
|
96
|
+
intersection = old_types.keys & new_types.keys
|
97
|
+
intersection.each do |common_type_name|
|
98
|
+
old_type = old_schema.types[common_type_name]
|
99
|
+
new_type = new_schema.types[common_type_name]
|
100
|
+
|
101
|
+
block.call(old_type, new_type)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def removed_types
|
106
|
+
(old_types.keys - new_types.keys).map { |type_name| old_schema.types[type_name] }
|
107
|
+
end
|
108
|
+
|
109
|
+
def added_types
|
110
|
+
(new_types.keys - old_types.keys).map { |type_name| new_schema.types[type_name] }
|
111
|
+
end
|
112
|
+
|
113
|
+
attr_reader :old_schema, :new_schema, :old_types, :new_types
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|