graphql-schema_comparator 0.4.0 → 0.5.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 +4 -4
- data/CHANGELOG.md +18 -0
- data/README.md +27 -22
- data/bin/graphql-schema +2 -0
- data/bin/schema_comparator +55 -0
- data/graphql-schema_comparator.gemspec +1 -1
- data/lib/graphql/schema_comparator.rb +7 -1
- data/lib/graphql/schema_comparator/changes.rb +352 -431
- data/lib/graphql/schema_comparator/changes/criticality.rb +88 -0
- data/lib/graphql/schema_comparator/changes/safe_type_change.rb +34 -0
- data/lib/graphql/schema_comparator/result.rb +10 -3
- data/lib/graphql/schema_comparator/version.rb +1 -1
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2bd04f7cdf16f7bc67fb74e219478457b180e022
|
4
|
+
data.tar.gz: eb021f9745da0a4fed5fb2cedfb5a848ca1685b0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7a6d826fd10f0bb42eebcc9546c77d40b32242d5c6888181e5dab77b7c1f9d4ce8d4be192ef750209d98d7230ee81aa0121645f6c1482b6fc1d80e9d5287f02e
|
7
|
+
data.tar.gz: f43508482f55b5b88b1627b5b9d12ce6800512a2386cb66785348f01951d25384d1142d62370dc7d339530d0593998200005ced1a4ae01402a24940676bbc62d
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,23 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 0.5.0 (Dec 2 2017)
|
4
|
+
|
5
|
+
## New Features
|
6
|
+
|
7
|
+
- `AbstractChange#criticality` now returns a criticality object which
|
8
|
+
has a level (non_breaking, dangerous, breaking) and a reason
|
9
|
+
|
10
|
+
- Schema::ComparatorResult maintains a list of `#dangerous_changes`
|
11
|
+
|
12
|
+
- New Methods: Change.non_breaking? Change.dangerous?
|
13
|
+
|
14
|
+
- New CLI `schema_comparator` which includes `dangerous_changes`
|
15
|
+
|
16
|
+
## Breaking Changes
|
17
|
+
|
18
|
+
- Some changes have been recategorized as dangerous
|
19
|
+
- Some type changes now return breaking or non-breaking depending on the type kind
|
20
|
+
|
3
21
|
## 0.4.0 (Nov 27 2017)
|
4
22
|
|
5
23
|
## Breaking Changes
|
data/README.md
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
[](https://travis-ci.org/xuorig/graphql-schema_comparator)
|
4
4
|
|
5
5
|
`GraphQL::SchemaComparator` is a GraphQL Schema comparator. What does that mean? `GraphQL::SchemaComparator` takes
|
6
|
-
two GraphQL schemas and outputs a list of changes
|
6
|
+
two GraphQL schemas and outputs a list of changes between versions. This is useful for many things:
|
7
7
|
|
8
8
|
- Breaking Change detection
|
9
9
|
- Applying custom rules to schema changes
|
@@ -28,26 +28,40 @@ Or install it yourself as:
|
|
28
28
|
## CLI
|
29
29
|
|
30
30
|
`GraphQL::SchemaComparator` comes with a handy CLI to help compare two schemas using
|
31
|
-
the
|
31
|
+
the command line.
|
32
32
|
|
33
33
|
After a `gem install graphql-schema_comparator`, use the CLI this way:
|
34
34
|
|
35
35
|
```
|
36
36
|
Commands:
|
37
|
-
|
38
|
-
|
37
|
+
schema_comparator compare OLD_SCHEMA NEW_SCHEMA # Compares OLD_SCHEMA with NEW_SCHEMA and returns a list of changes
|
38
|
+
schema_comparator help [COMMAND] # Describe available commands or one specific command
|
39
39
|
```
|
40
40
|
|
41
41
|
Where OLD_SCHEMA and NEW_SCHEMA can be a string containing a schema IDL or a filename where that IDL is located.
|
42
42
|
|
43
43
|
### Example
|
44
44
|
|
45
|
-
|
45
|
+
```
|
46
|
+
$ ./bin/schema_comparator compare "type Query { a: A } type A { a: String } enum B { A_VALUE }" "type Query { a: A } type A { b: String } enum B { A_VALUE ANOTHER_VALUE }"
|
47
|
+
⏳ Checking for changes...
|
48
|
+
🎉 Done! Result:
|
49
|
+
|
50
|
+
Detected the following changes between schemas:
|
51
|
+
|
52
|
+
🛑 Field `a` was removed from object type `A`
|
53
|
+
⚠️ Enum value `ANOTHER_VALUE` was added to enum `B`
|
54
|
+
✅ Field `b` was added to object type `A`
|
55
|
+
```
|
46
56
|
|
47
57
|
## Usage
|
48
58
|
|
49
59
|
`GraphQL::SchemaComparator`, provides a simple api for Ruby applications to use.
|
50
60
|
|
61
|
+
## Docs
|
62
|
+
|
63
|
+
http://www.rubydoc.info/github/xuorig/graphql-schema_comparator/master/GraphQL/SchemaComparator
|
64
|
+
|
51
65
|
### GraphQL::SchemaComparator.compare
|
52
66
|
|
53
67
|
The compare method takes two arguments, `old_schema` and `new_schema`, the two schemas to compare.
|
@@ -61,31 +75,22 @@ access information on the changes between the two schemas.
|
|
61
75
|
- `result.identical?` returns true if the two schemas were identical
|
62
76
|
- `result.breaking_changes` returns the list of breaking changes found between schemas.
|
63
77
|
- `result.non_breaking_changes` returns the list of non-breaking changes found between schemas.
|
78
|
+
- `result.dangerous_changes` returns the list of dangerous changes found between schemas.
|
64
79
|
- `result.changes` returns the full list of change objects.
|
65
80
|
|
66
81
|
### Change Objects
|
67
82
|
|
68
|
-
|
69
|
-
|
83
|
+
`GraphQL::SchemaComparator` returns a list of change objects. These change objects
|
84
|
+
all inherit from `Changes::AbstractChange`
|
70
85
|
|
71
86
|
Possible changes are all found in [changes.rb](lib/graphql/schema_comparator/changes.rb).
|
72
87
|
|
73
|
-
|
74
|
-
|
75
|
-
- [ ] Handle changes in schema directives
|
76
|
-
- [ ] Test each differ
|
77
|
-
|
78
|
-
## Development
|
79
|
-
|
80
|
-
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
81
|
-
|
82
|
-
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
83
|
-
|
84
|
-
## Contributing
|
85
|
-
|
86
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/xuorig/graphql-schema_comparator. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
88
|
+
### Change Criticality
|
87
89
|
|
90
|
+
Each change object has a ``#criticality` method which returns a `Changes::Criticality` object.
|
91
|
+
This objects defines how dangerous a change is to a schema.
|
88
92
|
|
89
|
-
|
93
|
+
The different levels of criticality (non_breaking, dangerous, breaking) are explained here:
|
94
|
+
https://github.com/xuorig/graphql-schema_comparator/blob/master/lib/graphql/schema_comparator/changes/criticality.rb#L6-L19
|
90
95
|
|
91
96
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/bin/graphql-schema
CHANGED
@@ -8,6 +8,8 @@ class GraphQLSchema < Thor
|
|
8
8
|
desc "compare OLD_SCHEMA NEW_SCHEMA", "Compares OLD_SCHEMA with NEW_SCHEMA and returns a list of changes"
|
9
9
|
|
10
10
|
def compare(old_schema, new_schema)
|
11
|
+
say "[Warning] graphql-schema is deprecated. Please use `schema_comparator` instead", :yellow
|
12
|
+
|
11
13
|
parsed_old = parse_schema(old_schema)
|
12
14
|
parsed_new = parse_schema(new_schema)
|
13
15
|
|
@@ -0,0 +1,55 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "thor"
|
5
|
+
require "graphql/schema_comparator"
|
6
|
+
|
7
|
+
class GraphQLSchema < Thor
|
8
|
+
desc "compare OLD_SCHEMA NEW_SCHEMA", "Compares OLD_SCHEMA with NEW_SCHEMA and returns a list of changes"
|
9
|
+
|
10
|
+
def compare(old_schema, new_schema)
|
11
|
+
parsed_old = parse_schema(old_schema)
|
12
|
+
parsed_new = parse_schema(new_schema)
|
13
|
+
|
14
|
+
say "⏳ Checking for changes..."
|
15
|
+
result = GraphQL::SchemaComparator.compare(parsed_old, parsed_new)
|
16
|
+
|
17
|
+
say "🎉 Done! Result:"
|
18
|
+
say "\n"
|
19
|
+
|
20
|
+
if result.identical?
|
21
|
+
say "✅ Schemas are identical"
|
22
|
+
else
|
23
|
+
print_changes(result)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def print_changes(result)
|
30
|
+
say "Detected the following changes between schemas:"
|
31
|
+
say "\n"
|
32
|
+
|
33
|
+
result.changes.each do |change|
|
34
|
+
if change.breaking?
|
35
|
+
say "🛑 #{change.message}", :red
|
36
|
+
elsif change.dangerous?
|
37
|
+
say "⚠️ #{change.message}", :yellow
|
38
|
+
else
|
39
|
+
say "✅ #{change.message}", :green
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def parse_schema(schema)
|
45
|
+
if File.file?(schema)
|
46
|
+
File.read(schema)
|
47
|
+
elsif schema.is_a?(String)
|
48
|
+
schema
|
49
|
+
else
|
50
|
+
raise ArgumentError, "Invalid argument #{schema}. Must be an IDL string or file containing the schema IDL."
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
GraphQLSchema.start(ARGV)
|
@@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
|
|
18
18
|
f.match(%r{^(test|spec|features)/})
|
19
19
|
end
|
20
20
|
spec.bindir = "bin"
|
21
|
-
spec.executables = ["graphql-schema"]
|
21
|
+
spec.executables = ["graphql-schema", "schema_comparator"]
|
22
22
|
spec.require_paths = ["lib"]
|
23
23
|
|
24
24
|
spec.add_dependency "graphql", "~> 1.6"
|
@@ -1,9 +1,10 @@
|
|
1
1
|
require "graphql"
|
2
2
|
|
3
3
|
require "graphql/schema_comparator/version"
|
4
|
-
require "graphql/schema_comparator/changes"
|
5
4
|
require "graphql/schema_comparator/result"
|
6
5
|
|
6
|
+
require 'graphql/schema_comparator/changes'
|
7
|
+
|
7
8
|
require "graphql/schema_comparator/diff/schema"
|
8
9
|
require "graphql/schema_comparator/diff/argument"
|
9
10
|
require "graphql/schema_comparator/diff/directive"
|
@@ -18,6 +19,11 @@ require "graphql/schema_comparator/diff/union"
|
|
18
19
|
|
19
20
|
module GraphQL
|
20
21
|
module SchemaComparator
|
22
|
+
# Compares and returns changes for two versions of a schema
|
23
|
+
#
|
24
|
+
# @param old_schema [GraphQL::Schema, String]
|
25
|
+
# @param new_schema [GraphQL::Schema, String]
|
26
|
+
# @return [GraphQL::SchemaComparator::Result] the result of the comparison
|
21
27
|
def self.compare(old_schema, new_schema)
|
22
28
|
parsed_old = parse_schema(old_schema)
|
23
29
|
parsed_new = parse_schema(new_schema)
|
@@ -1,41 +1,46 @@
|
|
1
|
+
require 'graphql/schema_comparator/changes/criticality'
|
2
|
+
require 'graphql/schema_comparator/changes/safe_type_change'
|
3
|
+
|
1
4
|
module GraphQL
|
2
5
|
module SchemaComparator
|
3
6
|
module Changes
|
4
|
-
|
5
|
-
def safe_change?(old_type, new_type)
|
6
|
-
if !old_type.kind.wraps? && !new_type.kind.wraps?
|
7
|
-
old_type == new_type
|
8
|
-
elsif old_type.kind.list? && new_type.kind.list?
|
9
|
-
safe_change?(old_type.of_type, new_type.of_type)
|
10
|
-
elsif old_type.kind.non_null?
|
11
|
-
of_type = new_type.kind.non_null? ? new_type.of_type : new_type
|
12
|
-
safe_change?(old_type.of_type, of_type)
|
13
|
-
else
|
14
|
-
false
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
7
|
+
# Base class for change objects
|
19
8
|
class AbstractChange
|
9
|
+
# A message describing the change that happened between the two version
|
10
|
+
# @return [String] The change message
|
20
11
|
def message
|
21
12
|
raise NotImplementedError
|
22
13
|
end
|
23
14
|
|
15
|
+
# @return [Boolean] If the change is breaking or not
|
24
16
|
def breaking?
|
17
|
+
criticality.breaking?
|
18
|
+
end
|
19
|
+
|
20
|
+
# @return [Boolean] If the change is dangerous or not
|
21
|
+
def dangerous?
|
22
|
+
criticality.dangerous?
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [Boolean] If the change is non breaking
|
26
|
+
def non_breaking?
|
27
|
+
criticality.non_breaking?
|
28
|
+
end
|
29
|
+
|
30
|
+
# @return [GraphQL::SchemaComparator::Changes::Criticality] The criticality of this change
|
31
|
+
def criticality
|
25
32
|
raise NotImplementedError
|
26
33
|
end
|
27
34
|
end
|
28
35
|
|
36
|
+
# Mostly breaking changes
|
37
|
+
|
29
38
|
class TypeRemoved < AbstractChange
|
30
|
-
attr_reader :removed_type
|
39
|
+
attr_reader :removed_type, :criticality
|
31
40
|
|
32
41
|
def initialize(removed_type)
|
33
42
|
@removed_type = removed_type
|
34
|
-
@
|
35
|
-
end
|
36
|
-
|
37
|
-
def breaking?
|
38
|
-
!!@breaking
|
43
|
+
@criticality = Changes::Criticality.breaking
|
39
44
|
end
|
40
45
|
|
41
46
|
def message
|
@@ -44,296 +49,499 @@ module GraphQL
|
|
44
49
|
end
|
45
50
|
|
46
51
|
class DirectiveRemoved < AbstractChange
|
47
|
-
attr_reader :directive
|
52
|
+
attr_reader :directive, :criticality
|
48
53
|
|
49
54
|
def initialize(directive)
|
50
55
|
@directive = directive
|
51
|
-
@
|
56
|
+
@criticality = Changes::Criticality.breaking
|
52
57
|
end
|
53
58
|
|
54
59
|
def message
|
55
60
|
"`#{directive.name}` was removed"
|
56
61
|
end
|
57
|
-
|
58
|
-
def breaking?
|
59
|
-
!!@breaking
|
60
|
-
end
|
61
62
|
end
|
62
63
|
|
63
64
|
class TypeKindChanged < AbstractChange
|
64
|
-
attr_reader :old_type, :new_type
|
65
|
+
attr_reader :old_type, :new_type, :criticality
|
65
66
|
|
66
67
|
def initialize(old_type, new_type)
|
67
68
|
@old_type = old_type
|
68
69
|
@new_type = new_type
|
69
|
-
@
|
70
|
+
@criticality = Changes::Criticality.breaking
|
70
71
|
end
|
71
72
|
|
72
73
|
def message
|
73
74
|
"`#{old_type.name}` kind changed from `#{old_type.kind}` to `#{new_type.kind}`"
|
74
75
|
end
|
75
|
-
|
76
|
-
def breaking?
|
77
|
-
!!@breaking
|
78
|
-
end
|
79
76
|
end
|
80
77
|
|
81
78
|
class EnumValueRemoved < AbstractChange
|
82
|
-
attr_reader :enum_value, :enum_type
|
79
|
+
attr_reader :enum_value, :enum_type, :criticality
|
83
80
|
|
84
81
|
def initialize(enum_type, enum_value)
|
85
82
|
@enum_value = enum_value
|
86
83
|
@enum_type = enum_type
|
87
|
-
@
|
84
|
+
@criticality = Changes::Criticality.breaking
|
88
85
|
end
|
89
86
|
|
90
87
|
def message
|
91
88
|
"Enum value `#{enum_value.name}` was removed from enum `#{enum_type.name}`"
|
92
89
|
end
|
93
|
-
|
94
|
-
def breaking?
|
95
|
-
!!@breaking
|
96
|
-
end
|
97
90
|
end
|
98
91
|
|
99
92
|
class UnionMemberRemoved < AbstractChange
|
100
|
-
attr_reader :union_type, :union_member
|
93
|
+
attr_reader :union_type, :union_member, :criticality
|
101
94
|
|
102
95
|
def initialize(union_type, union_member)
|
103
96
|
@union_member = union_member
|
104
97
|
@union_type = union_type
|
105
|
-
@
|
98
|
+
@criticality = Changes::Criticality.breaking
|
106
99
|
end
|
107
100
|
|
108
101
|
def message
|
109
102
|
"Union member `#{union_member.name}` was removed from Union type `#{union_type.name}`"
|
110
103
|
end
|
111
|
-
|
112
|
-
def breaking?
|
113
|
-
!!@breaking
|
114
|
-
end
|
115
104
|
end
|
116
105
|
|
117
106
|
class InputFieldRemoved < AbstractChange
|
118
|
-
attr_reader :input_object_type, :field
|
107
|
+
attr_reader :input_object_type, :field, :criticality
|
119
108
|
|
120
109
|
def initialize(input_object_type, field)
|
121
110
|
@input_object_type = input_object_type
|
122
111
|
@field = field
|
123
|
-
@
|
112
|
+
@criticality = Changes::Criticality.breaking
|
124
113
|
end
|
125
114
|
|
126
115
|
def message
|
127
116
|
"Input field `#{field.name}` was removed from input object type `#{input_object_type.name}`"
|
128
117
|
end
|
129
|
-
|
130
|
-
def breaking?
|
131
|
-
!!@breaking
|
132
|
-
end
|
133
118
|
end
|
134
119
|
|
135
120
|
class FieldArgumentRemoved < AbstractChange
|
136
|
-
attr_reader :object_type, :field, :argument
|
121
|
+
attr_reader :object_type, :field, :argument, :criticality
|
137
122
|
|
138
123
|
def initialize(object_type, field, argument)
|
139
124
|
@object_type = object_type
|
140
125
|
@field = field
|
141
126
|
@argument = argument
|
142
|
-
@
|
127
|
+
@criticality = Changes::Criticality.breaking
|
143
128
|
end
|
144
129
|
|
145
130
|
def message
|
146
131
|
"Argument `#{argument.name}: #{argument.type}` was removed from field `#{object_type.name}.#{field.name}`"
|
147
132
|
end
|
148
|
-
|
149
|
-
def breaking?
|
150
|
-
!!@breaking
|
151
|
-
end
|
152
133
|
end
|
153
134
|
|
154
135
|
class DirectiveArgumentRemoved < AbstractChange
|
155
|
-
attr_reader :directive, :argument
|
136
|
+
attr_reader :directive, :argument, :criticality
|
156
137
|
|
157
138
|
def initialize(directive, argument)
|
158
139
|
@directive = directive
|
159
140
|
@argument = argument
|
160
|
-
@
|
141
|
+
@criticality = Changes::Criticality.breaking
|
161
142
|
end
|
162
143
|
|
163
144
|
def message
|
164
145
|
"Argument `#{argument.name}` was removed from directive `#{directive.name}`"
|
165
146
|
end
|
166
|
-
|
167
|
-
def breaking?
|
168
|
-
!!@breaking
|
169
|
-
end
|
170
147
|
end
|
171
148
|
|
172
149
|
class SchemaQueryTypeChanged < AbstractChange
|
173
|
-
attr_reader :old_schema, :new_schema
|
150
|
+
attr_reader :old_schema, :new_schema, :criticality
|
174
151
|
|
175
152
|
def initialize(old_schema, new_schema)
|
176
153
|
@old_schema = old_schema
|
177
154
|
@new_schema = new_schema
|
178
|
-
@
|
155
|
+
@criticality = Changes::Criticality.breaking
|
179
156
|
end
|
180
157
|
|
181
158
|
def message
|
182
159
|
"Schema query root has changed from `#{old_schema.query.name}` to `#{new_schema.query.name}`"
|
183
160
|
end
|
184
|
-
|
185
|
-
def breaking?
|
186
|
-
!!@breaking
|
187
|
-
end
|
188
161
|
end
|
189
162
|
|
190
163
|
class FieldRemoved < AbstractChange
|
191
|
-
attr_reader :object_type, :field
|
164
|
+
attr_reader :object_type, :field, :criticality
|
192
165
|
|
193
166
|
def initialize(object_type, field)
|
194
167
|
@object_type = object_type
|
195
168
|
@field = field
|
196
|
-
@
|
169
|
+
@criticality = Changes::Criticality.breaking
|
197
170
|
end
|
198
171
|
|
199
172
|
def message
|
200
173
|
"Field `#{field.name}` was removed from object type `#{object_type.name}`"
|
201
174
|
end
|
202
|
-
|
203
|
-
def breaking?
|
204
|
-
!!@breaking
|
205
|
-
end
|
206
175
|
end
|
207
176
|
|
208
177
|
class DirectiveLocationRemoved < AbstractChange
|
209
|
-
attr_reader :directive, :location
|
178
|
+
attr_reader :directive, :location, :criticality
|
210
179
|
|
211
180
|
def initialize(directive, location)
|
212
181
|
@directive = directive
|
213
182
|
@location = location
|
214
|
-
@
|
183
|
+
@criticality = Changes::Criticality.breaking
|
215
184
|
end
|
216
185
|
|
217
186
|
def message
|
218
187
|
"Location `#{location}` was removed from directive `#{directive.name}`"
|
219
188
|
end
|
220
|
-
|
221
|
-
def breaking?
|
222
|
-
!!@breaking
|
223
|
-
end
|
224
189
|
end
|
225
190
|
|
226
191
|
class ObjectTypeInterfaceRemoved < AbstractChange
|
227
|
-
attr_reader :interface, :object_type
|
192
|
+
attr_reader :interface, :object_type, :criticality
|
228
193
|
|
229
194
|
def initialize(interface, object_type)
|
230
195
|
@interface = interface
|
231
196
|
@object_type = object_type
|
232
|
-
@
|
197
|
+
@criticality = Changes::Criticality.breaking
|
233
198
|
end
|
234
199
|
|
235
200
|
def message
|
236
201
|
"`#{object_type.name}` object type no longer implements `#{interface.name}` interface"
|
237
202
|
end
|
203
|
+
end
|
238
204
|
|
239
|
-
|
240
|
-
|
205
|
+
class FieldTypeChanged < AbstractChange
|
206
|
+
include SafeTypeChange
|
207
|
+
|
208
|
+
attr_reader :type, :old_field, :new_field
|
209
|
+
|
210
|
+
def initialize(type, old_field, new_field)
|
211
|
+
@type = type
|
212
|
+
@old_field = old_field
|
213
|
+
@new_field = new_field
|
214
|
+
end
|
215
|
+
|
216
|
+
def message
|
217
|
+
"Field `#{type}.#{old_field.name}` changed type from `#{old_field.type}` to `#{new_field.type}`"
|
218
|
+
end
|
219
|
+
|
220
|
+
def criticality
|
221
|
+
if safe_change_for_field?(old_field.type, new_field.type)
|
222
|
+
Changes::Criticality.non_breaking
|
223
|
+
else
|
224
|
+
Changes::Criticality.breaking
|
225
|
+
end
|
241
226
|
end
|
242
227
|
end
|
243
228
|
|
244
|
-
class
|
245
|
-
|
229
|
+
class InputFieldTypeChanged < AbstractChange
|
230
|
+
include SafeTypeChange
|
246
231
|
|
247
|
-
|
248
|
-
|
249
|
-
|
232
|
+
attr_reader :input_type, :old_input_field, :new_input_field, :criticality
|
233
|
+
|
234
|
+
def initialize(input_type, old_input_field, new_input_field)
|
235
|
+
if safe_change_for_input_value?(old_input_field.type, new_input_field.type)
|
236
|
+
@criticality = Changes::Criticality.non_breaking(
|
237
|
+
reason: "Changing an input field from non-null to null is considered non-breaking"
|
238
|
+
)
|
239
|
+
else
|
240
|
+
@criticality = Changes::Criticality.breaking
|
241
|
+
end
|
242
|
+
|
243
|
+
@input_type = input_type
|
244
|
+
@old_input_field = old_input_field
|
245
|
+
@new_input_field = new_input_field
|
250
246
|
end
|
251
247
|
|
252
248
|
def message
|
253
|
-
"
|
249
|
+
"Input field `#{input_type}.#{old_input_field.name}` changed type from `#{old_input_field.type}` to `#{new_input_field.type}`"
|
254
250
|
end
|
251
|
+
end
|
255
252
|
|
256
|
-
|
257
|
-
|
253
|
+
class FieldArgumentTypeChanged < AbstractChange
|
254
|
+
include SafeTypeChange
|
255
|
+
|
256
|
+
attr_reader :type, :field, :old_argument, :new_argument, :criticality
|
257
|
+
|
258
|
+
def initialize(type, field, old_argument, new_argument)
|
259
|
+
if safe_change_for_input_value?(old_argument.type, new_argument.type)
|
260
|
+
@criticality = Changes::Criticality.non_breaking(
|
261
|
+
reason: "Changing an input field from non-null to null is considered non-breaking"
|
262
|
+
)
|
263
|
+
else
|
264
|
+
@criticality = Changes::Criticality.breaking
|
265
|
+
end
|
266
|
+
|
267
|
+
@type = type
|
268
|
+
@field = field
|
269
|
+
@old_argument = old_argument
|
270
|
+
@new_argument = new_argument
|
271
|
+
end
|
272
|
+
|
273
|
+
def message
|
274
|
+
"Type for argument `#{new_argument.name}` on field `#{type.name}.#{field.name}` changed"\
|
275
|
+
" from `#{old_argument.type}` to `#{new_argument.type}`"
|
258
276
|
end
|
259
277
|
end
|
260
278
|
|
261
|
-
class
|
262
|
-
|
279
|
+
class DirectiveArgumentTypeChanged < AbstractChange
|
280
|
+
include SafeTypeChange
|
281
|
+
|
282
|
+
attr_reader :directive, :old_argument, :new_argument, :criticality
|
283
|
+
|
284
|
+
def initialize(directive, old_argument, new_argument)
|
285
|
+
if safe_change_for_input_value?(old_argument.type, new_argument.type)
|
286
|
+
@criticality = Changes::Criticality.non_breaking(
|
287
|
+
reason: "Changing an input field from non-null to null is considered non-breaking"
|
288
|
+
)
|
289
|
+
else
|
290
|
+
@criticality = Changes::Criticality.breaking
|
291
|
+
end
|
263
292
|
|
264
|
-
def initialize(directive)
|
265
293
|
@directive = directive
|
266
|
-
@
|
294
|
+
@old_argument = old_argument
|
295
|
+
@new_argument = new_argument
|
267
296
|
end
|
268
297
|
|
269
298
|
def message
|
270
|
-
"
|
299
|
+
"Type for argument `#{new_argument.name}` on directive `#{directive.name}` changed"\
|
300
|
+
" from `#{old_argument.type}` to `#{new_argument.type}`"
|
271
301
|
end
|
302
|
+
end
|
272
303
|
|
273
|
-
|
274
|
-
|
304
|
+
class SchemaMutationTypeChanged < AbstractChange
|
305
|
+
attr_reader :old_schema, :new_schema, :criticality
|
306
|
+
|
307
|
+
def initialize(old_schema, new_schema)
|
308
|
+
@old_schema = old_schema
|
309
|
+
@new_schema = new_schema
|
310
|
+
@criticality = Changes::Criticality.breaking
|
311
|
+
end
|
312
|
+
|
313
|
+
def message
|
314
|
+
"Schema mutation root has changed from `#{old_schema.mutation}` to `#{new_schema.mutation}`"
|
275
315
|
end
|
276
316
|
end
|
277
317
|
|
278
|
-
class
|
279
|
-
attr_reader :
|
318
|
+
class SchemaSubscriptionTypeChanged < AbstractChange
|
319
|
+
attr_reader :old_schema, :new_schema, :criticality
|
280
320
|
|
281
|
-
def initialize(
|
282
|
-
@
|
283
|
-
@
|
284
|
-
@
|
321
|
+
def initialize(old_schema, new_schema)
|
322
|
+
@old_schema = old_schema
|
323
|
+
@new_schema = new_schema
|
324
|
+
@criticality = Changes::Criticality.breaking
|
285
325
|
end
|
286
326
|
|
287
327
|
def message
|
288
|
-
"
|
328
|
+
"Schema subscription type has changed from `#{old_schema.subscription}` to `#{new_schema.subscription}`"
|
289
329
|
end
|
330
|
+
end
|
290
331
|
|
291
|
-
|
292
|
-
|
332
|
+
# Dangerous Changes
|
333
|
+
|
334
|
+
class FieldArgumentDefaultChanged < AbstractChange
|
335
|
+
attr_reader :type, :field, :old_argument, :new_argument, :criticality
|
336
|
+
|
337
|
+
def initialize(type, field, old_argument, new_argument)
|
338
|
+
@type = type
|
339
|
+
@field = field
|
340
|
+
@old_argument = old_argument
|
341
|
+
@new_argument = new_argument
|
342
|
+
@criticality = Changes::Criticality.dangerous(
|
343
|
+
reason: "Changing the default value for an argument may change the runtime " \
|
344
|
+
"behaviour of a field if it was never provided."
|
345
|
+
)
|
346
|
+
end
|
347
|
+
|
348
|
+
def message
|
349
|
+
"Default value for argument `#{new_argument.name}` on field `#{type.name}.#{field.name}` changed"\
|
350
|
+
" from `#{old_argument.default_value}` to `#{new_argument.default_value}`"
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
class InputFieldDefaultChanged < AbstractChange
|
355
|
+
attr_reader :input_type, :old_field, :new_field, :criticality
|
356
|
+
|
357
|
+
def initialize(input_type, old_field, new_field)
|
358
|
+
@criticality = Changes::Criticality.dangerous(
|
359
|
+
reason: "Changing the default value for an argument may change the runtime " \
|
360
|
+
"behaviour of a field if it was never provided."
|
361
|
+
)
|
362
|
+
@input_type = input_type
|
363
|
+
@old_field = old_field
|
364
|
+
@new_field = new_field
|
365
|
+
end
|
366
|
+
|
367
|
+
def message
|
368
|
+
"Input field `#{input_type.name}.#{old_field.name}` default changed"\
|
369
|
+
" from `#{old_field.default_value}` to `#{new_field.default_value}`"
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
class DirectiveArgumentDefaultChanged < AbstractChange
|
374
|
+
attr_reader :directive, :old_argument, :new_argument, :criticality
|
375
|
+
|
376
|
+
def initialize(directive, old_argument, new_argument)
|
377
|
+
@criticality = Changes::Criticality.dangerous(
|
378
|
+
reason: "Changing the default value for an argument may change the runtime " \
|
379
|
+
"behaviour of a field if it was never provided."
|
380
|
+
)
|
381
|
+
@directive = directive
|
382
|
+
@old_argument = old_argument
|
383
|
+
@new_argument = new_argument
|
384
|
+
end
|
385
|
+
|
386
|
+
def message
|
387
|
+
"Default value for argument `#{new_argument.name}` on directive `#{directive.name}` changed"\
|
388
|
+
" from `#{old_argument.default_value}` to `#{new_argument.default_value}`"
|
293
389
|
end
|
294
390
|
end
|
295
391
|
|
296
392
|
class EnumValueAdded < AbstractChange
|
297
|
-
attr_reader :enum_type, :enum_value
|
393
|
+
attr_reader :enum_type, :enum_value, :criticality
|
298
394
|
|
299
395
|
def initialize(enum_type, enum_value)
|
300
396
|
@enum_type = enum_type
|
301
397
|
@enum_value = enum_value
|
302
|
-
@
|
398
|
+
@criticality = Changes::Criticality.dangerous(
|
399
|
+
reason: "Adding an enum value may break existing clients that were not " \
|
400
|
+
"programming defensively against an added case when querying an enum."
|
401
|
+
)
|
402
|
+
end
|
403
|
+
|
404
|
+
def message
|
405
|
+
"Enum value `#{enum_value.name}` was added to enum `#{enum_type.name}`"
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
class UnionMemberAdded < AbstractChange
|
410
|
+
attr_reader :union_type, :union_member, :criticality
|
411
|
+
|
412
|
+
def initialize(union_type, union_member)
|
413
|
+
@union_member = union_member
|
414
|
+
@union_type = union_type
|
415
|
+
@criticality = Changes::Criticality.dangerous(
|
416
|
+
reason: "Adding a possible type to Unions may break existing clients " \
|
417
|
+
"that were not programming defensively against a new possible type."
|
418
|
+
)
|
419
|
+
end
|
420
|
+
|
421
|
+
def message
|
422
|
+
"Union member `#{union_member.name}` was added to Union type `#{union_type.name}`"
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
class ObjectTypeInterfaceAdded < AbstractChange
|
427
|
+
attr_reader :interface, :object_type, :criticality
|
428
|
+
|
429
|
+
def initialize(interface, object_type)
|
430
|
+
@criticality = Changes::Criticality.dangerous(
|
431
|
+
reason: "Adding an interface to an object type may break existing clients " \
|
432
|
+
"that were not programming defensively against a new possible type."
|
433
|
+
)
|
434
|
+
@interface = interface
|
435
|
+
@object_type = object_type
|
436
|
+
end
|
437
|
+
|
438
|
+
def message
|
439
|
+
"`#{object_type.name}` object implements `#{interface.name}` interface"
|
440
|
+
end
|
441
|
+
end
|
442
|
+
|
443
|
+
# Mostly Non-Breaking Changes
|
444
|
+
|
445
|
+
class InputFieldAdded < AbstractChange
|
446
|
+
attr_reader :input_object_type, :field, :criticality
|
447
|
+
|
448
|
+
def initialize(input_object_type, field)
|
449
|
+
@criticality = if field.type.non_null?
|
450
|
+
Changes::Criticality.breaking
|
451
|
+
else
|
452
|
+
Changes::Criticality.non_breaking
|
453
|
+
end
|
454
|
+
|
455
|
+
@input_object_type = input_object_type
|
456
|
+
@field = field
|
457
|
+
end
|
458
|
+
|
459
|
+
def message
|
460
|
+
"Input field `#{field.name}` was added to input object type `#{input_object_type.name}`"
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
464
|
+
class FieldArgumentAdded < AbstractChange
|
465
|
+
attr_reader :type, :field, :argument, :criticality
|
466
|
+
|
467
|
+
def initialize(type, field, argument)
|
468
|
+
@criticality = if argument.type.non_null?
|
469
|
+
Changes::Criticality.breaking
|
470
|
+
else
|
471
|
+
Changes::Criticality.non_breaking
|
472
|
+
end
|
473
|
+
|
474
|
+
@type = type
|
475
|
+
@field = field
|
476
|
+
@argument = argument
|
477
|
+
end
|
478
|
+
|
479
|
+
def message
|
480
|
+
"Argument `#{argument.name}: #{argument.type}` added to field `#{type.name}.#{field.name}`"
|
481
|
+
end
|
482
|
+
end
|
483
|
+
|
484
|
+
class TypeAdded < AbstractChange
|
485
|
+
attr_reader :type, :criticality
|
486
|
+
|
487
|
+
def initialize(type)
|
488
|
+
@type = type
|
489
|
+
@criticality = Changes::Criticality.non_breaking
|
490
|
+
end
|
491
|
+
|
492
|
+
def message
|
493
|
+
"Type `#{type.name}` was added"
|
494
|
+
end
|
495
|
+
end
|
496
|
+
|
497
|
+
class DirectiveAdded < AbstractChange
|
498
|
+
attr_reader :directive, :criticality
|
499
|
+
|
500
|
+
def initialize(directive)
|
501
|
+
@directive = directive
|
502
|
+
@criticality = Changes::Criticality.non_breaking
|
503
|
+
end
|
504
|
+
|
505
|
+
def message
|
506
|
+
"Directive `#{directive.name}` was added"
|
507
|
+
end
|
508
|
+
end
|
509
|
+
|
510
|
+
class TypeDescriptionChanged < AbstractChange
|
511
|
+
attr_reader :old_type, :new_type, :criticality
|
512
|
+
|
513
|
+
def initialize(old_type, new_type)
|
514
|
+
@old_type = old_type
|
515
|
+
@new_type = new_type
|
516
|
+
@criticality = Changes::Criticality.non_breaking
|
303
517
|
end
|
304
518
|
|
305
519
|
def message
|
306
|
-
"
|
307
|
-
end
|
308
|
-
|
309
|
-
def breaking?
|
310
|
-
!!@breaking
|
520
|
+
"Description `#{old_type.description}` on type `#{old_type.name}` has changed to `#{new_type.description}`"
|
311
521
|
end
|
312
522
|
end
|
313
523
|
|
314
524
|
class EnumValueDescriptionChanged < AbstractChange
|
315
|
-
attr_reader :enum, :old_enum_value, :new_enum_value
|
525
|
+
attr_reader :enum, :old_enum_value, :new_enum_value, :criticality
|
316
526
|
|
317
527
|
def initialize(enum, old_enum_value, new_enum_value)
|
318
528
|
@enum = enum
|
319
529
|
@old_enum_value = old_enum_value
|
320
530
|
@new_enum_value = new_enum_value
|
531
|
+
@criticality = Changes::Criticality.non_breaking
|
321
532
|
end
|
322
533
|
|
323
534
|
def message
|
324
535
|
"Description for enum value `#{enum.name}.#{new_enum_value.name}` changed from " \
|
325
536
|
"`#{old_enum_value.description}` to `#{new_enum_value.description}`"
|
326
537
|
end
|
327
|
-
|
328
|
-
def breaking?
|
329
|
-
false
|
330
|
-
end
|
331
538
|
end
|
332
539
|
|
333
540
|
class EnumValueDeprecated < AbstractChange
|
334
|
-
attr_reader :enum, :old_enum_value, :new_enum_value
|
541
|
+
attr_reader :enum, :old_enum_value, :new_enum_value, :criticality
|
335
542
|
|
336
543
|
def initialize(enum, old_enum_value, new_enum_value)
|
544
|
+
@criticality = Changes::Criticality.non_breaking
|
337
545
|
@enum = enum
|
338
546
|
@old_enum_value = old_enum_value
|
339
547
|
@new_enum_value = new_enum_value
|
@@ -348,263 +556,131 @@ module GraphQL
|
|
348
556
|
" `#{new_enum_value.deprecation_reason}`"
|
349
557
|
end
|
350
558
|
end
|
351
|
-
|
352
|
-
def breaking?
|
353
|
-
false
|
354
|
-
end
|
355
|
-
end
|
356
|
-
|
357
|
-
class UnionMemberAdded < AbstractChange
|
358
|
-
attr_reader :union_type, :union_member
|
359
|
-
|
360
|
-
def initialize(union_type, union_member)
|
361
|
-
@union_member = union_member
|
362
|
-
@union_type = union_type
|
363
|
-
@breaking = false
|
364
|
-
end
|
365
|
-
|
366
|
-
def message
|
367
|
-
"Union member `#{union_member.name}` was added to Union type `#{union_type.name}`"
|
368
|
-
end
|
369
|
-
|
370
|
-
def breaking?
|
371
|
-
!!@breaking
|
372
|
-
end
|
373
559
|
end
|
374
560
|
|
375
561
|
class InputFieldDescriptionChanged < AbstractChange
|
376
|
-
attr_reader :input_type, :old_field, :new_field
|
562
|
+
attr_reader :input_type, :old_field, :new_field, :criticality
|
377
563
|
|
378
564
|
def initialize(input_type, old_field, new_field)
|
565
|
+
@criticality = Changes::Criticality.non_breaking
|
379
566
|
@input_type = input_type
|
380
567
|
@old_field = old_field
|
381
568
|
@new_field = new_field
|
382
|
-
@breaking = false
|
383
569
|
end
|
384
570
|
|
385
571
|
def message
|
386
572
|
"Input field `#{input_type.name}.#{old_field.name}` description changed"\
|
387
573
|
" from `#{old_field.description}` to `#{new_field.description}`"
|
388
574
|
end
|
389
|
-
|
390
|
-
def breaking?
|
391
|
-
!!@breaking
|
392
|
-
end
|
393
575
|
end
|
394
576
|
|
395
577
|
class DirectiveDescriptionChanged < AbstractChange
|
396
|
-
attr_reader :old_directive, :new_directive
|
578
|
+
attr_reader :old_directive, :new_directive, :criticality
|
397
579
|
|
398
580
|
def initialize(old_directive, new_directive)
|
581
|
+
@criticality = Changes::Criticality.non_breaking
|
399
582
|
@old_directive = old_directive
|
400
583
|
@new_directive = new_directive
|
401
|
-
@breaking = false
|
402
584
|
end
|
403
585
|
|
404
586
|
def message
|
405
587
|
"Directive `#{new_directive.name}` description changed"\
|
406
588
|
" from `#{old_directive.description}` to `#{new_directive.description}`"
|
407
589
|
end
|
408
|
-
|
409
|
-
def breaking?
|
410
|
-
!!@breaking
|
411
|
-
end
|
412
590
|
end
|
413
591
|
|
414
592
|
class FieldDescriptionChanged < AbstractChange
|
415
|
-
attr_reader :type, :old_field, :new_field
|
593
|
+
attr_reader :type, :old_field, :new_field, :criticality
|
416
594
|
|
417
595
|
def initialize(type, old_field, new_field)
|
596
|
+
@criticality = Changes::Criticality.non_breaking
|
418
597
|
@type = type
|
419
598
|
@old_field = old_field
|
420
599
|
@new_field = new_field
|
421
|
-
@breaking = false
|
422
600
|
end
|
423
601
|
|
424
602
|
def message
|
425
603
|
"Field `#{type.name}.#{old_field.name}` description changed"\
|
426
604
|
" from `#{old_field.description}` to `#{new_field.description}`"
|
427
605
|
end
|
428
|
-
|
429
|
-
def breaking?
|
430
|
-
!!@breaking
|
431
|
-
end
|
432
606
|
end
|
433
607
|
|
434
608
|
class FieldArgumentDescriptionChanged < AbstractChange
|
435
|
-
attr_reader :type, :field, :old_argument, :new_argument
|
609
|
+
attr_reader :type, :field, :old_argument, :new_argument, :criticality
|
436
610
|
|
437
611
|
def initialize(type, field, old_argument, new_argument)
|
612
|
+
@criticality = Changes::Criticality.non_breaking
|
438
613
|
@type = type
|
439
614
|
@field = field
|
440
615
|
@old_argument = old_argument
|
441
616
|
@new_argument = new_argument
|
442
|
-
@breaking = false
|
443
617
|
end
|
444
618
|
|
445
619
|
def message
|
446
620
|
"Description for argument `#{new_argument.name}` on field `#{type.name}.#{field.name}` changed"\
|
447
621
|
" from `#{old_argument.description}` to `#{new_argument.description}`"
|
448
622
|
end
|
449
|
-
|
450
|
-
def breaking?
|
451
|
-
!!@breaking
|
452
|
-
end
|
453
623
|
end
|
454
624
|
|
455
625
|
class DirectiveArgumentDescriptionChanged < AbstractChange
|
456
|
-
attr_reader :directive, :old_argument, :new_argument
|
626
|
+
attr_reader :directive, :old_argument, :new_argument, :criticality
|
457
627
|
|
458
628
|
def initialize(directive, old_argument, new_argument)
|
629
|
+
@criticality = Changes::Criticality.non_breaking
|
459
630
|
@directive = directive
|
460
631
|
@old_argument = old_argument
|
461
632
|
@new_argument = new_argument
|
462
|
-
@breaking = false
|
463
633
|
end
|
464
634
|
|
465
635
|
def message
|
466
636
|
"Description for argument `#{new_argument.name}` on directive `#{directive.name}` changed"\
|
467
637
|
" from `#{old_argument.description}` to `#{new_argument.description}`"
|
468
638
|
end
|
469
|
-
|
470
|
-
def breaking?
|
471
|
-
!!@breaking
|
472
|
-
end
|
473
639
|
end
|
474
640
|
|
475
641
|
class FieldDeprecationChanged < AbstractChange
|
476
|
-
attr_reader :type, :old_field, :new_field
|
642
|
+
attr_reader :type, :old_field, :new_field, :criticality
|
477
643
|
|
478
644
|
def initialize(type, old_field, new_field)
|
645
|
+
@criticality = Changes::Criticality.non_breaking
|
479
646
|
@type = type
|
480
647
|
@old_field = old_field
|
481
648
|
@new_field = new_field
|
482
|
-
@breaking = false
|
483
649
|
end
|
484
650
|
|
485
651
|
def message
|
486
652
|
"Deprecation reason on field `#{type.name}.#{new_field.name}` has changed "\
|
487
653
|
"from `#{old_field.deprecation_reason}` to `#{new_field.deprecation_reason}`"
|
488
654
|
end
|
489
|
-
|
490
|
-
def breaking?
|
491
|
-
!!@breaking
|
492
|
-
end
|
493
|
-
end
|
494
|
-
|
495
|
-
class InputFieldDefaultChanged < AbstractChange
|
496
|
-
attr_reader :input_type, :old_field, :new_field
|
497
|
-
|
498
|
-
def initialize(input_type, old_field, new_field)
|
499
|
-
@input_type = input_type
|
500
|
-
@old_field = old_field
|
501
|
-
@new_field = new_field
|
502
|
-
@breaking = false
|
503
|
-
end
|
504
|
-
|
505
|
-
def message
|
506
|
-
"Input field `#{input_type.name}.#{old_field.name}` default changed"\
|
507
|
-
" from `#{old_field.default_value}` to `#{new_field.default_value}`"
|
508
|
-
end
|
509
|
-
|
510
|
-
def breaking?
|
511
|
-
!!@breaking
|
512
|
-
end
|
513
|
-
end
|
514
|
-
|
515
|
-
class FieldArgumentDefaultChanged < AbstractChange
|
516
|
-
attr_reader :type, :field, :old_argument, :new_argument
|
517
|
-
|
518
|
-
def initialize(type, field, old_argument, new_argument)
|
519
|
-
@type = type
|
520
|
-
@field = field
|
521
|
-
@old_argument = old_argument
|
522
|
-
@new_argument = new_argument
|
523
|
-
@breaking = false
|
524
|
-
end
|
525
|
-
|
526
|
-
def message
|
527
|
-
"Default value for argument `#{new_argument.name}` on field `#{type.name}.#{field.name}` changed"\
|
528
|
-
" from `#{old_argument.default_value}` to `#{new_argument.default_value}`"
|
529
|
-
end
|
530
|
-
|
531
|
-
def breaking?
|
532
|
-
!!@breaking
|
533
|
-
end
|
534
|
-
end
|
535
|
-
|
536
|
-
class DirectiveArgumentDefaultChanged < AbstractChange
|
537
|
-
attr_reader :directive, :old_argument, :new_argument
|
538
|
-
|
539
|
-
def initialize(directive, old_argument, new_argument)
|
540
|
-
@directive = directive
|
541
|
-
@old_argument = old_argument
|
542
|
-
@new_argument = new_argument
|
543
|
-
@breaking = false
|
544
|
-
end
|
545
|
-
|
546
|
-
def message
|
547
|
-
"Default value for argument `#{new_argument.name}` on directive `#{directive.name}` changed"\
|
548
|
-
" from `#{old_argument.default_value}` to `#{new_argument.default_value}`"
|
549
|
-
end
|
550
|
-
|
551
|
-
def breaking?
|
552
|
-
!!@breaking
|
553
|
-
end
|
554
|
-
end
|
555
|
-
|
556
|
-
class ObjectTypeInterfaceAdded < AbstractChange
|
557
|
-
attr_reader :interface, :object_type
|
558
|
-
|
559
|
-
def initialize(interface, object_type)
|
560
|
-
@interface = interface
|
561
|
-
@object_type = object_type
|
562
|
-
@breaking = false
|
563
|
-
end
|
564
|
-
|
565
|
-
def message
|
566
|
-
"`#{object_type.name}` object implements `#{interface.name}` interface"
|
567
|
-
end
|
568
|
-
|
569
|
-
def breaking?
|
570
|
-
!!@breaking
|
571
|
-
end
|
572
655
|
end
|
573
656
|
|
574
657
|
class FieldAdded < AbstractChange
|
575
|
-
attr_reader :object_type, :field
|
658
|
+
attr_reader :object_type, :field, :criticality
|
576
659
|
|
577
660
|
def initialize(object_type, field)
|
661
|
+
@criticality = Changes::Criticality.non_breaking
|
578
662
|
@object_type = object_type
|
579
663
|
@field = field
|
580
|
-
@breaking = false
|
581
664
|
end
|
582
665
|
|
583
666
|
def message
|
584
667
|
"Field `#{field.name}` was added to object type `#{object_type.name}`"
|
585
668
|
end
|
586
669
|
|
587
|
-
def breaking?
|
588
|
-
!!@breaking
|
589
|
-
end
|
590
670
|
end
|
591
671
|
|
592
672
|
class DirectiveLocationAdded < AbstractChange
|
593
|
-
attr_reader :directive, :location
|
673
|
+
attr_reader :directive, :location, :criticality
|
594
674
|
|
595
675
|
def initialize(directive, location)
|
676
|
+
@criticality = Changes::Criticality.non_breaking
|
596
677
|
@directive = directive
|
597
678
|
@location = location
|
598
|
-
@breaking = false
|
599
679
|
end
|
600
680
|
|
601
681
|
def message
|
602
682
|
"Location `#{location}` was added to directive `#{directive.name}`"
|
603
683
|
end
|
604
|
-
|
605
|
-
def breaking?
|
606
|
-
!!@breaking
|
607
|
-
end
|
608
684
|
end
|
609
685
|
|
610
686
|
# TODO
|
@@ -751,177 +827,22 @@ module GraphQL
|
|
751
827
|
end
|
752
828
|
end
|
753
829
|
|
754
|
-
class InputFieldAdded < AbstractChange
|
755
|
-
attr_reader :input_object_type, :field
|
756
|
-
|
757
|
-
def initialize(input_object_type, field)
|
758
|
-
@input_object_type = input_object_type
|
759
|
-
@field = field
|
760
|
-
@breaking = field.type.kind.non_null? ? true : false
|
761
|
-
end
|
762
|
-
|
763
|
-
def message
|
764
|
-
"Input field `#{field.name}` was added to input object type `#{input_object_type.name}`"
|
765
|
-
end
|
766
|
-
|
767
|
-
def breaking?
|
768
|
-
!!@breaking
|
769
|
-
end
|
770
|
-
end
|
771
|
-
|
772
|
-
class FieldArgumentAdded < AbstractChange
|
773
|
-
attr_reader :type, :field, :argument
|
774
|
-
|
775
|
-
def initialize(type, field, argument)
|
776
|
-
@type = type
|
777
|
-
@field = field
|
778
|
-
@argument = argument
|
779
|
-
# TODO: should at least have a warning that it may still be breaking
|
780
|
-
@breaking = argument.type.kind.non_null? ? true : false
|
781
|
-
end
|
782
|
-
|
783
|
-
def message
|
784
|
-
"Argument `#{argument.name}: #{argument.type}` added to field `#{type.name}.#{field.name}`"
|
785
|
-
end
|
786
|
-
|
787
|
-
def breaking?
|
788
|
-
!!@breaking
|
789
|
-
end
|
790
|
-
end
|
791
|
-
|
792
830
|
class DirectiveArgumentAdded < AbstractChange
|
793
|
-
attr_reader :directive, :argument
|
831
|
+
attr_reader :directive, :argument, :criticality
|
794
832
|
|
795
833
|
def initialize(directive, argument)
|
834
|
+
@criticality = if argument.type.non_null?
|
835
|
+
Changes::Criticality.breaking
|
836
|
+
else
|
837
|
+
Changes::Criticality.non_breaking
|
838
|
+
end
|
796
839
|
@directive = directive
|
797
840
|
@argument = argument
|
798
|
-
@breaking = false
|
799
841
|
end
|
800
842
|
|
801
843
|
def message
|
802
844
|
"Argument `#{argument.name}` was added to directive `#{directive.name}`"
|
803
845
|
end
|
804
|
-
|
805
|
-
def breaking?
|
806
|
-
!!@breaking
|
807
|
-
end
|
808
|
-
end
|
809
|
-
|
810
|
-
class InputFieldTypeChanged < AbstractChange
|
811
|
-
include SafeTypeChange
|
812
|
-
|
813
|
-
attr_reader :input_type, :old_input_field, :new_input_field
|
814
|
-
|
815
|
-
def initialize(input_type, old_input_field, new_input_field)
|
816
|
-
@input_type = input_type
|
817
|
-
@old_input_field = old_input_field
|
818
|
-
@new_input_field = new_input_field
|
819
|
-
@breaking = !safe_change?(old_input_field.type, new_input_field.type)
|
820
|
-
end
|
821
|
-
|
822
|
-
def message
|
823
|
-
"Input field `#{input_type}.#{old_input_field.name}` changed type from #{old_input_field.type} to #{new_input_field.type}"
|
824
|
-
end
|
825
|
-
|
826
|
-
def breaking?
|
827
|
-
!!@breaking
|
828
|
-
end
|
829
|
-
end
|
830
|
-
|
831
|
-
class FieldArgumentTypeChanged < AbstractChange
|
832
|
-
include SafeTypeChange
|
833
|
-
|
834
|
-
attr_reader :type, :field, :old_argument, :new_argument
|
835
|
-
|
836
|
-
def initialize(type, field, old_argument, new_argument)
|
837
|
-
@type = type
|
838
|
-
@field = field
|
839
|
-
@old_argument = old_argument
|
840
|
-
@new_argument = new_argument
|
841
|
-
@breaking = !safe_change?(old_argument.type, new_argument.type)
|
842
|
-
end
|
843
|
-
|
844
|
-
def message
|
845
|
-
"Type for argument `#{new_argument.name}` on field `#{type.name}.#{field.name}` changed"\
|
846
|
-
" from `#{old_argument.type}` to `#{new_argument.type}`"
|
847
|
-
end
|
848
|
-
|
849
|
-
def breaking?
|
850
|
-
!!@breaking
|
851
|
-
end
|
852
|
-
end
|
853
|
-
|
854
|
-
class DirectiveArgumentTypeChanged < AbstractChange
|
855
|
-
attr_reader :directive, :old_argument, :new_argument
|
856
|
-
|
857
|
-
def initialize(directive, old_argument, new_argument)
|
858
|
-
@directive = directive
|
859
|
-
@old_argument = old_argument
|
860
|
-
@new_argument = new_argument
|
861
|
-
@breaking = false
|
862
|
-
end
|
863
|
-
|
864
|
-
def message
|
865
|
-
"Type for argument `#{new_argument.name}` on directive `#{directive.name}` changed"\
|
866
|
-
" from `#{old_argument.type}` to `#{new_argument.type}`"
|
867
|
-
end
|
868
|
-
|
869
|
-
def breaking?
|
870
|
-
!!@breaking
|
871
|
-
end
|
872
|
-
end
|
873
|
-
|
874
|
-
class FieldTypeChanged < AbstractChange
|
875
|
-
attr_reader :type, :old_field, :new_field
|
876
|
-
|
877
|
-
def initialize(type, old_field, new_field)
|
878
|
-
@type = type
|
879
|
-
@old_field = old_field
|
880
|
-
@new_field = new_field
|
881
|
-
@breaking = true
|
882
|
-
end
|
883
|
-
|
884
|
-
def message
|
885
|
-
"Field `#{type}.#{old_field.name}` changed type from `#{old_field.type}` to `#{new_field.type}`"
|
886
|
-
end
|
887
|
-
|
888
|
-
def breaking?
|
889
|
-
!!@breaking
|
890
|
-
end
|
891
|
-
end
|
892
|
-
|
893
|
-
class SchemaMutationTypeChanged < AbstractChange
|
894
|
-
attr_reader :old_schema, :new_schema
|
895
|
-
|
896
|
-
def initialize(old_schema, new_schema)
|
897
|
-
@old_schema = old_schema
|
898
|
-
@new_schema = new_schema
|
899
|
-
@breaking = true
|
900
|
-
end
|
901
|
-
|
902
|
-
def message
|
903
|
-
"Schema mutation root has changed from `#{old_schema.mutation}` to `#{new_schema.mutation}`"
|
904
|
-
end
|
905
|
-
|
906
|
-
def breaking?
|
907
|
-
!!@breaking
|
908
|
-
end
|
909
|
-
end
|
910
|
-
|
911
|
-
class SchemaSubscriptionTypeChanged < AbstractChange
|
912
|
-
def initialize(old_schema, new_schema)
|
913
|
-
@old_schema = old_schema
|
914
|
-
@new_schema = new_schema
|
915
|
-
@breaking = true
|
916
|
-
end
|
917
|
-
|
918
|
-
def message
|
919
|
-
"Schema subscription type has changed from `#{old_schema.subscription}` to `#{new_schema.subscription}`"
|
920
|
-
end
|
921
|
-
|
922
|
-
def breaking?
|
923
|
-
!!@breaking
|
924
|
-
end
|
925
846
|
end
|
926
847
|
end
|
927
848
|
end
|