diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8f29b7c..de16328 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,7 +1,8 @@ name: Tests on: - push + - push + - pull_request jobs: test: @@ -9,11 +10,8 @@ jobs: strategy: fail-fast: false matrix: - ruby: [2.3, 2.7, '3.0'] - graphql_version: ['1.9.19', '1.11.6'] - exclude: - - graphql_version: '1.9.19' - ruby: '3.0' + ruby: [2.4, 2.7, '3.0'] + graphql_version: ['~> 1.10.0', '~> 1.13'] steps: - uses: actions/checkout@v2 - uses: ruby/setup-ruby@v1 @@ -22,6 +20,4 @@ jobs: ruby-version: ${{ matrix.ruby }} env: GRAPHQL_VERSION: ${{ matrix.graphql_version }} - TESTING_LEGACY_DEFINITION_LAYER: ${{ startsWith(matrix.graphql_version, '1.9') }} - - run: bundle install - run: bundle exec rake diff --git a/.rubocop.yml b/.rubocop.yml index 34b2a82..8e0a2a5 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -5,6 +5,7 @@ inherit_from: - .rubocop_todo.yml AllCops: + SuggestExtensions: false TargetRubyVersion: 2.7 Exclude: - vendor/**/* diff --git a/README.md b/README.md index 0b9917f..e142787 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ end Use `GraphQL::Batch` as a plugin in your schema _after_ specifying the mutation so that `GraphQL::Batch` can extend the mutation fields to clear the cache after -they are resolved (for graphql >= `1.5.0`). +they are resolved. ```ruby class MySchema < GraphQL::Schema @@ -61,16 +61,6 @@ class MySchema < GraphQL::Schema end ``` -For pre `1.5.0` versions: - -```ruby -MySchema = GraphQL::Schema.define do - query MyQueryType - - GraphQL::Batch.use(self) -end -``` - #### Field Usage The loader class can be used from the resolver for a graphql field by calling `.for` with the grouping arguments to get a loader instance, then call `.load` on that instance with the key to load. @@ -155,19 +145,19 @@ end ## Unit Testing Your loaders can be tested outside of a GraphQL query by doing the -batch loads in a block passed to GraphQL::Batch.batch. That method +batch loads in a block passed to `GraphQL::Batch.batch`. That method will set up thread-local state to store the loaders, batch load any promise returned from the block then clear the thread-local state to avoid leaking state between tests. ```ruby - def test_single_query - product = products(:snowboard) - title = GraphQL::Batch.batch do - RecordLoader.for(Product).load(product.id).then(&:title) - end - assert_equal product.title, title +def test_single_query + product = products(:snowboard) + title = GraphQL::Batch.batch do + RecordLoader.for(Product).load(product.id).then(&:title) end + assert_equal product.title, title +end ``` ## Development diff --git a/graphql-batch.gemspec b/graphql-batch.gemspec index a716bf6..f4bc6b6 100644 --- a/graphql-batch.gemspec +++ b/graphql-batch.gemspec @@ -1,7 +1,4 @@ -# coding: utf-8 -lib = File.expand_path('../lib', __FILE__) -$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) -require 'graphql/batch/version' +require_relative 'lib/graphql/batch/version' Gem::Specification.new do |spec| spec.name = "graphql-batch" @@ -20,7 +17,7 @@ Gem::Specification.new do |spec| spec.metadata['allowed_push_host'] = "https://rubygems.org" - spec.add_runtime_dependency "graphql", ">= 1.3", "< 2" + spec.add_runtime_dependency "graphql", ">= 1.10", "< 2" spec.add_runtime_dependency "promise.rb", "~> 0.7.2" spec.add_development_dependency "byebug" if RUBY_ENGINE == 'ruby' diff --git a/lib/graphql/batch.rb b/lib/graphql/batch.rb index dca20fe..0979cc1 100644 --- a/lib/graphql/batch.rb +++ b/lib/graphql/batch.rb @@ -16,29 +16,17 @@ def self.batch(executor_class: GraphQL::Batch::Executor) end def self.use(schema_defn, executor_class: GraphQL::Batch::Executor) - # Support 1.10+ which passes the class instead of the definition proxy - schema = schema_defn.is_a?(Class) ? schema_defn : schema_defn.target - current_gem_version = Gem::Version.new(GraphQL::VERSION) - if current_gem_version >= Gem::Version.new("1.6.0") - instrumentation = GraphQL::Batch::SetupMultiplex.new(schema, executor_class: executor_class) - schema_defn.instrument(:multiplex, instrumentation) - if schema.mutation - if current_gem_version >= Gem::Version.new('1.9.0.pre3') && - (schema.mutation.is_a?(Class) || schema.mutation.metadata[:type_class]) - require_relative "batch/mutation_field_extension" - schema.mutation.fields.each do |name, f| - field = f.respond_to?(:type_class) ? f.type_class : f.metadata[:type_class] - field.extension(GraphQL::Batch::MutationFieldExtension) - end - else - schema_defn.instrument(:field, instrumentation) - end + instrumentation = GraphQL::Batch::SetupMultiplex.new(schema_defn, executor_class: executor_class) + schema_defn.instrument(:multiplex, instrumentation) + + if schema_defn.mutation + require_relative "batch/mutation_field_extension" + + schema_defn.mutation.fields.each do |name, field| + field.extension(GraphQL::Batch::MutationFieldExtension) end - else - instrumentation = GraphQL::Batch::Setup.new(schema, executor_class: executor_class) - schema_defn.instrument(:query, instrumentation) - schema_defn.instrument(:field, instrumentation) end + schema_defn.lazy_resolve(::Promise, :sync) end end @@ -47,5 +35,4 @@ def self.use(schema_defn, executor_class: GraphQL::Batch::Executor) require_relative "batch/version" require_relative "batch/loader" require_relative "batch/executor" -require_relative "batch/setup" require_relative "batch/setup_multiplex" diff --git a/lib/graphql/batch/loader.rb b/lib/graphql/batch/loader.rb index 0bc8ea2..646edf2 100644 --- a/lib/graphql/batch/loader.rb +++ b/lib/graphql/batch/loader.rb @@ -34,7 +34,7 @@ def current_executor unless executor raise GraphQL::Batch::NoExecutorError, 'Cannot create loader without'\ ' an Executor. Wrap the call to `for` with `GraphQL::Batch.batch`'\ - ' or use `GraphQL::Batch::Setup` as a query instrumenter if'\ + ' or use `GraphQL::Batch::SetupMultiplex` as a query instrumenter if'\ ' using with `graphql-ruby`' end diff --git a/lib/graphql/batch/setup.rb b/lib/graphql/batch/setup.rb deleted file mode 100644 index 3967586..0000000 --- a/lib/graphql/batch/setup.rb +++ /dev/null @@ -1,45 +0,0 @@ -module GraphQL::Batch - class Setup - class << self - def start_batching(executor_class) - GraphQL::Batch::Executor.start_batch(executor_class) - end - - def end_batching - GraphQL::Batch::Executor.end_batch - end - - def instrument_field(schema, type, field) - return field unless type == schema.mutation - old_resolve_proc = field.resolve_proc - field.redefine do - resolve(->(obj, args, ctx) { - GraphQL::Batch::Executor.current.clear - begin - ::Promise.sync(old_resolve_proc.call(obj, args, ctx)) - ensure - GraphQL::Batch::Executor.current.clear - end - }) - end - end - end - - def initialize(schema, executor_class:) - @schema = schema - @executor_class = executor_class - end - - def before_query(query) - Setup.start_batching(@executor_class) - end - - def after_query(query) - Setup.end_batching - end - - def instrument(type, field) - Setup.instrument_field(@schema, type, field) - end - end -end diff --git a/lib/graphql/batch/setup_multiplex.rb b/lib/graphql/batch/setup_multiplex.rb index 8ed05de..940d2a7 100644 --- a/lib/graphql/batch/setup_multiplex.rb +++ b/lib/graphql/batch/setup_multiplex.rb @@ -6,15 +6,11 @@ def initialize(schema, executor_class:) end def before_multiplex(multiplex) - Setup.start_batching(@executor_class) + GraphQL::Batch::Executor.start_batch(@executor_class) end def after_multiplex(multiplex) - Setup.end_batching - end - - def instrument(type, field) - Setup.instrument_field(@schema, type, field) + GraphQL::Batch::Executor.end_batch end end end diff --git a/test/support/schema.rb b/test/support/schema.rb index a0501e9..3e4e537 100644 --- a/test/support/schema.rb +++ b/test/support/schema.rb @@ -157,41 +157,17 @@ def resolve end end -if ENV["TESTING_LEGACY_DEFINITION_LAYER"] != "true" - class MutationType < GraphQL::Schema::Object - field :increment_counter, mutation: IncrementCounterMutation - field :counter_loader, mutation: CounterLoaderMutation - field :no_op, mutation: NoOpMutation - end -else - MutationType = GraphQL::ObjectType.define do - name "Mutation" - - field :incrementCounter, CounterType.to_non_null_type do - resolve ->(_, _, ctx) { ctx[:counter][0] += 1; CounterLoader.load(ctx[:counter]) } - end - - field :counterLoader, !types.Int do - resolve ->(_, _, ctx) { - CounterLoader.load(ctx[:counter]) - } - end - - field :noOp, QueryType.to_non_null_type do - resolve ->(_, _, ctx) { Hash.new } - end - end +class MutationType < GraphQL::Schema::Object + field :increment_counter, mutation: IncrementCounterMutation + field :counter_loader, mutation: CounterLoaderMutation + field :no_op, mutation: NoOpMutation end class Schema < GraphQL::Schema query QueryType mutation MutationType - if ENV["TESTING_LEGACY_DEFINITION_LAYER"] != "true" - use GraphQL::Execution::Interpreter - # This probably has no effect, but just to get the full test: - use GraphQL::Analysis::AST - end - + use GraphQL::Execution::Interpreter + use GraphQL::Analysis::AST use GraphQL::Batch end