graphql-schema_comparator 0.2.0 → 0.3.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 +15 -0
- data/lib/graphql/schema_comparator.rb +13 -1
- data/lib/graphql/schema_comparator/changes.rb +381 -113
- data/lib/graphql/schema_comparator/diff/directive.rb +63 -0
- data/lib/graphql/schema_comparator/diff/directive_argument.rb +37 -0
- data/lib/graphql/schema_comparator/diff/field.rb +0 -2
- data/lib/graphql/schema_comparator/diff/has_directives.rb +0 -0
- data/lib/graphql/schema_comparator/diff/schema.rb +32 -12
- data/lib/graphql/schema_comparator/result.rb +2 -2
- data/lib/graphql/schema_comparator/version.rb +1 -1
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: de7ab5aa8da6dcfe86369cab4a4278f4e243365e
|
4
|
+
data.tar.gz: a0cf175638ba9b983a793555514c3c51a91c6725
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cc5dd38fb9f71124dd18323a1ceb8e123994476bee91a134854140e160075a515c0975fb60fc662d918ea7deac6e0b6c48a015da5afb53a14191a22de8514837
|
7
|
+
data.tar.gz: c9f9ca7b7672748246ee7519b35e06f36b49ad55fba891699249de86107518fac2f0033afe22e2ae8529b5d4f9b9ba17a31aa701a977ab9a9ae8bcdf9ba2d095
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,20 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 0.3.0 (Oct 14 2017)
|
4
|
+
|
5
|
+
### New features
|
6
|
+
|
7
|
+
- Top level Directive definitions are now diffed, but not directives used on definitions (Coming soon)
|
8
|
+
- Base class for changes added.
|
9
|
+
|
10
|
+
### breaking changes
|
11
|
+
|
12
|
+
- `breaking` method on change objects has been renamed `breaking?` for style
|
13
|
+
|
14
|
+
### Bug fixes
|
15
|
+
|
16
|
+
- Fix message for `EnumValueRemoved` (#5)
|
17
|
+
|
3
18
|
## 0.2.0 (Aug 18 2017)
|
4
19
|
|
5
20
|
### New features
|
@@ -1,9 +1,21 @@
|
|
1
1
|
require "graphql"
|
2
|
+
|
2
3
|
require "graphql/schema_comparator/version"
|
3
|
-
require "graphql/schema_comparator/diff/schema"
|
4
4
|
require "graphql/schema_comparator/changes"
|
5
5
|
require "graphql/schema_comparator/result"
|
6
6
|
|
7
|
+
require "graphql/schema_comparator/diff/schema"
|
8
|
+
require "graphql/schema_comparator/diff/argument"
|
9
|
+
require "graphql/schema_comparator/diff/directive"
|
10
|
+
require "graphql/schema_comparator/diff/directive_argument"
|
11
|
+
require "graphql/schema_comparator/diff/enum"
|
12
|
+
require "graphql/schema_comparator/diff/field"
|
13
|
+
require "graphql/schema_comparator/diff/input_object"
|
14
|
+
require "graphql/schema_comparator/diff/input_field"
|
15
|
+
require "graphql/schema_comparator/diff/object_type"
|
16
|
+
require "graphql/schema_comparator/diff/interface"
|
17
|
+
require "graphql/schema_comparator/diff/union"
|
18
|
+
|
7
19
|
module GraphQL
|
8
20
|
module SchemaComparator
|
9
21
|
def self.compare(old_schema, new_schema)
|
@@ -1,36 +1,52 @@
|
|
1
1
|
module GraphQL
|
2
2
|
module SchemaComparator
|
3
3
|
module Changes
|
4
|
-
|
4
|
+
class AbstractChange
|
5
|
+
def message
|
6
|
+
raise NotImplementedError
|
7
|
+
end
|
8
|
+
|
9
|
+
def breaking?
|
10
|
+
raise NotImplementedError
|
11
|
+
end
|
12
|
+
end
|
5
13
|
|
6
|
-
class TypeRemoved
|
7
|
-
attr_reader :removed_type
|
14
|
+
class TypeRemoved < AbstractChange
|
15
|
+
attr_reader :removed_type
|
8
16
|
|
9
17
|
def initialize(removed_type)
|
10
18
|
@removed_type = removed_type
|
11
19
|
@breaking = true
|
12
20
|
end
|
13
21
|
|
22
|
+
def breaking?
|
23
|
+
!!@breaking
|
24
|
+
end
|
25
|
+
|
14
26
|
def message
|
15
|
-
"
|
27
|
+
"`#{removed_type.name}` was removed"
|
16
28
|
end
|
17
29
|
end
|
18
30
|
|
19
|
-
class DirectiveRemoved
|
20
|
-
attr_reader :
|
31
|
+
class DirectiveRemoved < AbstractChange
|
32
|
+
attr_reader :directive
|
21
33
|
|
22
|
-
def initialize(
|
23
|
-
@
|
34
|
+
def initialize(directive)
|
35
|
+
@directive = directive
|
24
36
|
@breaking = true
|
25
37
|
end
|
26
38
|
|
27
39
|
def message
|
28
|
-
"
|
40
|
+
"`#{directive.name}` was removed"
|
41
|
+
end
|
42
|
+
|
43
|
+
def breaking?
|
44
|
+
!!@breaking
|
29
45
|
end
|
30
46
|
end
|
31
47
|
|
32
|
-
class TypeKindChanged
|
33
|
-
attr_reader :old_type, :new_type
|
48
|
+
class TypeKindChanged < AbstractChange
|
49
|
+
attr_reader :old_type, :new_type
|
34
50
|
|
35
51
|
def initialize(old_type, new_type)
|
36
52
|
@old_type = old_type
|
@@ -41,10 +57,14 @@ module GraphQL
|
|
41
57
|
def message
|
42
58
|
"`#{old_type.name}` kind changed from `#{old_type.kind}` to `#{new_type.kind}`"
|
43
59
|
end
|
60
|
+
|
61
|
+
def breaking?
|
62
|
+
!!@breaking
|
63
|
+
end
|
44
64
|
end
|
45
65
|
|
46
|
-
class EnumValueRemoved
|
47
|
-
attr_reader :enum_value, :enum_type
|
66
|
+
class EnumValueRemoved < AbstractChange
|
67
|
+
attr_reader :enum_value, :enum_type
|
48
68
|
|
49
69
|
def initialize(enum_type, enum_value)
|
50
70
|
@enum_value = enum_value
|
@@ -55,10 +75,14 @@ module GraphQL
|
|
55
75
|
def message
|
56
76
|
"Enum value `#{enum_value.name}` was removed from enum `#{enum_type.name}`"
|
57
77
|
end
|
78
|
+
|
79
|
+
def breaking?
|
80
|
+
!!@breaking
|
81
|
+
end
|
58
82
|
end
|
59
83
|
|
60
|
-
class UnionMemberRemoved
|
61
|
-
attr_reader :union_type, :union_member
|
84
|
+
class UnionMemberRemoved < AbstractChange
|
85
|
+
attr_reader :union_type, :union_member
|
62
86
|
|
63
87
|
def initialize(union_type, union_member)
|
64
88
|
@union_member = union_member
|
@@ -69,10 +93,14 @@ module GraphQL
|
|
69
93
|
def message
|
70
94
|
"Union member `#{union_member.name}` was removed from Union type `#{union_type.name}`"
|
71
95
|
end
|
96
|
+
|
97
|
+
def breaking?
|
98
|
+
!!@breaking
|
99
|
+
end
|
72
100
|
end
|
73
101
|
|
74
|
-
class InputFieldRemoved
|
75
|
-
attr_reader :input_object_type, :field
|
102
|
+
class InputFieldRemoved < AbstractChange
|
103
|
+
attr_reader :input_object_type, :field
|
76
104
|
|
77
105
|
def initialize(input_object_type, field)
|
78
106
|
@input_object_type = input_object_type
|
@@ -83,10 +111,14 @@ module GraphQL
|
|
83
111
|
def message
|
84
112
|
"Input field `#{field.name}` was removed from input object type `#{input_object_type.name}`"
|
85
113
|
end
|
114
|
+
|
115
|
+
def breaking?
|
116
|
+
!!@breaking
|
117
|
+
end
|
86
118
|
end
|
87
119
|
|
88
|
-
class FieldArgumentRemoved
|
89
|
-
attr_reader :object_type, :field, :argument
|
120
|
+
class FieldArgumentRemoved < AbstractChange
|
121
|
+
attr_reader :object_type, :field, :argument
|
90
122
|
|
91
123
|
def initialize(object_type, field, argument)
|
92
124
|
@object_type = object_type
|
@@ -98,14 +130,32 @@ module GraphQL
|
|
98
130
|
def message
|
99
131
|
"Argument `#{argument.name}: #{argument.type}` was removed from field `#{object_type.name}.#{field.name}`"
|
100
132
|
end
|
133
|
+
|
134
|
+
def breaking?
|
135
|
+
!!@breaking
|
136
|
+
end
|
101
137
|
end
|
102
138
|
|
103
|
-
|
104
|
-
|
105
|
-
|
139
|
+
class DirectiveArgumentRemoved < AbstractChange
|
140
|
+
attr_reader :directive, :argument
|
141
|
+
|
142
|
+
def initialize(directive, argument)
|
143
|
+
@directive = directive
|
144
|
+
@argument = argument
|
145
|
+
@breaking = true
|
146
|
+
end
|
106
147
|
|
107
|
-
|
108
|
-
|
148
|
+
def message
|
149
|
+
"Argument `#{argument.name}` was removed from directive `#{directive.name}`"
|
150
|
+
end
|
151
|
+
|
152
|
+
def breaking?
|
153
|
+
!!@breaking
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
class SchemaQueryTypeChanged < AbstractChange
|
158
|
+
attr_reader :old_schema, :new_schema
|
109
159
|
|
110
160
|
def initialize(old_schema, new_schema)
|
111
161
|
@old_schema = old_schema
|
@@ -116,10 +166,14 @@ module GraphQL
|
|
116
166
|
def message
|
117
167
|
"Schema query root has changed from `#{old_schema.query.name}` to `#{new_schema.query.name}`"
|
118
168
|
end
|
169
|
+
|
170
|
+
def breaking?
|
171
|
+
!!@breaking
|
172
|
+
end
|
119
173
|
end
|
120
174
|
|
121
|
-
class FieldRemoved
|
122
|
-
attr_reader :object_type, :field
|
175
|
+
class FieldRemoved < AbstractChange
|
176
|
+
attr_reader :object_type, :field
|
123
177
|
|
124
178
|
def initialize(object_type, field)
|
125
179
|
@object_type = object_type
|
@@ -130,14 +184,32 @@ module GraphQL
|
|
130
184
|
def message
|
131
185
|
"Field `#{field.name}` was removed from object type `#{object_type.name}`"
|
132
186
|
end
|
187
|
+
|
188
|
+
def breaking?
|
189
|
+
!!@breaking
|
190
|
+
end
|
133
191
|
end
|
134
192
|
|
135
|
-
|
136
|
-
|
137
|
-
|
193
|
+
class DirectiveLocationRemoved < AbstractChange
|
194
|
+
attr_reader :directive, :location
|
195
|
+
|
196
|
+
def initialize(directive, location)
|
197
|
+
@directive = directive
|
198
|
+
@location = location
|
199
|
+
@breaking = true
|
200
|
+
end
|
201
|
+
|
202
|
+
def message
|
203
|
+
"Location `#{location}` was removed from directive `#{directive.name}`"
|
204
|
+
end
|
205
|
+
|
206
|
+
def breaking?
|
207
|
+
!!@breaking
|
208
|
+
end
|
209
|
+
end
|
138
210
|
|
139
|
-
class ObjectTypeInterfaceRemoved
|
140
|
-
attr_reader :interface, :object_type
|
211
|
+
class ObjectTypeInterfaceRemoved < AbstractChange
|
212
|
+
attr_reader :interface, :object_type
|
141
213
|
|
142
214
|
def initialize(interface, object_type)
|
143
215
|
@interface = interface
|
@@ -148,12 +220,14 @@ module GraphQL
|
|
148
220
|
def message
|
149
221
|
"`#{object_type.name}` object type no longer implements `#{interface.name}` interface"
|
150
222
|
end
|
151
|
-
end
|
152
223
|
|
153
|
-
|
224
|
+
def breaking?
|
225
|
+
!!@breaking
|
226
|
+
end
|
227
|
+
end
|
154
228
|
|
155
|
-
class TypeAdded
|
156
|
-
attr_reader :type
|
229
|
+
class TypeAdded < AbstractChange
|
230
|
+
attr_reader :type
|
157
231
|
|
158
232
|
def initialize(type)
|
159
233
|
@type = type
|
@@ -163,18 +237,31 @@ module GraphQL
|
|
163
237
|
def message
|
164
238
|
"Type `#{type.name}` was added"
|
165
239
|
end
|
240
|
+
|
241
|
+
def breaking?
|
242
|
+
!!@breaking
|
243
|
+
end
|
166
244
|
end
|
167
245
|
|
168
|
-
class DirectiveAdded
|
169
|
-
attr_reader :
|
246
|
+
class DirectiveAdded < AbstractChange
|
247
|
+
attr_reader :directive
|
170
248
|
|
171
|
-
def initialize(
|
249
|
+
def initialize(directive)
|
250
|
+
@directive = directive
|
172
251
|
@breaking = false
|
173
252
|
end
|
253
|
+
|
254
|
+
def message
|
255
|
+
"Directive `#{directive.name}` was added"
|
256
|
+
end
|
257
|
+
|
258
|
+
def breaking?
|
259
|
+
!!@breaking
|
260
|
+
end
|
174
261
|
end
|
175
262
|
|
176
|
-
class TypeDescriptionChanged
|
177
|
-
attr_reader :old_type, :new_type
|
263
|
+
class TypeDescriptionChanged < AbstractChange
|
264
|
+
attr_reader :old_type, :new_type
|
178
265
|
|
179
266
|
def initialize(old_type, new_type)
|
180
267
|
@old_type = old_type
|
@@ -185,10 +272,14 @@ module GraphQL
|
|
185
272
|
def message
|
186
273
|
"Description `#{old_type.description}` on type `#{old_type.name}` has changed to `#{new_type.description}`"
|
187
274
|
end
|
275
|
+
|
276
|
+
def breaking?
|
277
|
+
!!@breaking
|
278
|
+
end
|
188
279
|
end
|
189
280
|
|
190
|
-
class EnumValueAdded
|
191
|
-
attr_reader :enum_type, :enum_value
|
281
|
+
class EnumValueAdded < AbstractChange
|
282
|
+
attr_reader :enum_type, :enum_value
|
192
283
|
|
193
284
|
def initialize(enum_type, enum_value)
|
194
285
|
@enum_type = enum_type
|
@@ -199,20 +290,26 @@ module GraphQL
|
|
199
290
|
def message
|
200
291
|
"Enum value `#{enum_value.name}` was added to enum `#{enum_type.name}`"
|
201
292
|
end
|
293
|
+
|
294
|
+
def breaking?
|
295
|
+
!!@breaking
|
296
|
+
end
|
202
297
|
end
|
203
298
|
|
204
|
-
|
299
|
+
# TODO
|
300
|
+
class EnumValueDescriptionChanged < AbstractChange
|
205
301
|
def initialize(*)
|
206
302
|
end
|
207
303
|
end
|
208
304
|
|
209
|
-
|
305
|
+
# TODO
|
306
|
+
class EnumValueDeprecated < AbstractChange
|
210
307
|
def initialize(*)
|
211
308
|
end
|
212
309
|
end
|
213
310
|
|
214
|
-
class UnionMemberAdded
|
215
|
-
attr_reader :union_type, :union_member
|
311
|
+
class UnionMemberAdded < AbstractChange
|
312
|
+
attr_reader :union_type, :union_member
|
216
313
|
|
217
314
|
def initialize(union_type, union_member)
|
218
315
|
@union_member = union_member
|
@@ -223,10 +320,14 @@ module GraphQL
|
|
223
320
|
def message
|
224
321
|
"Union member `#{union_member.name}` was added to Union type `#{union_type.name}`"
|
225
322
|
end
|
323
|
+
|
324
|
+
def breaking?
|
325
|
+
!!@breaking
|
326
|
+
end
|
226
327
|
end
|
227
328
|
|
228
|
-
class InputFieldDescriptionChanged
|
229
|
-
attr_reader :input_type, :old_field, :new_field
|
329
|
+
class InputFieldDescriptionChanged < AbstractChange
|
330
|
+
attr_reader :input_type, :old_field, :new_field
|
230
331
|
|
231
332
|
def initialize(input_type, old_field, new_field)
|
232
333
|
@input_type = input_type
|
@@ -239,15 +340,33 @@ module GraphQL
|
|
239
340
|
"Input field `#{input_type.name}.#{old_field.name}` description changed"\
|
240
341
|
" from `#{old_field.description}` to `#{new_field.description}`"
|
241
342
|
end
|
343
|
+
|
344
|
+
def breaking?
|
345
|
+
!!@breaking
|
346
|
+
end
|
242
347
|
end
|
243
348
|
|
244
|
-
class DirectiveDescriptionChanged
|
245
|
-
|
349
|
+
class DirectiveDescriptionChanged < AbstractChange
|
350
|
+
attr_reader :old_directive, :new_directive
|
351
|
+
|
352
|
+
def initialize(old_directive, new_directive)
|
353
|
+
@old_directive = old_directive
|
354
|
+
@new_directive = new_directive
|
355
|
+
@breaking = false
|
356
|
+
end
|
357
|
+
|
358
|
+
def message
|
359
|
+
"Directive `#{new_directive.name}` description changed"\
|
360
|
+
" from `#{old_directive.description}` to `#{new_directive.description}`"
|
361
|
+
end
|
362
|
+
|
363
|
+
def breaking?
|
364
|
+
!!@breaking
|
246
365
|
end
|
247
366
|
end
|
248
367
|
|
249
|
-
class FieldDescriptionChanged
|
250
|
-
attr_reader :type, :old_field, :new_field
|
368
|
+
class FieldDescriptionChanged < AbstractChange
|
369
|
+
attr_reader :type, :old_field, :new_field
|
251
370
|
|
252
371
|
def initialize(type, old_field, new_field)
|
253
372
|
@type = type
|
@@ -260,10 +379,14 @@ module GraphQL
|
|
260
379
|
"Field `#{type.name}.#{old_field.name}` description changed"\
|
261
380
|
" from `#{old_field.description}` to `#{new_field.description}`"
|
262
381
|
end
|
382
|
+
|
383
|
+
def breaking?
|
384
|
+
!!@breaking
|
385
|
+
end
|
263
386
|
end
|
264
387
|
|
265
|
-
class FieldArgumentDescriptionChanged
|
266
|
-
attr_reader :type, :field, :old_argument, :new_argument
|
388
|
+
class FieldArgumentDescriptionChanged < AbstractChange
|
389
|
+
attr_reader :type, :field, :old_argument, :new_argument
|
267
390
|
|
268
391
|
def initialize(type, field, old_argument, new_argument)
|
269
392
|
@type = type
|
@@ -277,15 +400,34 @@ module GraphQL
|
|
277
400
|
"Description for argument `#{new_argument.name}` on field `#{type.name}.#{field.name}` changed"\
|
278
401
|
" from `#{old_argument.description}` to `#{new_argument.description}`"
|
279
402
|
end
|
403
|
+
|
404
|
+
def breaking?
|
405
|
+
!!@breaking
|
406
|
+
end
|
280
407
|
end
|
281
408
|
|
282
|
-
class DirectiveArgumentDescriptionChanged
|
283
|
-
|
409
|
+
class DirectiveArgumentDescriptionChanged < AbstractChange
|
410
|
+
attr_reader :directive, :old_argument, :new_argument
|
411
|
+
|
412
|
+
def initialize(directive, old_argument, new_argument)
|
413
|
+
@directive = directive
|
414
|
+
@old_argument = old_argument
|
415
|
+
@new_argument = new_argument
|
416
|
+
@breaking = false
|
417
|
+
end
|
418
|
+
|
419
|
+
def message
|
420
|
+
"Description for argument `#{new_argument.name}` on directive `#{directive.name}` changed"\
|
421
|
+
" from `#{old_argument.description}` to `#{new_argument.description}`"
|
422
|
+
end
|
423
|
+
|
424
|
+
def breaking?
|
425
|
+
!!@breaking
|
284
426
|
end
|
285
427
|
end
|
286
428
|
|
287
|
-
class FieldDeprecationChanged
|
288
|
-
attr_reader :type, :old_field, :new_field
|
429
|
+
class FieldDeprecationChanged < AbstractChange
|
430
|
+
attr_reader :type, :old_field, :new_field
|
289
431
|
|
290
432
|
def initialize(type, old_field, new_field)
|
291
433
|
@type = type
|
@@ -298,10 +440,14 @@ module GraphQL
|
|
298
440
|
"Deprecation reason on field `#{type.name}.#{new_field.name}` has changed "\
|
299
441
|
"from `#{old_field.deprecation_reason}` to `#{new_field.deprecation_reason}`"
|
300
442
|
end
|
443
|
+
|
444
|
+
def breaking?
|
445
|
+
!!@breaking
|
446
|
+
end
|
301
447
|
end
|
302
448
|
|
303
|
-
class InputFieldDefaultChanged
|
304
|
-
attr_reader :input_type, :old_field, :new_field
|
449
|
+
class InputFieldDefaultChanged < AbstractChange
|
450
|
+
attr_reader :input_type, :old_field, :new_field
|
305
451
|
|
306
452
|
def initialize(input_type, old_field, new_field)
|
307
453
|
@input_type = input_type
|
@@ -314,10 +460,14 @@ module GraphQL
|
|
314
460
|
"Input field `#{input_type.name}.#{old_field.name}` default changed"\
|
315
461
|
" from `#{old_field.default_value}` to `#{new_field.default_value}`"
|
316
462
|
end
|
463
|
+
|
464
|
+
def breaking?
|
465
|
+
!!@breaking
|
466
|
+
end
|
317
467
|
end
|
318
468
|
|
319
|
-
class FieldArgumentDefaultChanged
|
320
|
-
attr_reader :type, :field, :old_argument, :new_argument
|
469
|
+
class FieldArgumentDefaultChanged < AbstractChange
|
470
|
+
attr_reader :type, :field, :old_argument, :new_argument
|
321
471
|
|
322
472
|
def initialize(type, field, old_argument, new_argument)
|
323
473
|
@type = type
|
@@ -331,15 +481,34 @@ module GraphQL
|
|
331
481
|
"Default value for argument `#{new_argument.name}` on field `#{type.name}.#{field.name}` changed"\
|
332
482
|
" from `#{old_argument.default_value}` to `#{new_argument.default_value}`"
|
333
483
|
end
|
484
|
+
|
485
|
+
def breaking?
|
486
|
+
!!@breaking
|
487
|
+
end
|
334
488
|
end
|
335
489
|
|
336
|
-
class DirectiveArgumentDefaultChanged
|
337
|
-
|
490
|
+
class DirectiveArgumentDefaultChanged < AbstractChange
|
491
|
+
attr_reader :directive, :old_argument, :new_argument
|
492
|
+
|
493
|
+
def initialize(directive, old_argument, new_argument)
|
494
|
+
@directive = directive
|
495
|
+
@old_argument = old_argument
|
496
|
+
@new_argument = new_argument
|
497
|
+
@breaking = false
|
498
|
+
end
|
499
|
+
|
500
|
+
def message
|
501
|
+
"Default value for argument `#{new_argument.name}` on directive `#{directive.name}` changed"\
|
502
|
+
" from `#{old_argument.default_value}` to `#{new_argument.default_value}`"
|
503
|
+
end
|
504
|
+
|
505
|
+
def breaking?
|
506
|
+
!!@breaking
|
338
507
|
end
|
339
508
|
end
|
340
509
|
|
341
|
-
class ObjectTypeInterfaceAdded
|
342
|
-
attr_reader :interface, :object_type
|
510
|
+
class ObjectTypeInterfaceAdded < AbstractChange
|
511
|
+
attr_reader :interface, :object_type
|
343
512
|
|
344
513
|
def initialize(interface, object_type)
|
345
514
|
@interface = interface
|
@@ -350,10 +519,14 @@ module GraphQL
|
|
350
519
|
def message
|
351
520
|
"`#{object_type.name}` object implements `#{interface.name}` interface"
|
352
521
|
end
|
522
|
+
|
523
|
+
def breaking?
|
524
|
+
!!@breaking
|
525
|
+
end
|
353
526
|
end
|
354
527
|
|
355
|
-
class FieldAdded
|
356
|
-
attr_reader :object_type, :field
|
528
|
+
class FieldAdded < AbstractChange
|
529
|
+
attr_reader :object_type, :field
|
357
530
|
|
358
531
|
def initialize(object_type, field)
|
359
532
|
@object_type = object_type
|
@@ -364,137 +537,176 @@ module GraphQL
|
|
364
537
|
def message
|
365
538
|
"Field `#{field.name}` was added to object type `#{object_type.name}`"
|
366
539
|
end
|
540
|
+
|
541
|
+
def breaking?
|
542
|
+
!!@breaking
|
543
|
+
end
|
367
544
|
end
|
368
545
|
|
369
|
-
class DirectiveLocationAdded
|
370
|
-
|
546
|
+
class DirectiveLocationAdded < AbstractChange
|
547
|
+
attr_reader :directive, :location
|
548
|
+
|
549
|
+
def initialize(directive, location)
|
550
|
+
@directive = directive
|
551
|
+
@location = location
|
552
|
+
@breaking = false
|
553
|
+
end
|
554
|
+
|
555
|
+
def message
|
556
|
+
"Location `#{location}` was added to directive `#{directive.name}`"
|
557
|
+
end
|
558
|
+
|
559
|
+
def breaking?
|
560
|
+
!!@breaking
|
371
561
|
end
|
372
562
|
end
|
373
563
|
|
374
|
-
|
564
|
+
# TODO
|
565
|
+
class FieldAstDirectiveAdded < AbstractChange
|
375
566
|
def initialize(*)
|
376
567
|
end
|
377
568
|
end
|
378
569
|
|
379
|
-
|
570
|
+
# TODO
|
571
|
+
class FieldAstDirectiveRemoved < AbstractChange
|
380
572
|
def initialize(*)
|
381
573
|
end
|
382
574
|
end
|
383
575
|
|
384
|
-
|
576
|
+
# TODO
|
577
|
+
class EnumValueAstDirectiveAdded < AbstractChange
|
385
578
|
def initialize(*)
|
386
579
|
end
|
387
580
|
end
|
388
581
|
|
389
|
-
|
582
|
+
# TODO
|
583
|
+
class EnumValueAstDirectiveRemoved < AbstractChange
|
390
584
|
def initialize(*)
|
391
585
|
end
|
392
586
|
end
|
393
587
|
|
394
|
-
|
588
|
+
# TODO
|
589
|
+
class InputFieldAstDirectiveAdded < AbstractChange
|
395
590
|
def initialize(*)
|
396
591
|
end
|
397
592
|
end
|
398
593
|
|
399
|
-
|
594
|
+
# TODO
|
595
|
+
class InputFieldAstDirectiveRemoved < AbstractChange
|
400
596
|
def initialize(*)
|
401
597
|
end
|
402
598
|
end
|
403
599
|
|
404
|
-
|
600
|
+
# TODO
|
601
|
+
class DirectiveArgumentAstDirectiveAdded < AbstractChange
|
405
602
|
def initialize(*)
|
406
603
|
end
|
407
604
|
end
|
408
605
|
|
409
|
-
|
606
|
+
# TODO
|
607
|
+
class DirectiveArgumentAstDirectiveRemoved < AbstractChange
|
410
608
|
def initialize(*)
|
411
609
|
end
|
412
610
|
end
|
413
611
|
|
414
|
-
|
612
|
+
# TODO
|
613
|
+
class FieldArgumentAstDirectiveAdded < AbstractChange
|
415
614
|
def initialize(*)
|
416
615
|
end
|
417
616
|
end
|
418
617
|
|
419
|
-
|
618
|
+
# TODO
|
619
|
+
class FieldArgumentAstDirectiveRemoved < AbstractChange
|
420
620
|
def initialize(*)
|
421
621
|
end
|
422
622
|
end
|
423
623
|
|
424
|
-
|
624
|
+
# TODO
|
625
|
+
class ObjectTypeAstDirectiveAdded < AbstractChange
|
425
626
|
def initialize(*)
|
426
627
|
end
|
427
628
|
end
|
428
629
|
|
429
|
-
|
630
|
+
# TODO
|
631
|
+
class ObjectTypeAstDirectiveRemoved < AbstractChange
|
430
632
|
def initialize(*)
|
431
633
|
end
|
432
634
|
end
|
433
635
|
|
434
|
-
|
636
|
+
# TODO
|
637
|
+
class InterfaceTypeAstDirectiveAdded < AbstractChange
|
435
638
|
def initialize(*)
|
436
639
|
end
|
437
640
|
end
|
438
641
|
|
439
|
-
|
642
|
+
# TODO
|
643
|
+
class InterfaceTypeAstDirectiveRemoved < AbstractChange
|
440
644
|
def initialize(*)
|
441
645
|
end
|
442
646
|
end
|
443
647
|
|
444
|
-
|
648
|
+
# TODO
|
649
|
+
class UnionTypeAstDirectiveAdded < AbstractChange
|
445
650
|
def initialize(*)
|
446
651
|
end
|
447
652
|
end
|
448
653
|
|
449
|
-
|
654
|
+
# TODO
|
655
|
+
class UnionTypeAstDirectiveRemoved < AbstractChange
|
450
656
|
def initialize(*)
|
451
657
|
end
|
452
658
|
end
|
453
659
|
|
454
|
-
|
660
|
+
# TODO
|
661
|
+
class EnumTypeAstDirectiveAdded < AbstractChange
|
455
662
|
def initialize(*)
|
456
663
|
end
|
457
664
|
end
|
458
665
|
|
459
|
-
|
666
|
+
# TODO
|
667
|
+
class EnumTypeAstDirectiveRemoved < AbstractChange
|
460
668
|
def initialize(*)
|
461
669
|
end
|
462
670
|
end
|
463
671
|
|
464
|
-
|
672
|
+
# TODO
|
673
|
+
class ScalarTypeAstDirectiveAdded < AbstractChange
|
465
674
|
def initialize(*)
|
466
675
|
end
|
467
676
|
end
|
468
677
|
|
469
|
-
|
678
|
+
# TODO
|
679
|
+
class ScalarTypeAstDirectiveRemoved < AbstractChange
|
470
680
|
def initialize(*)
|
471
681
|
end
|
472
682
|
end
|
473
683
|
|
474
|
-
|
684
|
+
# TODO
|
685
|
+
class InputObjectTypeAstDirectiveAdded < AbstractChange
|
475
686
|
def initialize(*)
|
476
687
|
end
|
477
688
|
end
|
478
689
|
|
479
|
-
|
690
|
+
# TODO
|
691
|
+
class InputObjectTypeAstDirectiveRemoved < AbstractChange
|
480
692
|
def initialize(*)
|
481
693
|
end
|
482
694
|
end
|
483
695
|
|
484
|
-
|
696
|
+
# TODO
|
697
|
+
class SchemaAstDirectiveAdded < AbstractChange
|
485
698
|
def initialize(*)
|
486
699
|
end
|
487
700
|
end
|
488
701
|
|
489
|
-
|
702
|
+
# TODO
|
703
|
+
class SchemaAstDirectiveRemoved < AbstractChange
|
490
704
|
def initialize(*)
|
491
705
|
end
|
492
706
|
end
|
493
707
|
|
494
|
-
|
495
|
-
|
496
|
-
class InputFieldAdded
|
497
|
-
attr_reader :input_object_type, :field, :breaking
|
708
|
+
class InputFieldAdded < AbstractChange
|
709
|
+
attr_reader :input_object_type, :field
|
498
710
|
|
499
711
|
def initialize(input_object_type, field)
|
500
712
|
@input_object_type = input_object_type
|
@@ -505,10 +717,14 @@ module GraphQL
|
|
505
717
|
def message
|
506
718
|
"Input field `#{field.name}` was added to input object type `#{input_object_type.name}`"
|
507
719
|
end
|
720
|
+
|
721
|
+
def breaking?
|
722
|
+
!!@breaking
|
723
|
+
end
|
508
724
|
end
|
509
725
|
|
510
|
-
class FieldArgumentAdded
|
511
|
-
attr_reader :type, :field, :argument
|
726
|
+
class FieldArgumentAdded < AbstractChange
|
727
|
+
attr_reader :type, :field, :argument
|
512
728
|
|
513
729
|
def initialize(type, field, argument)
|
514
730
|
@type = type
|
@@ -521,15 +737,32 @@ module GraphQL
|
|
521
737
|
def message
|
522
738
|
"Argument `#{argument.name}: #{argument.type}` added to field `#{type.name}.#{field.name}`"
|
523
739
|
end
|
740
|
+
|
741
|
+
def breaking?
|
742
|
+
!!@breaking
|
743
|
+
end
|
524
744
|
end
|
525
745
|
|
526
|
-
class DirectiveArgumentAdded
|
527
|
-
|
746
|
+
class DirectiveArgumentAdded < AbstractChange
|
747
|
+
attr_reader :directive, :argument
|
748
|
+
|
749
|
+
def initialize(directive, argument)
|
750
|
+
@directive = directive
|
751
|
+
@argument = argument
|
752
|
+
@breaking = false
|
753
|
+
end
|
754
|
+
|
755
|
+
def message
|
756
|
+
"Argument `#{argument.name}` was added to directive `#{directive.name}`"
|
757
|
+
end
|
758
|
+
|
759
|
+
def breaking?
|
760
|
+
!!@breaking
|
528
761
|
end
|
529
762
|
end
|
530
763
|
|
531
|
-
class InputFieldTypeChanged
|
532
|
-
attr_reader :input_type, :old_input_field, :new_input_field
|
764
|
+
class InputFieldTypeChanged < AbstractChange
|
765
|
+
attr_reader :input_type, :old_input_field, :new_input_field
|
533
766
|
|
534
767
|
def initialize(input_type, old_input_field, new_input_field)
|
535
768
|
@input_type = input_type
|
@@ -541,10 +774,14 @@ module GraphQL
|
|
541
774
|
def message
|
542
775
|
"Input field `#{input_type}.#{old_input_field.name}` changed type from #{old_input_field.type} to #{new_input_field.type}"
|
543
776
|
end
|
777
|
+
|
778
|
+
def breaking?
|
779
|
+
!!@breaking
|
780
|
+
end
|
544
781
|
end
|
545
782
|
|
546
|
-
class FieldArgumentTypeChanged
|
547
|
-
attr_reader :type, :field, :old_argument, :new_argument
|
783
|
+
class FieldArgumentTypeChanged < AbstractChange
|
784
|
+
attr_reader :type, :field, :old_argument, :new_argument
|
548
785
|
|
549
786
|
def initialize(type, field, old_argument, new_argument)
|
550
787
|
@type = type
|
@@ -558,15 +795,34 @@ module GraphQL
|
|
558
795
|
"Type for argument `#{new_argument.name}` on field `#{type.name}.#{field.name}` changed"\
|
559
796
|
" from `#{old_argument.type}` to `#{new_argument.type}`"
|
560
797
|
end
|
798
|
+
|
799
|
+
def breaking?
|
800
|
+
!!@breaking
|
801
|
+
end
|
561
802
|
end
|
562
803
|
|
563
|
-
class DirectiveArgumentTypeChanged
|
564
|
-
|
804
|
+
class DirectiveArgumentTypeChanged < AbstractChange
|
805
|
+
attr_reader :directive, :old_argument, :new_argument
|
806
|
+
|
807
|
+
def initialize(directive, old_argument, new_argument)
|
808
|
+
@directive = directive
|
809
|
+
@old_argument = old_argument
|
810
|
+
@new_argument = new_argument
|
811
|
+
@breaking = false
|
812
|
+
end
|
813
|
+
|
814
|
+
def message
|
815
|
+
"Type for argument `#{new_argument.name}` on directive `#{directive.name}` changed"\
|
816
|
+
" from `#{old_argument.type}` to `#{new_argument.type}`"
|
817
|
+
end
|
818
|
+
|
819
|
+
def breaking?
|
820
|
+
!!@breaking
|
565
821
|
end
|
566
822
|
end
|
567
823
|
|
568
|
-
class FieldTypeChanged
|
569
|
-
attr_reader :type, :old_field, :new_field
|
824
|
+
class FieldTypeChanged < AbstractChange
|
825
|
+
attr_reader :type, :old_field, :new_field
|
570
826
|
|
571
827
|
def initialize(type, old_field, new_field)
|
572
828
|
@type = type
|
@@ -578,10 +834,14 @@ module GraphQL
|
|
578
834
|
def message
|
579
835
|
"Field `#{type}.#{old_field.name}` changed type from `#{old_field.type}` to `#{new_field.type}`"
|
580
836
|
end
|
837
|
+
|
838
|
+
def breaking?
|
839
|
+
!!@breaking
|
840
|
+
end
|
581
841
|
end
|
582
842
|
|
583
|
-
class SchemaMutationTypeChanged
|
584
|
-
attr_reader :old_schema, :new_schema
|
843
|
+
class SchemaMutationTypeChanged < AbstractChange
|
844
|
+
attr_reader :old_schema, :new_schema
|
585
845
|
|
586
846
|
def initialize(old_schema, new_schema)
|
587
847
|
@old_schema = old_schema
|
@@ -592,9 +852,13 @@ module GraphQL
|
|
592
852
|
def message
|
593
853
|
"Schema mutation root has changed from `#{old_schema.mutation}` to `#{new_schema.mutation}`"
|
594
854
|
end
|
855
|
+
|
856
|
+
def breaking?
|
857
|
+
!!@breaking
|
858
|
+
end
|
595
859
|
end
|
596
860
|
|
597
|
-
class SchemaSubscriptionTypeChanged
|
861
|
+
class SchemaSubscriptionTypeChanged < AbstractChange
|
598
862
|
def initialize(old_schema, new_schema)
|
599
863
|
@old_schema = old_schema
|
600
864
|
@new_schema = new_schema
|
@@ -604,6 +868,10 @@ module GraphQL
|
|
604
868
|
def message
|
605
869
|
"Schema subscription type has changed from `#{old_schema.subscription}` to `#{new_schema.subscription}`"
|
606
870
|
end
|
871
|
+
|
872
|
+
def breaking?
|
873
|
+
!!@breaking
|
874
|
+
end
|
607
875
|
end
|
608
876
|
end
|
609
877
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module GraphQL
|
2
|
+
module SchemaComparator
|
3
|
+
module Diff
|
4
|
+
class Directive
|
5
|
+
def initialize(old_directive, new_directive)
|
6
|
+
@old_directive = old_directive
|
7
|
+
@new_directive = new_directive
|
8
|
+
@old_arguments = old_directive.arguments
|
9
|
+
@new_arguments = new_directive.arguments
|
10
|
+
end
|
11
|
+
|
12
|
+
def diff
|
13
|
+
changes = []
|
14
|
+
|
15
|
+
if old_directive.description != new_directive.description
|
16
|
+
changes << Changes::DirectiveDescriptionChanged.new(old_directive, new_directive)
|
17
|
+
end
|
18
|
+
|
19
|
+
changes += removed_locations.map { |location| Changes::DirectiveLocationRemoved.new(new_directive, location) }
|
20
|
+
changes += added_locations.map { |location| Changes::DirectiveLocationAdded.new(new_directive, location) }
|
21
|
+
changes += added_arguments.map { |argument| Changes::DirectiveArgumentAdded.new(new_directive, argument) }
|
22
|
+
changes += removed_arguments.map { |argument| Changes::DirectiveArgumentRemoved.new(new_directive, argument) }
|
23
|
+
|
24
|
+
each_common_argument do |old_argument, new_argument|
|
25
|
+
changes += Diff::DirectiveArgument.new(new_directive, old_argument, new_argument).diff
|
26
|
+
end
|
27
|
+
|
28
|
+
changes
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def removed_locations
|
34
|
+
(old_directive.locations - new_directive.locations)
|
35
|
+
end
|
36
|
+
|
37
|
+
def added_locations
|
38
|
+
(new_directive.locations - old_directive.locations)
|
39
|
+
end
|
40
|
+
|
41
|
+
def removed_arguments
|
42
|
+
old_arguments.values.select { |arg| !new_arguments[arg.name] }
|
43
|
+
end
|
44
|
+
|
45
|
+
def added_arguments
|
46
|
+
new_arguments.values.select { |arg| !old_arguments[arg.name] }
|
47
|
+
end
|
48
|
+
|
49
|
+
def each_common_argument(&block)
|
50
|
+
intersection = old_arguments.keys & new_arguments.keys
|
51
|
+
intersection.each do |common_arg|
|
52
|
+
old_arg = new_directive.arguments[common_arg]
|
53
|
+
new_arg = old_directive.arguments[common_arg]
|
54
|
+
|
55
|
+
block.call(old_arg, new_arg)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
attr_reader(:old_directive, :new_directive, :old_arguments, :new_arguments)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module GraphQL
|
2
|
+
module SchemaComparator
|
3
|
+
module Diff
|
4
|
+
class DirectiveArgument
|
5
|
+
def initialize(directive, old_arg, new_arg)
|
6
|
+
@directive = directive
|
7
|
+
@old_arg = old_arg
|
8
|
+
@new_arg = new_arg
|
9
|
+
end
|
10
|
+
|
11
|
+
def diff
|
12
|
+
changes = []
|
13
|
+
|
14
|
+
if old_arg.description != new_arg.description
|
15
|
+
changes << Changes::DirectiveArgumentDescriptionChanged.new(directive, old_arg, new_arg)
|
16
|
+
end
|
17
|
+
|
18
|
+
if old_arg.default_value != new_arg.default_value
|
19
|
+
changes << Changes::DirectiveArgumentDefaultChanged.new(directive, old_arg, new_arg)
|
20
|
+
end
|
21
|
+
|
22
|
+
if old_arg.type != new_arg.type
|
23
|
+
changes << Changes::DirectiveArgumentTypeChanged.new(directive, old_arg, new_arg)
|
24
|
+
end
|
25
|
+
|
26
|
+
# TODO directives on directive arguments
|
27
|
+
|
28
|
+
changes
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
attr_reader(:directive, :new_arg, :old_arg)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
File without changes
|
@@ -1,12 +1,3 @@
|
|
1
|
-
require "graphql/schema_comparator/diff/enum"
|
2
|
-
require "graphql/schema_comparator/diff/union"
|
3
|
-
require "graphql/schema_comparator/diff/input_object"
|
4
|
-
require "graphql/schema_comparator/diff/input_field"
|
5
|
-
require "graphql/schema_comparator/diff/object_type"
|
6
|
-
require "graphql/schema_comparator/diff/interface"
|
7
|
-
require "graphql/schema_comparator/diff/field"
|
8
|
-
require "graphql/schema_comparator/diff/argument"
|
9
|
-
|
10
1
|
module GraphQL
|
11
2
|
module SchemaComparator
|
12
3
|
module Diff
|
@@ -17,6 +8,9 @@ module GraphQL
|
|
17
8
|
|
18
9
|
@old_types = old_schema.types
|
19
10
|
@new_types = new_schema.types
|
11
|
+
|
12
|
+
@old_directives = old_schema.directives
|
13
|
+
@new_directives = new_schema.directives
|
20
14
|
end
|
21
15
|
|
22
16
|
def diff
|
@@ -86,8 +80,16 @@ module GraphQL
|
|
86
80
|
end
|
87
81
|
|
88
82
|
def changes_in_directives
|
89
|
-
|
90
|
-
|
83
|
+
changes = []
|
84
|
+
|
85
|
+
changes += removed_directives.map { |directive| Changes::DirectiveRemoved.new(directive) }
|
86
|
+
changes += added_directives.map { |directive| Changes::DirectiveAdded.new(directive) }
|
87
|
+
|
88
|
+
each_common_directive do |old_directive, new_directive|
|
89
|
+
changes += Diff::Directive.new(old_directive, new_directive).diff
|
90
|
+
end
|
91
|
+
|
92
|
+
changes
|
91
93
|
end
|
92
94
|
|
93
95
|
private
|
@@ -110,7 +112,25 @@ module GraphQL
|
|
110
112
|
(new_types.keys - old_types.keys).map { |type_name| new_schema.types[type_name] }
|
111
113
|
end
|
112
114
|
|
113
|
-
|
115
|
+
def removed_directives
|
116
|
+
(old_directives.keys - new_directives.keys).map { |directive_name| old_schema.directives[directive_name] }
|
117
|
+
end
|
118
|
+
|
119
|
+
def added_directives
|
120
|
+
(new_directives.keys - old_directives.keys).map { |directive_name| new_schema.directives[directive_name] }
|
121
|
+
end
|
122
|
+
|
123
|
+
def each_common_directive(&block)
|
124
|
+
intersection = old_directives.keys & new_directives.keys
|
125
|
+
intersection.each do |common_directive_name|
|
126
|
+
old_directive = old_schema.directives[common_directive_name]
|
127
|
+
new_directive = new_schema.directives[common_directive_name]
|
128
|
+
|
129
|
+
block.call(old_directive, new_directive)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
attr_reader :old_schema, :new_schema, :old_types, :new_types, :old_directives, :new_directives
|
114
134
|
end
|
115
135
|
end
|
116
136
|
end
|
@@ -4,8 +4,8 @@ module GraphQL
|
|
4
4
|
attr_reader :changes, :breaking_changes, :non_breaking_changes
|
5
5
|
|
6
6
|
def initialize(changes)
|
7
|
-
@changes = changes.sort_by { |c| [c.breaking ? 1 : 2, c.message] }
|
8
|
-
@breaking_changes, @non_breaking_changes = @changes.partition(&:breaking)
|
7
|
+
@changes = changes.sort_by { |c| [c.breaking? ? 1 : 2, c.message] }
|
8
|
+
@breaking_changes, @non_breaking_changes = @changes.partition(&:breaking?)
|
9
9
|
end
|
10
10
|
|
11
11
|
def identical?
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql-schema_comparator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Marc-Andre Giroux
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-10-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: graphql
|
@@ -118,8 +118,11 @@ files:
|
|
118
118
|
- lib/graphql/schema_comparator.rb
|
119
119
|
- lib/graphql/schema_comparator/changes.rb
|
120
120
|
- lib/graphql/schema_comparator/diff/argument.rb
|
121
|
+
- lib/graphql/schema_comparator/diff/directive.rb
|
122
|
+
- lib/graphql/schema_comparator/diff/directive_argument.rb
|
121
123
|
- lib/graphql/schema_comparator/diff/enum.rb
|
122
124
|
- lib/graphql/schema_comparator/diff/field.rb
|
125
|
+
- lib/graphql/schema_comparator/diff/has_directives.rb
|
123
126
|
- lib/graphql/schema_comparator/diff/input_field.rb
|
124
127
|
- lib/graphql/schema_comparator/diff/input_object.rb
|
125
128
|
- lib/graphql/schema_comparator/diff/interface.rb
|
@@ -149,7 +152,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
149
152
|
version: '0'
|
150
153
|
requirements: []
|
151
154
|
rubyforge_project:
|
152
|
-
rubygems_version: 2.6.
|
155
|
+
rubygems_version: 2.6.13
|
153
156
|
signing_key:
|
154
157
|
specification_version: 4
|
155
158
|
summary: Compare GraphQL schemas and get the changes that happened.
|