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
         |