graphql-schema_comparator 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|