graphql-schema_comparator 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Build Status](https://travis-ci.org/xuorig/graphql-schema_comparator.svg?branch=master)](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
|