module GraphQL::Execution::Instrumentation
Public Class Methods
This function implements the instrumentation policy:
-
Instrumenters are a stack; the first `before_query` will have the last `after_query`
-
If a `before_` hook returned without an error, its corresponding `after_` hook will run.
-
If the `before_` hook did not run, the `after_` hook will not be called.
When errors are raised from `after_` hooks:
- Subsequent `after_` hooks _are_ called - The first raised error is captured; later errors are ignored - If an error was capture, it's re-raised after all hooks are finished
Partial runs of instrumentation are possible:
-
If a `before_multiplex` hook raises an error, no `before_query` hooks will run
-
If a `before_query` hook raises an error, subsequent `before_query` hooks will not run (on any query)
# File lib/graphql/execution/instrumentation.rb, line 18 def self.apply_instrumenters(multiplex) schema = multiplex.schema queries = multiplex.queries query_instrumenters = schema.instrumenters[:query] multiplex_instrumenters = schema.instrumenters[:multiplex] # First, run multiplex instrumentation, then query instrumentation for each query call_hooks(multiplex_instrumenters, multiplex, :before_multiplex, :after_multiplex) do each_query_call_hooks(query_instrumenters, queries) do # Let them be executed yield end end end
Private Class Methods
Call each before hook, and if they all succeed, yield. If they don't all succeed, call after_ for each one that succeeded.
# File lib/graphql/execution/instrumentation.rb, line 53 def call_hooks(instrumenters, object, before_hook_name, after_hook_name, i = 0) if i >= instrumenters.length # We've reached the end of the instrumenters, so start exiting the call stack. # (This will eventually call the originally-passed block.) yield else # Get the next instrumenter and call the before_hook. instrumenter = instrumenters[i] instrumenter.public_send(before_hook_name, object) # At this point, the before_hook did _not_ raise an error. # (If it did raise an error, we wouldn't reach this point.) # So we should guarantee that we run the after_hook. begin # Call the next instrumenter on the list, # basically passing along the original block call_hooks(instrumenters, object, before_hook_name, after_hook_name, i + 1) { yield } ensure # Regardless of how the next instrumenter in the list behaves, # call the after_hook of this instrumenter instrumenter.public_send(after_hook_name, object) end end end
Call the before_ hooks of each query, Then yield if no errors. `call_hooks` takes care of appropriate cleanup.
# File lib/graphql/execution/instrumentation.rb, line 38 def each_query_call_hooks(instrumenters, queries, i = 0) if i >= queries.length yield else query = queries[i] call_hooks(instrumenters, query, :before_query, :after_query) { each_query_call_hooks(instrumenters, queries, i + 1) { yield } } end end