graphql 1.8.7 → 1.9.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/lib/generators/graphql/install_generator.rb +2 -1
- data/lib/generators/graphql/scalar_generator.rb +20 -0
- data/lib/generators/graphql/templates/base_scalar.erb +4 -0
- data/lib/generators/graphql/templates/scalar.erb +13 -0
- data/lib/graphql/analysis/ast/analyzer.rb +62 -0
- data/lib/graphql/analysis/ast/field_usage.rb +28 -0
- data/lib/graphql/analysis/ast/max_query_complexity.rb +23 -0
- data/lib/graphql/analysis/ast/max_query_depth.rb +18 -0
- data/lib/graphql/analysis/ast/query_complexity.rb +114 -0
- data/lib/graphql/analysis/ast/query_depth.rb +66 -0
- data/lib/graphql/analysis/ast/visitor.rb +255 -0
- data/lib/graphql/analysis/ast.rb +82 -0
- data/lib/graphql/analysis.rb +1 -0
- data/lib/graphql/argument.rb +5 -0
- data/lib/graphql/authorization.rb +1 -0
- data/lib/graphql/backwards_compatibility.rb +1 -1
- data/lib/graphql/base_type.rb +1 -1
- data/lib/graphql/compatibility/execution_specification/specification_schema.rb +1 -2
- data/lib/graphql/compatibility/schema_parser_specification.rb +2 -6
- data/lib/graphql/dig.rb +19 -0
- data/lib/graphql/directive/include_directive.rb +1 -7
- data/lib/graphql/directive/skip_directive.rb +1 -8
- data/lib/graphql/directive.rb +13 -1
- data/lib/graphql/enum_type.rb +8 -0
- data/lib/graphql/execution/execute.rb +36 -17
- data/lib/graphql/execution/instrumentation.rb +2 -0
- data/lib/graphql/execution/interpreter/execution_errors.rb +29 -0
- data/lib/graphql/execution/interpreter/hash_response.rb +46 -0
- data/lib/graphql/execution/interpreter/resolve.rb +58 -0
- data/lib/graphql/execution/interpreter/runtime.rb +597 -0
- data/lib/graphql/execution/interpreter.rb +99 -0
- data/lib/graphql/execution/lazy.rb +8 -1
- data/lib/graphql/execution/lookahead.rb +351 -0
- data/lib/graphql/execution/multiplex.rb +37 -29
- data/lib/graphql/execution.rb +2 -0
- data/lib/graphql/execution_error.rb +1 -1
- data/lib/graphql/field.rb +1 -7
- data/lib/graphql/integer_encoding_error.rb +12 -0
- data/lib/graphql/internal_representation/rewrite.rb +127 -142
- data/lib/graphql/introspection/dynamic_fields.rb +8 -2
- data/lib/graphql/introspection/entry_points.rb +11 -6
- data/lib/graphql/introspection/enum_value_type.rb +4 -0
- data/lib/graphql/introspection/schema_type.rb +7 -2
- data/lib/graphql/introspection/type_type.rb +9 -5
- data/lib/graphql/invalid_null_error.rb +1 -1
- data/lib/graphql/language/block_string.rb +37 -0
- data/lib/graphql/language/document_from_schema_definition.rb +10 -7
- data/lib/graphql/language/lexer.rb +55 -36
- data/lib/graphql/language/lexer.rl +8 -3
- data/lib/graphql/language/nodes.rb +440 -362
- data/lib/graphql/language/parser.rb +56 -56
- data/lib/graphql/language/parser.y +12 -12
- data/lib/graphql/language/printer.rb +2 -2
- data/lib/graphql/language/visitor.rb +158 -15
- data/lib/graphql/language.rb +0 -1
- data/lib/graphql/literal_validation_error.rb +6 -0
- data/lib/graphql/query/arguments.rb +3 -2
- data/lib/graphql/query/arguments_cache.rb +1 -1
- data/lib/graphql/query/context.rb +14 -5
- data/lib/graphql/query/executor.rb +1 -1
- data/lib/graphql/query/result.rb +1 -1
- data/lib/graphql/query/validation_pipeline.rb +35 -11
- data/lib/graphql/query/variable_validation_error.rb +10 -1
- data/lib/graphql/query.rb +16 -2
- data/lib/graphql/relay/base_connection.rb +2 -0
- data/lib/graphql/relay/connection_instrumentation.rb +3 -1
- data/lib/graphql/relay/connection_resolve.rb +1 -1
- data/lib/graphql/relay/node.rb +2 -28
- data/lib/graphql/relay/relation_connection.rb +1 -1
- data/lib/graphql/schema/argument.rb +13 -5
- data/lib/graphql/schema/base_64_encoder.rb +4 -4
- data/lib/graphql/schema/build_from_definition.rb +2 -4
- data/lib/graphql/schema/default_type_error.rb +1 -1
- data/lib/graphql/schema/directive/feature.rb +66 -0
- data/lib/graphql/schema/directive/include.rb +25 -0
- data/lib/graphql/schema/directive/skip.rb +25 -0
- data/lib/graphql/schema/directive/transform.rb +48 -0
- data/lib/graphql/schema/directive.rb +103 -0
- data/lib/graphql/schema/enum_value.rb +3 -2
- data/lib/graphql/schema/field/connection_extension.rb +50 -0
- data/lib/graphql/schema/field/scope_extension.rb +18 -0
- data/lib/graphql/schema/field.rb +273 -64
- data/lib/graphql/schema/field_extension.rb +69 -0
- data/lib/graphql/schema/input_object.rb +16 -8
- data/lib/graphql/schema/interface.rb +1 -0
- data/lib/graphql/schema/loader.rb +22 -16
- data/lib/graphql/schema/member/base_dsl_methods.rb +8 -2
- data/lib/graphql/schema/member/build_type.rb +33 -1
- data/lib/graphql/schema/member/has_arguments.rb +6 -2
- data/lib/graphql/schema/member/has_fields.rb +18 -70
- data/lib/graphql/schema/member/has_path.rb +25 -0
- data/lib/graphql/schema/member/instrumentation.rb +10 -7
- data/lib/graphql/schema/member.rb +2 -0
- data/lib/graphql/schema/mutation.rb +6 -48
- data/lib/graphql/schema/non_null.rb +5 -1
- data/lib/graphql/schema/object.rb +18 -4
- data/lib/graphql/schema/printer.rb +1 -1
- data/lib/graphql/schema/relay_classic_mutation.rb +42 -9
- data/lib/graphql/schema/resolver/has_payload_type.rb +65 -0
- data/lib/graphql/schema/resolver.rb +45 -20
- data/lib/graphql/schema/subscription.rb +97 -0
- data/lib/graphql/schema/traversal.rb +11 -7
- data/lib/graphql/schema.rb +186 -38
- data/lib/graphql/static_validation/all_rules.rb +3 -2
- data/lib/graphql/static_validation/base_visitor.rb +199 -0
- data/lib/graphql/static_validation/default_visitor.rb +15 -0
- data/lib/graphql/static_validation/definition_dependencies.rb +62 -68
- data/lib/graphql/static_validation/{message.rb → error.rb} +11 -11
- data/lib/graphql/static_validation/interpreter_visitor.rb +14 -0
- data/lib/graphql/static_validation/literal_validator.rb +54 -11
- data/lib/graphql/static_validation/no_validate_visitor.rb +10 -0
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +87 -16
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible_error.rb +31 -0
- data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +11 -11
- data/lib/graphql/static_validation/rules/argument_names_are_unique_error.rb +30 -0
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +52 -8
- data/lib/graphql/static_validation/rules/arguments_are_defined_error.rb +35 -0
- data/lib/graphql/static_validation/rules/directives_are_defined.rb +12 -15
- data/lib/graphql/static_validation/rules/directives_are_defined_error.rb +29 -0
- data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +19 -14
- data/lib/graphql/static_validation/rules/directives_are_in_valid_locations_error.rb +31 -0
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +17 -19
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type_error.rb +32 -0
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +30 -14
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections_error.rb +31 -0
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +356 -29
- data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +32 -0
- data/lib/graphql/static_validation/rules/fragment_names_are_unique.rb +20 -13
- data/lib/graphql/static_validation/rules/fragment_names_are_unique_error.rb +29 -0
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +37 -29
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible_error.rb +35 -0
- data/lib/graphql/static_validation/rules/fragment_types_exist.rb +25 -17
- data/lib/graphql/static_validation/rules/fragment_types_exist_error.rb +29 -0
- data/lib/graphql/static_validation/rules/fragments_are_finite.rb +12 -10
- data/lib/graphql/static_validation/rules/fragments_are_finite_error.rb +29 -0
- data/lib/graphql/static_validation/rules/fragments_are_named.rb +7 -11
- data/lib/graphql/static_validation/rules/fragments_are_named_error.rb +26 -0
- data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +16 -16
- data/lib/graphql/static_validation/rules/fragments_are_on_composite_types_error.rb +30 -0
- data/lib/graphql/static_validation/rules/fragments_are_used.rb +21 -14
- data/lib/graphql/static_validation/rules/fragments_are_used_error.rb +29 -0
- data/lib/graphql/static_validation/rules/mutation_root_exists.rb +10 -14
- data/lib/graphql/static_validation/rules/mutation_root_exists_error.rb +26 -0
- data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +30 -30
- data/lib/graphql/static_validation/rules/no_definitions_are_present_error.rb +25 -0
- data/lib/graphql/static_validation/rules/operation_names_are_valid.rb +25 -17
- data/lib/graphql/static_validation/rules/operation_names_are_valid_error.rb +28 -0
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +17 -18
- data/lib/graphql/static_validation/rules/required_arguments_are_present_error.rb +35 -0
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +47 -0
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present_error.rb +35 -0
- data/lib/graphql/static_validation/rules/subscription_root_exists.rb +10 -14
- data/lib/graphql/static_validation/rules/subscription_root_exists_error.rb +26 -0
- data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +28 -17
- data/lib/graphql/static_validation/rules/unique_directives_per_location_error.rb +29 -0
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +35 -27
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed_error.rb +39 -0
- data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +15 -14
- data/lib/graphql/static_validation/rules/variable_names_are_unique_error.rb +29 -0
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +41 -30
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed_error.rb +38 -0
- data/lib/graphql/static_validation/rules/variables_are_input_types.rb +18 -14
- data/lib/graphql/static_validation/rules/variables_are_input_types_error.rb +32 -0
- data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +73 -65
- data/lib/graphql/static_validation/rules/variables_are_used_and_defined_error.rb +37 -0
- data/lib/graphql/static_validation/validation_context.rb +8 -51
- data/lib/graphql/static_validation/validator.rb +23 -15
- data/lib/graphql/static_validation.rb +5 -3
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +21 -2
- data/lib/graphql/subscriptions/event.rb +28 -5
- data/lib/graphql/subscriptions/subscription_root.rb +66 -0
- data/lib/graphql/subscriptions.rb +16 -2
- data/lib/graphql/tracing/active_support_notifications_tracing.rb +0 -1
- data/lib/graphql/tracing/appsignal_tracing.rb +1 -1
- data/lib/graphql/tracing/data_dog_tracing.rb +1 -1
- data/lib/graphql/tracing/new_relic_tracing.rb +3 -3
- data/lib/graphql/tracing/platform_tracing.rb +17 -1
- data/lib/graphql/tracing/prometheus_tracing.rb +1 -1
- data/lib/graphql/tracing/scout_tracing.rb +1 -1
- data/lib/graphql/tracing/skylight_tracing.rb +3 -3
- data/lib/graphql/tracing.rb +8 -8
- data/lib/graphql/types/float.rb +1 -1
- data/lib/graphql/types/int.rb +11 -2
- data/lib/graphql/types/iso_8601_date_time.rb +15 -1
- data/lib/graphql/types/relay/base_connection.rb +15 -1
- data/lib/graphql/types/relay/node.rb +0 -1
- data/lib/graphql/types/relay/node_field.rb +43 -0
- data/lib/graphql/types/relay/nodes_field.rb +45 -0
- data/lib/graphql/types/relay.rb +2 -0
- data/lib/graphql/unauthorized_error.rb +4 -0
- data/lib/graphql/unauthorized_field_error.rb +23 -0
- data/lib/graphql/upgrader/member.rb +5 -0
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +6 -1
- data/readme.md +7 -7
- data/spec/dummy/Gemfile +1 -1
- data/spec/dummy/Gemfile.lock +157 -0
- data/spec/dummy/app/channels/graphql_channel.rb +22 -11
- data/spec/dummy/config/locales/en.yml +1 -1
- data/spec/dummy/log/test.log +199 -0
- data/spec/dummy/test/test_helper.rb +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/4w/4wzXRZrAkwKdgYaSE0pid5eB-fer8vSfSku_NPg4rMA.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/7I/7IHVBiJT06QSpgLpLoJIxboQ0B-D_tMTxsvoezBTV3Q.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/8w/8wY_SKagj8wHuwGNAAf6JnQ8joMbC6cEYpHrTAI8Urc.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/AK/AKzz1u6bGb4auXcrObA_g5LL-oV0ejNGa448AgAi_WQ.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/ET/ETW4uxvaYpruL8y6_ZptUH82ZowMaHIqvg5WexBFdEM.cache +3 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/F1/F1TWpjjyA56k9Z90n5B3xRn7DUdGjX73QCkYC6k07JQ.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/F8/F8MUNRzORGFgr329fNM0xLaoWCXdv3BIalT7dsvLfjs.cache +2 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/KB/KB07ZaKNC5uXJ7TjLi-WqnY6g7dq8wWp_8N3HNjBNxg.cache +2 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Ms/MsKSimH_UCB-H1tLvDABDHuvGciuoW6kVqQWDrXU5FQ.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Mt/Mtci-Kim50aPOmeClD4AIicKn1d1WJ0n454IjSd94sk.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/QH/QHt3Tc1Y6M66Oo_pDuMyWrQNs4Pp3SMeZR5K1wJj2Ts.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/XU/XU4k1OXnfMils5SrirorPvDSyDSqiOWLZNtmAH1HH8k.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/ZI/ZIof7mZxWWCnraIFOCuV6a8QRWzKJXJnx2Xd7C0ZyX0.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/cG/cGc_puuPS5pZKgUcy1Y_i1L6jl5UtsiIrMH59rTzR6c.cache +3 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/df/dfro_B6bx3KP1Go-7jEOqqZ2j4hVRseXIc3es9PKQno.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/jO/jO1DfbqnG0mTULsjJJANc3fefrG2zt7DIMmcptMT628.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/pE/pE7gO6pQ-z187Swb4hT554wmqsq-cNzgPWLrCz-LQQQ.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/r9/r9iU1l58a6rxkZSW5RSC52_tD-_UQuHxoMVnkfJ7Mhs.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/xi/xitPPFfPIyDMpaznV0sBBcw8eSCV8PJcLLWin78sCgE.cache +0 -0
- data/spec/dummy/tmp/screenshots/failures_test_it_handles_subscriptions.png +0 -0
- data/spec/graphql/analysis/analyze_query_spec.rb +1 -1
- data/spec/graphql/analysis/ast/field_usage_spec.rb +51 -0
- data/spec/graphql/analysis/ast/max_query_complexity_spec.rb +120 -0
- data/spec/graphql/analysis/ast/max_query_depth_spec.rb +114 -0
- data/spec/graphql/analysis/ast/query_complexity_spec.rb +299 -0
- data/spec/graphql/analysis/ast/query_depth_spec.rb +108 -0
- data/spec/graphql/analysis/ast_spec.rb +269 -0
- data/spec/graphql/authorization_spec.rb +120 -23
- data/spec/graphql/base_type_spec.rb +6 -4
- data/spec/graphql/enum_type_spec.rb +6 -1
- data/spec/graphql/execution/execute_spec.rb +9 -9
- data/spec/graphql/execution/instrumentation_spec.rb +19 -0
- data/spec/graphql/execution/interpreter_spec.rb +485 -0
- data/spec/graphql/execution/lazy_spec.rb +67 -1
- data/spec/graphql/execution/lookahead_spec.rb +363 -0
- data/spec/graphql/execution/multiplex_spec.rb +31 -3
- data/spec/graphql/execution/typecast_spec.rb +20 -20
- data/spec/graphql/execution_error_spec.rb +110 -96
- data/spec/graphql/field_spec.rb +1 -1
- data/spec/graphql/input_object_type_spec.rb +13 -352
- data/spec/graphql/int_type_spec.rb +19 -0
- data/spec/graphql/interface_type_spec.rb +4 -4
- data/spec/graphql/internal_representation/rewrite_spec.rb +2 -0
- data/spec/graphql/introspection/input_value_type_spec.rb +1 -1
- data/spec/graphql/introspection/type_type_spec.rb +1 -2
- data/spec/graphql/language/document_from_schema_definition_spec.rb +2 -2
- data/spec/graphql/language/lexer_spec.rb +72 -3
- data/spec/graphql/language/nodes_spec.rb +20 -0
- data/spec/graphql/language/printer_spec.rb +18 -6
- data/spec/graphql/language/visitor_spec.rb +320 -14
- data/spec/graphql/non_null_type_spec.rb +1 -1
- data/spec/graphql/object_type_spec.rb +32 -27
- data/spec/graphql/query/arguments_spec.rb +21 -0
- data/spec/graphql/query/context_spec.rb +28 -0
- data/spec/graphql/query/executor_spec.rb +40 -36
- data/spec/graphql/query_spec.rb +12 -6
- data/spec/graphql/schema/argument_spec.rb +35 -1
- data/spec/graphql/schema/build_from_definition_spec.rb +144 -29
- data/spec/graphql/schema/catchall_middleware_spec.rb +16 -15
- data/spec/graphql/schema/directive/feature_spec.rb +81 -0
- data/spec/graphql/schema/directive/transform_spec.rb +39 -0
- data/spec/graphql/schema/enum_spec.rb +12 -3
- data/spec/graphql/schema/enum_value_spec.rb +11 -0
- data/spec/graphql/schema/field_extension_spec.rb +115 -0
- data/spec/graphql/schema/field_spec.rb +47 -7
- data/spec/graphql/schema/input_object_spec.rb +95 -0
- data/spec/graphql/schema/instrumentation_spec.rb +3 -0
- data/spec/graphql/schema/interface_spec.rb +8 -2
- data/spec/graphql/schema/introspection_system_spec.rb +9 -1
- data/spec/graphql/schema/loader_spec.rb +5 -0
- data/spec/graphql/schema/member/accepts_definition_spec.rb +4 -0
- data/spec/graphql/schema/member/build_type_spec.rb +46 -0
- data/spec/graphql/schema/member/scoped_spec.rb +19 -3
- data/spec/graphql/schema/mutation_spec.rb +5 -3
- data/spec/graphql/schema/object_spec.rb +9 -1
- data/spec/graphql/schema/printer_spec.rb +255 -93
- data/spec/graphql/schema/relay_classic_mutation_spec.rb +133 -0
- data/spec/graphql/schema/resolver_spec.rb +173 -9
- data/spec/graphql/schema/scalar_spec.rb +6 -0
- data/spec/graphql/schema/subscription_spec.rb +416 -0
- data/spec/graphql/schema/traversal_spec.rb +10 -10
- data/spec/graphql/schema/type_expression_spec.rb +2 -2
- data/spec/graphql/schema/union_spec.rb +7 -0
- data/spec/graphql/schema/validation_spec.rb +1 -1
- data/spec/graphql/schema/warden_spec.rb +145 -88
- data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +213 -73
- data/spec/graphql/static_validation/rules/argument_names_are_unique_spec.rb +2 -2
- data/spec/graphql/static_validation/rules/arguments_are_defined_spec.rb +72 -29
- data/spec/graphql/static_validation/rules/directives_are_defined_spec.rb +4 -2
- data/spec/graphql/static_validation/rules/directives_are_in_valid_locations_spec.rb +4 -2
- data/spec/graphql/static_validation/rules/fields_are_defined_on_type_spec.rb +10 -5
- data/spec/graphql/static_validation/rules/fields_have_appropriate_selections_spec.rb +10 -5
- data/spec/graphql/static_validation/rules/fields_will_merge_spec.rb +131 -5
- data/spec/graphql/static_validation/rules/fragment_names_are_unique_spec.rb +2 -1
- data/spec/graphql/static_validation/rules/fragment_spreads_are_possible_spec.rb +6 -3
- data/spec/graphql/static_validation/rules/fragment_types_exist_spec.rb +4 -2
- data/spec/graphql/static_validation/rules/fragments_are_finite_spec.rb +4 -2
- data/spec/graphql/static_validation/rules/fragments_are_named_spec.rb +2 -1
- data/spec/graphql/static_validation/rules/fragments_are_on_composite_types_spec.rb +6 -3
- data/spec/graphql/static_validation/rules/fragments_are_used_spec.rb +22 -2
- data/spec/graphql/static_validation/rules/mutation_root_exists_spec.rb +2 -1
- data/spec/graphql/static_validation/rules/operation_names_are_valid_spec.rb +6 -3
- data/spec/graphql/static_validation/rules/required_arguments_are_present_spec.rb +13 -4
- data/spec/graphql/static_validation/rules/required_input_object_attributes_are_present_spec.rb +58 -0
- data/spec/graphql/static_validation/rules/subscription_root_exists_spec.rb +2 -1
- data/spec/graphql/static_validation/rules/unique_directives_per_location_spec.rb +14 -7
- data/spec/graphql/static_validation/rules/variable_default_values_are_correctly_typed_spec.rb +14 -7
- data/spec/graphql/static_validation/rules/variable_usages_are_allowed_spec.rb +8 -4
- data/spec/graphql/static_validation/rules/variables_are_input_types_spec.rb +8 -4
- data/spec/graphql/static_validation/rules/variables_are_used_and_defined_spec.rb +23 -3
- data/spec/graphql/static_validation/type_stack_spec.rb +10 -19
- data/spec/graphql/static_validation/validator_spec.rb +50 -2
- data/spec/graphql/subscriptions_spec.rb +27 -16
- data/spec/graphql/tracing/new_relic_tracing_spec.rb +16 -0
- data/spec/graphql/tracing/platform_tracing_spec.rb +59 -37
- data/spec/graphql/tracing/prometheus_tracing_spec.rb +3 -0
- data/spec/graphql/tracing/skylight_tracing_spec.rb +16 -0
- data/spec/graphql/types/iso_8601_date_time_spec.rb +29 -2
- data/spec/graphql/union_type_spec.rb +2 -2
- data/spec/graphql/upgrader/member_spec.rb +67 -0
- data/spec/{graphql → integration/mongoid/graphql}/relay/mongo_relation_connection_spec.rb +11 -22
- data/spec/integration/mongoid/spec_helper.rb +2 -0
- data/spec/{support → integration/mongoid}/star_trek/data.rb +0 -0
- data/spec/{support → integration/mongoid}/star_trek/schema.rb +56 -34
- data/spec/{support/star_wars → integration/rails}/data.rb +1 -0
- data/spec/{support → integration/rails/generators}/base_generator_test.rb +0 -0
- data/spec/{generators → integration/rails/generators}/graphql/enum_generator_spec.rb +0 -0
- data/spec/{generators → integration/rails/generators}/graphql/install_generator_spec.rb +1 -1
- data/spec/{generators → integration/rails/generators}/graphql/interface_generator_spec.rb +0 -0
- data/spec/{generators → integration/rails/generators}/graphql/loader_generator_spec.rb +0 -0
- data/spec/{generators → integration/rails/generators}/graphql/mutation_generator_spec.rb +0 -0
- data/spec/{generators → integration/rails/generators}/graphql/object_generator_spec.rb +0 -0
- data/spec/integration/rails/generators/graphql/scalar_generator_spec.rb +28 -0
- data/spec/{generators → integration/rails/generators}/graphql/union_generator_spec.rb +0 -0
- data/spec/integration/rails/graphql/input_object_type_spec.rb +364 -0
- data/spec/{graphql → integration/rails/graphql}/query/variables_spec.rb +7 -7
- data/spec/{graphql → integration/rails/graphql}/relay/array_connection_spec.rb +9 -9
- data/spec/{graphql → integration/rails/graphql}/relay/base_connection_spec.rb +11 -3
- data/spec/{graphql → integration/rails/graphql}/relay/connection_instrumentation_spec.rb +19 -22
- data/spec/{graphql → integration/rails/graphql}/relay/connection_resolve_spec.rb +16 -0
- data/spec/{graphql → integration/rails/graphql}/relay/connection_type_spec.rb +0 -0
- data/spec/{graphql → integration/rails/graphql}/relay/edge_spec.rb +0 -0
- data/spec/{graphql → integration/rails/graphql}/relay/mutation_spec.rb +48 -0
- data/spec/{graphql → integration/rails/graphql}/relay/node_spec.rb +0 -0
- data/spec/{graphql → integration/rails/graphql}/relay/page_info_spec.rb +22 -22
- data/spec/{graphql → integration/rails/graphql}/relay/range_add_spec.rb +4 -4
- data/spec/{graphql → integration/rails/graphql}/relay/relation_connection_spec.rb +56 -27
- data/spec/{graphql → integration/rails/graphql}/schema_spec.rb +15 -11
- data/spec/{graphql → integration/rails/graphql}/tracing/active_support_notifications_tracing_spec.rb +16 -9
- data/spec/integration/rails/spec_helper.rb +25 -0
- data/spec/integration/tmp/app/graphql/types/family_type.rb +9 -0
- data/spec/spec_helper.rb +23 -39
- data/spec/support/dummy/data.rb +20 -17
- data/spec/support/dummy/schema.rb +315 -305
- data/spec/support/error_bubbling_helpers.rb +23 -0
- data/spec/support/jazz.rb +213 -46
- data/spec/support/lazy_helpers.rb +69 -27
- data/spec/support/new_relic.rb +3 -0
- data/spec/support/skylight.rb +3 -0
- data/spec/support/star_wars/schema.rb +131 -81
- data/spec/support/static_validation_helpers.rb +9 -5
- metadata +418 -261
- data/lib/graphql/language/comments.rb +0 -45
- data/lib/graphql/static_validation/arguments_validator.rb +0 -50
- data/spec/graphql/schema/member/has_fields_spec.rb +0 -129
- data/spec/rails_dependency_sanity_spec.rb +0 -14
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require "graphql/execution/lazy/lazy_method_map"
|
3
3
|
require "graphql/execution/lazy/resolve"
|
4
|
+
|
4
5
|
module GraphQL
|
5
6
|
module Execution
|
6
7
|
# This wraps a value which is available, but not yet calculated, like a promise or future.
|
@@ -19,11 +20,17 @@ module GraphQL
|
|
19
20
|
Resolve.resolve(val)
|
20
21
|
end
|
21
22
|
|
23
|
+
attr_reader :path, :field
|
24
|
+
|
22
25
|
# Create a {Lazy} which will get its inner value by calling the block
|
26
|
+
# @param path [Array<String, Integer>]
|
27
|
+
# @param field [GraphQL::Schema::Field]
|
23
28
|
# @param get_value_func [Proc] a block to get the inner value (later)
|
24
|
-
def initialize(&get_value_func)
|
29
|
+
def initialize(path: nil, field: nil, &get_value_func)
|
25
30
|
@get_value_func = get_value_func
|
26
31
|
@resolved = false
|
32
|
+
@path = path
|
33
|
+
@field = field
|
27
34
|
end
|
28
35
|
|
29
36
|
# @return [Object] The wrapped value, calling the lazy block if necessary
|
@@ -0,0 +1,351 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
module Execution
|
4
|
+
# Lookahead creates a uniform interface to inspect the forthcoming selections.
|
5
|
+
#
|
6
|
+
# It assumes that the AST it's working with is valid. (So, it's safe to use
|
7
|
+
# during execution, but if you're using it directly, be sure to validate first.)
|
8
|
+
#
|
9
|
+
# A field may get access to its lookahead by adding `extras: [:lookahead]`
|
10
|
+
# to its configuration.
|
11
|
+
#
|
12
|
+
# __NOTE__: Lookahead for typed fragments (eg `node { ... on Thing { ... } }`)
|
13
|
+
# hasn't been implemented yet. It's possible, I just didn't need it yet.
|
14
|
+
# Feel free to open a PR or an issue if you want to add it.
|
15
|
+
#
|
16
|
+
# @example looking ahead in a field
|
17
|
+
# field :articles, [Types::Article], null: false,
|
18
|
+
# extras: [:lookahead]
|
19
|
+
#
|
20
|
+
# # For example, imagine a faster database call
|
21
|
+
# # may be issued when only some fields are requested.
|
22
|
+
# #
|
23
|
+
# # Imagine that _full_ fetch must be made to satisfy `fullContent`,
|
24
|
+
# # we can look ahead to see if we need that field. If we do,
|
25
|
+
# # we make the expensive database call instead of the cheap one.
|
26
|
+
# def articles(lookahead:)
|
27
|
+
# if lookahead.selects?(:full_content)
|
28
|
+
# fetch_full_articles(object)
|
29
|
+
# else
|
30
|
+
# fetch_preview_articles(object)
|
31
|
+
# end
|
32
|
+
# end
|
33
|
+
class Lookahead
|
34
|
+
# @param query [GraphQL::Query]
|
35
|
+
# @param ast_nodes [Array<GraphQL::Language::Nodes::Field>, Array<GraphQL::Language::Nodes::OperationDefinition>]
|
36
|
+
# @param field [GraphQL::Schema::Field] if `ast_nodes` are fields, this is the field definition matching those nodes
|
37
|
+
# @param root_type [Class] if `ast_nodes` are operation definition, this is the root type for that operation
|
38
|
+
def initialize(query:, ast_nodes:, field: nil, root_type: nil)
|
39
|
+
@ast_nodes = ast_nodes.freeze
|
40
|
+
@field = field
|
41
|
+
@root_type = root_type
|
42
|
+
@query = query
|
43
|
+
@selected_type = @field ? @field.type.unwrap : root_type
|
44
|
+
end
|
45
|
+
|
46
|
+
# @return [Array<GraphQL::Language::Nodes::Field>]
|
47
|
+
attr_reader :ast_nodes
|
48
|
+
|
49
|
+
# @return [GraphQL::Schema::Field]
|
50
|
+
attr_reader :field
|
51
|
+
|
52
|
+
# True if this node has a selection on `field_name`.
|
53
|
+
# If `field_name` is a String, it is treated as a GraphQL-style (camelized)
|
54
|
+
# field name and used verbatim. If `field_name` is a Symbol, it is
|
55
|
+
# treated as a Ruby-style (underscored) name and camelized before comparing.
|
56
|
+
#
|
57
|
+
# If `arguments:` is provided, each provided key/value will be matched
|
58
|
+
# against the arguments in the next selection. This method will return false
|
59
|
+
# if any of the given `arguments:` are not present and matching in the next selection.
|
60
|
+
# (But, the next selection may contain _more_ than the given arguments.)
|
61
|
+
# @param field_name [String, Symbol]
|
62
|
+
# @param arguments [Hash] Arguments which must match in the selection
|
63
|
+
# @return [Boolean]
|
64
|
+
def selects?(field_name, arguments: nil)
|
65
|
+
selection(field_name, arguments: arguments).selected?
|
66
|
+
end
|
67
|
+
|
68
|
+
# @return [Boolean] True if this lookahead represents a field that was requested
|
69
|
+
def selected?
|
70
|
+
true
|
71
|
+
end
|
72
|
+
|
73
|
+
# Like {#selects?}, but can be used for chaining.
|
74
|
+
# It returns a null object (check with {#selected?})
|
75
|
+
# @return [GraphQL::Execution::Lookahead]
|
76
|
+
def selection(field_name, selected_type: @selected_type, arguments: nil)
|
77
|
+
next_field_name = normalize_name(field_name)
|
78
|
+
|
79
|
+
next_field_defn = FieldHelpers.get_field(@query.schema, selected_type, next_field_name)
|
80
|
+
if next_field_defn
|
81
|
+
next_nodes = []
|
82
|
+
@ast_nodes.each do |ast_node|
|
83
|
+
ast_node.selections.each do |selection|
|
84
|
+
find_selected_nodes(selection, next_field_name, next_field_defn, arguments: arguments, matches: next_nodes)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
if next_nodes.any?
|
89
|
+
Lookahead.new(query: @query, ast_nodes: next_nodes, field: next_field_defn)
|
90
|
+
else
|
91
|
+
NULL_LOOKAHEAD
|
92
|
+
end
|
93
|
+
else
|
94
|
+
NULL_LOOKAHEAD
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Like {#selection}, but for all nodes.
|
99
|
+
# It returns a list of Lookaheads for all Selections
|
100
|
+
#
|
101
|
+
# If `arguments:` is provided, each provided key/value will be matched
|
102
|
+
# against the arguments in each selection. This method will filter the selections
|
103
|
+
# if any of the given `arguments:` do not match the given selection.
|
104
|
+
#
|
105
|
+
# @example getting the name of a selection
|
106
|
+
# def articles(lookahead:)
|
107
|
+
# next_lookaheads = lookahead.selections # => [#<GraphQL::Execution::Lookahead ...>, ...]
|
108
|
+
# next_lookaheads.map(&:name) #=> [:full_content, :title]
|
109
|
+
# end
|
110
|
+
#
|
111
|
+
# @param arguments [Hash] Arguments which must match in the selection
|
112
|
+
# @return [Array<GraphQL::Execution::Lookahead>]
|
113
|
+
def selections(arguments: nil)
|
114
|
+
subselections_by_name = {}
|
115
|
+
@ast_nodes.each do |node|
|
116
|
+
find_selections(subselections_by_name, @selected_type, node.selections, arguments)
|
117
|
+
end
|
118
|
+
|
119
|
+
# Items may be filtered out if `arguments` doesn't match
|
120
|
+
subselections_by_name.values.select(&:selected?)
|
121
|
+
end
|
122
|
+
|
123
|
+
# The method name of the field.
|
124
|
+
# It returns the method_sym of the Lookahead's field.
|
125
|
+
#
|
126
|
+
# @example getting the name of a selection
|
127
|
+
# def articles(lookahead:)
|
128
|
+
# article.selection(:full_content).name # => :full_content
|
129
|
+
# # ...
|
130
|
+
# end
|
131
|
+
#
|
132
|
+
# @return [Symbol]
|
133
|
+
def name
|
134
|
+
@field && @field.original_name
|
135
|
+
end
|
136
|
+
|
137
|
+
def inspect
|
138
|
+
"#<GraphQL::Execution::Lookahead #{@field ? "@field=#{@field.path.inspect}": "@root_type=#{@root_type}"} @ast_nodes.size=#{@ast_nodes.size}>"
|
139
|
+
end
|
140
|
+
|
141
|
+
# This is returned for {Lookahead#selection} when a non-existent field is passed
|
142
|
+
class NullLookahead < Lookahead
|
143
|
+
# No inputs required here.
|
144
|
+
def initialize
|
145
|
+
end
|
146
|
+
|
147
|
+
def selected?
|
148
|
+
false
|
149
|
+
end
|
150
|
+
|
151
|
+
def selects?(*)
|
152
|
+
false
|
153
|
+
end
|
154
|
+
|
155
|
+
def selection(*)
|
156
|
+
NULL_LOOKAHEAD
|
157
|
+
end
|
158
|
+
|
159
|
+
def selections(*)
|
160
|
+
[]
|
161
|
+
end
|
162
|
+
|
163
|
+
def inspect
|
164
|
+
"#<GraphQL::Execution::Lookahead::NullLookahead>"
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# A singleton, so that misses don't come with overhead.
|
169
|
+
NULL_LOOKAHEAD = NullLookahead.new
|
170
|
+
|
171
|
+
private
|
172
|
+
|
173
|
+
# If it's a symbol, stringify and camelize it
|
174
|
+
def normalize_name(name)
|
175
|
+
if name.is_a?(Symbol)
|
176
|
+
Schema::Member::BuildType.camelize(name.to_s)
|
177
|
+
else
|
178
|
+
name
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def normalize_keyword(keyword)
|
183
|
+
if keyword.is_a?(String)
|
184
|
+
Schema::Member::BuildType.underscore(keyword).to_sym
|
185
|
+
else
|
186
|
+
keyword
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def find_selections(subselections_by_name, selected_type, ast_selections, arguments)
|
191
|
+
ast_selections.each do |ast_selection|
|
192
|
+
case ast_selection
|
193
|
+
when GraphQL::Language::Nodes::Field
|
194
|
+
subselections_by_name[ast_selection.name] ||= selection(ast_selection.name, selected_type: selected_type, arguments: arguments)
|
195
|
+
when GraphQL::Language::Nodes::InlineFragment
|
196
|
+
if (t = ast_selection.type)
|
197
|
+
# Assuming this is valid, that `t` will be found.
|
198
|
+
selected_type = @query.schema.types[t.name].metadata[:type_class]
|
199
|
+
end
|
200
|
+
find_selections(subselections_by_name, selected_type, ast_selection.selections, arguments)
|
201
|
+
when GraphQL::Language::Nodes::FragmentSpread
|
202
|
+
frag_defn = @query.fragments[ast_selection.name] || raise("Invariant: Can't look ahead to nonexistent fragment #{ast_selection.name} (found: #{@query.fragments.keys})")
|
203
|
+
# Again, assuming a valid AST
|
204
|
+
selected_type = @query.schema.types[frag_defn.type.name].metadata[:type_class]
|
205
|
+
find_selections(subselections_by_name, selected_type, frag_defn.selections, arguments)
|
206
|
+
else
|
207
|
+
raise "Invariant: Unexpected selection type: #{ast_selection.class}"
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
# If a selection on `node` matches `field_name` (which is backed by `field_defn`)
|
213
|
+
# and matches the `arguments:` constraints, then add that node to `matches`
|
214
|
+
def find_selected_nodes(node, field_name, field_defn, arguments:, matches:)
|
215
|
+
case node
|
216
|
+
when GraphQL::Language::Nodes::Field
|
217
|
+
if node.name == field_name
|
218
|
+
if arguments.nil? || arguments.empty?
|
219
|
+
# No constraint applied
|
220
|
+
matches << node
|
221
|
+
else
|
222
|
+
query_kwargs = ArgumentHelpers.arguments(@query, nil, field_defn, node)
|
223
|
+
passes_args = arguments.all? do |arg_name, arg_value|
|
224
|
+
arg_name = normalize_keyword(arg_name)
|
225
|
+
# Make sure the constraint is present with a matching value
|
226
|
+
query_kwargs.key?(arg_name) && query_kwargs[arg_name] == arg_value
|
227
|
+
end
|
228
|
+
if passes_args
|
229
|
+
matches << node
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
when GraphQL::Language::Nodes::InlineFragment
|
234
|
+
node.selections.each { |s| find_selected_nodes(s, field_name, field_defn, arguments: arguments, matches: matches) }
|
235
|
+
when GraphQL::Language::Nodes::FragmentSpread
|
236
|
+
frag_defn = @query.fragments[node.name] || raise("Invariant: Can't look ahead to nonexistent fragment #{node.name} (found: #{@query.fragments.keys})")
|
237
|
+
frag_defn.selections.each { |s| find_selected_nodes(s, field_name, field_defn, arguments: arguments, matches: matches) }
|
238
|
+
else
|
239
|
+
raise "Unexpected selection comparison on #{node.class.name} (#{node})"
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
# TODO Dedup with interpreter
|
244
|
+
module ArgumentHelpers
|
245
|
+
module_function
|
246
|
+
|
247
|
+
def arguments(query, graphql_object, arg_owner, ast_node)
|
248
|
+
kwarg_arguments = {}
|
249
|
+
arg_defns = arg_owner.arguments
|
250
|
+
ast_node.arguments.each do |arg|
|
251
|
+
arg_defn = arg_defns[arg.name] || raise("Invariant: missing argument definition for #{arg.name.inspect} in #{arg_defns.keys} from #{arg_owner}")
|
252
|
+
# Need to distinguish between client-provided `nil`
|
253
|
+
# and nothing-at-all
|
254
|
+
is_present, value = arg_to_value(query, graphql_object, arg_defn.type, arg.value)
|
255
|
+
if is_present
|
256
|
+
# This doesn't apply to directives, which are legacy
|
257
|
+
# Can remove this when Skip and Include use classes or something.
|
258
|
+
if graphql_object
|
259
|
+
value = arg_defn.prepare_value(graphql_object, value)
|
260
|
+
end
|
261
|
+
kwarg_arguments[arg_defn.keyword] = value
|
262
|
+
end
|
263
|
+
end
|
264
|
+
arg_defns.each do |name, arg_defn|
|
265
|
+
if arg_defn.default_value? && !kwarg_arguments.key?(arg_defn.keyword)
|
266
|
+
kwarg_arguments[arg_defn.keyword] = arg_defn.default_value
|
267
|
+
end
|
268
|
+
end
|
269
|
+
kwarg_arguments
|
270
|
+
end
|
271
|
+
|
272
|
+
# Get a Ruby-ready value from a client query.
|
273
|
+
# @param graphql_object [Object] The owner of the field whose argument this is
|
274
|
+
# @param arg_type [Class, GraphQL::Schema::NonNull, GraphQL::Schema::List]
|
275
|
+
# @param ast_value [GraphQL::Language::Nodes::VariableIdentifier, String, Integer, Float, Boolean]
|
276
|
+
# @return [Array(is_present, value)]
|
277
|
+
def arg_to_value(query, graphql_object, arg_type, ast_value)
|
278
|
+
if ast_value.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
|
279
|
+
# If it's not here, it will get added later
|
280
|
+
if query.variables.key?(ast_value.name)
|
281
|
+
return true, query.variables[ast_value.name]
|
282
|
+
else
|
283
|
+
return false, nil
|
284
|
+
end
|
285
|
+
elsif ast_value.is_a?(GraphQL::Language::Nodes::NullValue)
|
286
|
+
return true, nil
|
287
|
+
elsif arg_type.is_a?(GraphQL::Schema::NonNull)
|
288
|
+
arg_to_value(query, graphql_object, arg_type.of_type, ast_value)
|
289
|
+
elsif arg_type.is_a?(GraphQL::Schema::List)
|
290
|
+
# Treat a single value like a list
|
291
|
+
arg_value = Array(ast_value)
|
292
|
+
list = []
|
293
|
+
arg_value.map do |inner_v|
|
294
|
+
_present, value = arg_to_value(query, graphql_object, arg_type.of_type, inner_v)
|
295
|
+
list << value
|
296
|
+
end
|
297
|
+
return true, list
|
298
|
+
elsif arg_type.is_a?(Class) && arg_type < GraphQL::Schema::InputObject
|
299
|
+
# For these, `prepare` is applied during `#initialize`.
|
300
|
+
# Pass `nil` so it will be skipped in `#arguments`.
|
301
|
+
# What a mess.
|
302
|
+
args = arguments(query, nil, arg_type, ast_value)
|
303
|
+
# We're not tracking defaults_used, but for our purposes
|
304
|
+
# we compare the value to the default value.
|
305
|
+
return true, arg_type.new(ruby_kwargs: args, context: query.context, defaults_used: nil)
|
306
|
+
else
|
307
|
+
flat_value = flatten_ast_value(query, ast_value)
|
308
|
+
return true, arg_type.coerce_input(flat_value, query.context)
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
def flatten_ast_value(query, v)
|
313
|
+
case v
|
314
|
+
when GraphQL::Language::Nodes::Enum
|
315
|
+
v.name
|
316
|
+
when GraphQL::Language::Nodes::InputObject
|
317
|
+
h = {}
|
318
|
+
v.arguments.each do |arg|
|
319
|
+
h[arg.name] = flatten_ast_value(query, arg.value)
|
320
|
+
end
|
321
|
+
h
|
322
|
+
when Array
|
323
|
+
v.map { |v2| flatten_ast_value(query, v2) }
|
324
|
+
when GraphQL::Language::Nodes::VariableIdentifier
|
325
|
+
flatten_ast_value(query.variables[v.name])
|
326
|
+
else
|
327
|
+
v
|
328
|
+
end
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
# TODO dedup with interpreter
|
333
|
+
module FieldHelpers
|
334
|
+
module_function
|
335
|
+
|
336
|
+
def get_field(schema, owner_type, field_name)
|
337
|
+
field_defn = owner_type.get_field(field_name)
|
338
|
+
field_defn ||= if owner_type == schema.query.metadata[:type_class] && (entry_point_field = schema.introspection_system.entry_point(name: field_name))
|
339
|
+
entry_point_field.metadata[:type_class]
|
340
|
+
elsif (dynamic_field = schema.introspection_system.dynamic_field(name: field_name))
|
341
|
+
dynamic_field.metadata[:type_class]
|
342
|
+
else
|
343
|
+
nil
|
344
|
+
end
|
345
|
+
|
346
|
+
field_defn
|
347
|
+
end
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
@@ -29,8 +29,8 @@ module GraphQL
|
|
29
29
|
|
30
30
|
include Tracing::Traceable
|
31
31
|
|
32
|
-
attr_reader :context, :queries, :schema
|
33
|
-
def initialize(schema:, queries:, context:)
|
32
|
+
attr_reader :context, :queries, :schema, :max_complexity
|
33
|
+
def initialize(schema:, queries:, context:, max_complexity:)
|
34
34
|
@schema = schema
|
35
35
|
@queries = queries
|
36
36
|
@context = context
|
@@ -40,6 +40,7 @@ module GraphQL
|
|
40
40
|
if context[:backtrace] && !@tracers.include?(GraphQL::Backtrace::Tracer)
|
41
41
|
@tracers << GraphQL::Backtrace::Tracer
|
42
42
|
end
|
43
|
+
@max_complexity = max_complexity
|
43
44
|
end
|
44
45
|
|
45
46
|
class << self
|
@@ -54,20 +55,20 @@ module GraphQL
|
|
54
55
|
# @param max_complexity [Integer, nil]
|
55
56
|
# @return [Array<Hash>] One result per query
|
56
57
|
def run_queries(schema, queries, context: {}, max_complexity: schema.max_complexity)
|
57
|
-
multiplex = self.new(schema: schema, queries: queries, context: context)
|
58
|
+
multiplex = self.new(schema: schema, queries: queries, context: context, max_complexity: max_complexity)
|
58
59
|
multiplex.trace("execute_multiplex", { multiplex: multiplex }) do
|
59
|
-
if
|
60
|
+
if supports_multiplexing?(schema)
|
61
|
+
instrument_and_analyze(multiplex) do
|
62
|
+
run_as_multiplex(multiplex)
|
63
|
+
end
|
64
|
+
else
|
60
65
|
if queries.length != 1
|
61
66
|
raise ArgumentError, "Multiplexing doesn't support custom execution strategies, run one query at a time instead"
|
62
67
|
else
|
63
|
-
instrument_and_analyze(multiplex
|
68
|
+
instrument_and_analyze(multiplex) do
|
64
69
|
[run_one_legacy(schema, queries.first)]
|
65
70
|
end
|
66
71
|
end
|
67
|
-
else
|
68
|
-
instrument_and_analyze(multiplex, max_complexity: max_complexity) do
|
69
|
-
run_as_multiplex(multiplex)
|
70
|
-
end
|
71
72
|
end
|
72
73
|
end
|
73
74
|
end
|
@@ -75,23 +76,25 @@ module GraphQL
|
|
75
76
|
private
|
76
77
|
|
77
78
|
def run_as_multiplex(multiplex)
|
79
|
+
|
80
|
+
multiplex.schema.query_execution_strategy.begin_multiplex(multiplex)
|
78
81
|
queries = multiplex.queries
|
79
82
|
# Do as much eager evaluation of the query as possible
|
80
83
|
results = queries.map do |query|
|
81
|
-
begin_query(query)
|
84
|
+
begin_query(query, multiplex)
|
82
85
|
end
|
83
86
|
|
84
87
|
# Then, work through lazy results in a breadth-first way
|
85
|
-
|
88
|
+
multiplex.schema.query_execution_strategy.finish_multiplex(results, multiplex)
|
86
89
|
|
87
90
|
# Then, find all errors and assign the result to the query object
|
88
91
|
results.each_with_index.map do |data_result, idx|
|
89
92
|
query = queries[idx]
|
90
|
-
finish_query(data_result, query)
|
93
|
+
finish_query(data_result, query, multiplex)
|
91
94
|
# Get the Query::Result, not the Hash
|
92
95
|
query.result
|
93
96
|
end
|
94
|
-
rescue
|
97
|
+
rescue Exception
|
95
98
|
# Assign values here so that the query's `@executed` becomes true
|
96
99
|
queries.map { |q| q.result_values ||= {} }
|
97
100
|
raise
|
@@ -99,13 +102,14 @@ module GraphQL
|
|
99
102
|
|
100
103
|
# @param query [GraphQL::Query]
|
101
104
|
# @return [Hash] The initial result (may not be finished if there are lazy values)
|
102
|
-
def begin_query(query)
|
105
|
+
def begin_query(query, multiplex)
|
103
106
|
operation = query.selected_operation
|
104
|
-
if operation.nil? || !query.valid?
|
107
|
+
if operation.nil? || !query.valid? || query.context.errors.any?
|
105
108
|
NO_OPERATION
|
106
109
|
else
|
107
110
|
begin
|
108
|
-
|
111
|
+
# These were checked to be the same in `#supports_multiplexing?`
|
112
|
+
query.schema.query_execution_strategy.begin_query(query, multiplex)
|
109
113
|
rescue GraphQL::ExecutionError => err
|
110
114
|
query.context.errors << err
|
111
115
|
NO_OPERATION
|
@@ -116,19 +120,18 @@ module GraphQL
|
|
116
120
|
# @param data_result [Hash] The result for the "data" key, if any
|
117
121
|
# @param query [GraphQL::Query] The query which was run
|
118
122
|
# @return [Hash] final result of this query, including all values and errors
|
119
|
-
def finish_query(data_result, query)
|
123
|
+
def finish_query(data_result, query, multiplex)
|
120
124
|
# Assign the result so that it can be accessed in instrumentation
|
121
125
|
query.result_values = if data_result.equal?(NO_OPERATION)
|
122
|
-
if !query.valid?
|
126
|
+
if !query.valid? || query.context.errors.any?
|
127
|
+
# A bit weird, but `Query#static_errors` _includes_ `query.context.errors`
|
123
128
|
{ "errors" => query.static_errors.map(&:to_h) }
|
124
129
|
else
|
125
130
|
data_result
|
126
131
|
end
|
127
132
|
else
|
128
133
|
# Use `context.value` which was assigned during execution
|
129
|
-
result =
|
130
|
-
"data" => Execution::Flatten.call(query.context)
|
131
|
-
}
|
134
|
+
result = query.schema.query_execution_strategy.finish_query(query, multiplex)
|
132
135
|
|
133
136
|
if query.context.errors.any?
|
134
137
|
error_result = query.context.errors.map(&:to_h)
|
@@ -153,24 +156,29 @@ module GraphQL
|
|
153
156
|
end
|
154
157
|
end
|
155
158
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
159
|
+
DEFAULT_STRATEGIES = [
|
160
|
+
GraphQL::Execution::Execute,
|
161
|
+
GraphQL::Execution::Interpreter
|
162
|
+
]
|
163
|
+
# @return [Boolean] True if the schema is only using one strategy, and it's one that supports multiplexing.
|
164
|
+
def supports_multiplexing?(schema)
|
165
|
+
schema_strategies = [schema.query_execution_strategy, schema.mutation_execution_strategy, schema.subscription_execution_strategy]
|
166
|
+
schema_strategies.uniq!
|
167
|
+
schema_strategies.size == 1 && DEFAULT_STRATEGIES.include?(schema_strategies.first)
|
160
168
|
end
|
161
169
|
|
162
170
|
# Apply multiplex & query instrumentation to `queries`.
|
163
171
|
#
|
164
172
|
# It yields when the queries should be executed, then runs teardown.
|
165
|
-
def instrument_and_analyze(multiplex
|
173
|
+
def instrument_and_analyze(multiplex)
|
166
174
|
GraphQL::Execution::Instrumentation.apply_instrumenters(multiplex) do
|
167
175
|
schema = multiplex.schema
|
168
176
|
multiplex_analyzers = schema.multiplex_analyzers
|
169
|
-
if max_complexity
|
170
|
-
multiplex_analyzers += [GraphQL::Analysis::MaxQueryComplexity.new(max_complexity)]
|
177
|
+
if multiplex.max_complexity
|
178
|
+
multiplex_analyzers += [GraphQL::Analysis::MaxQueryComplexity.new(multiplex.max_complexity)]
|
171
179
|
end
|
172
180
|
|
173
|
-
|
181
|
+
schema.analysis_engine.analyze_multiplex(multiplex, multiplex_analyzers)
|
174
182
|
yield
|
175
183
|
end
|
176
184
|
end
|
data/lib/graphql/execution.rb
CHANGED
@@ -3,6 +3,8 @@ require "graphql/execution/directive_checks"
|
|
3
3
|
require "graphql/execution/execute"
|
4
4
|
require "graphql/execution/flatten"
|
5
5
|
require "graphql/execution/instrumentation"
|
6
|
+
require "graphql/execution/interpreter"
|
6
7
|
require "graphql/execution/lazy"
|
8
|
+
require "graphql/execution/lookahead"
|
7
9
|
require "graphql/execution/multiplex"
|
8
10
|
require "graphql/execution/typecast"
|
@@ -4,7 +4,7 @@ module GraphQL
|
|
4
4
|
# the error will be inserted into the response's `"errors"` key
|
5
5
|
# and the field will resolve to `nil`.
|
6
6
|
class ExecutionError < GraphQL::Error
|
7
|
-
# @return [GraphQL::Language::Nodes::Field] the field where the error
|
7
|
+
# @return [GraphQL::Language::Nodes::Field] the field where the error occurred
|
8
8
|
attr_accessor :ast_node
|
9
9
|
|
10
10
|
# @return [String] an array describing the JSON-path into the execution
|
data/lib/graphql/field.rb
CHANGED
@@ -323,13 +323,7 @@ module GraphQL
|
|
323
323
|
|
324
324
|
module DefaultLazyResolve
|
325
325
|
def self.call(obj, args, ctx)
|
326
|
-
|
327
|
-
next_obj = obj.public_send(method_name)
|
328
|
-
if ctx.schema.lazy?(next_obj)
|
329
|
-
call(next_obj, args, ctx)
|
330
|
-
else
|
331
|
-
next_obj
|
332
|
-
end
|
326
|
+
ctx.schema.sync_lazy(obj)
|
333
327
|
end
|
334
328
|
end
|
335
329
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
class IntegerEncodingError < GraphQL::RuntimeTypeError
|
4
|
+
# The value which couldn't be encoded
|
5
|
+
attr_reader :integer_value
|
6
|
+
|
7
|
+
def initialize(value)
|
8
|
+
@integer_value = value
|
9
|
+
super('Integer out of bounds.')
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|