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
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
class Schema
|
5
|
+
class Field
|
6
|
+
class ConnectionExtension < GraphQL::Schema::FieldExtension
|
7
|
+
def apply
|
8
|
+
field.argument :after, "String", "Returns the elements in the list that come after the specified cursor.", required: false
|
9
|
+
field.argument :before, "String", "Returns the elements in the list that come before the specified cursor.", required: false
|
10
|
+
field.argument :first, "Int", "Returns the first _n_ elements from the list.", required: false
|
11
|
+
field.argument :last, "Int", "Returns the last _n_ elements from the list.", required: false
|
12
|
+
end
|
13
|
+
|
14
|
+
# Remove pagination args before passing it to a user method
|
15
|
+
def resolve(object:, arguments:, context:)
|
16
|
+
next_args = arguments.dup
|
17
|
+
next_args.delete(:first)
|
18
|
+
next_args.delete(:last)
|
19
|
+
next_args.delete(:before)
|
20
|
+
next_args.delete(:after)
|
21
|
+
yield(object, next_args)
|
22
|
+
end
|
23
|
+
|
24
|
+
def after_resolve(value:, object:, arguments:, context:, memo:)
|
25
|
+
if value.is_a? GraphQL::ExecutionError
|
26
|
+
# This isn't even going to work because context doesn't have ast_node anymore
|
27
|
+
context.add_error(value)
|
28
|
+
nil
|
29
|
+
elsif value.nil?
|
30
|
+
nil
|
31
|
+
else
|
32
|
+
if object.is_a?(GraphQL::Schema::Object)
|
33
|
+
object = object.object
|
34
|
+
end
|
35
|
+
connection_class = GraphQL::Relay::BaseConnection.connection_for_nodes(value)
|
36
|
+
connection_class.new(
|
37
|
+
value,
|
38
|
+
arguments,
|
39
|
+
field: field,
|
40
|
+
max_page_size: field.max_page_size,
|
41
|
+
parent: object,
|
42
|
+
context: context,
|
43
|
+
)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
class Schema
|
5
|
+
class Field
|
6
|
+
class ScopeExtension < GraphQL::Schema::FieldExtension
|
7
|
+
def after_resolve(value:, context:, **rest)
|
8
|
+
ret_type = @field.type.unwrap
|
9
|
+
if ret_type.respond_to?(:scope_items)
|
10
|
+
ret_type.scope_items(value, context)
|
11
|
+
else
|
12
|
+
value
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/graphql/schema/field.rb
CHANGED
@@ -1,11 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
# test_via: ../object.rb
|
3
|
+
require "graphql/schema/field/connection_extension"
|
4
|
+
require "graphql/schema/field/scope_extension"
|
5
|
+
|
3
6
|
module GraphQL
|
4
7
|
class Schema
|
5
8
|
class Field
|
6
9
|
include GraphQL::Schema::Member::CachedGraphQLDefinition
|
7
10
|
include GraphQL::Schema::Member::AcceptsDefinition
|
8
11
|
include GraphQL::Schema::Member::HasArguments
|
12
|
+
include GraphQL::Schema::Member::HasPath
|
9
13
|
|
10
14
|
# @return [String] the GraphQL name for this field, camelized unless `camelize: false` is provided
|
11
15
|
attr_reader :name
|
@@ -16,15 +20,20 @@ module GraphQL
|
|
16
20
|
# @return [String, nil] If present, the field is marked as deprecated with this documentation
|
17
21
|
attr_accessor :deprecation_reason
|
18
22
|
|
19
|
-
# @return [Symbol] Method or hash key to look up
|
23
|
+
# @return [Symbol] Method or hash key on the underlying object to look up
|
20
24
|
attr_reader :method_sym
|
21
25
|
|
22
|
-
# @return [String] Method or hash key to look up
|
26
|
+
# @return [String] Method or hash key on the underlying object to look up
|
23
27
|
attr_reader :method_str
|
24
28
|
|
29
|
+
# @return [Symbol] The method on the type to look up
|
30
|
+
attr_reader :resolver_method
|
31
|
+
|
25
32
|
# @return [Class] The type that this field belongs to
|
26
33
|
attr_reader :owner
|
27
34
|
|
35
|
+
# @return [Symobol] the original name of the field, passed in by the user
|
36
|
+
attr_reader :original_name
|
28
37
|
|
29
38
|
# @return [Class, nil] The {Schema::Resolver} this field was derived from, if there is one
|
30
39
|
def resolver
|
@@ -33,6 +42,15 @@ module GraphQL
|
|
33
42
|
|
34
43
|
alias :mutation :resolver
|
35
44
|
|
45
|
+
# @return [Array<Symbol>]
|
46
|
+
attr_reader :extras
|
47
|
+
|
48
|
+
# @return [Boolean] Apply tracing to this field? (Default: skip scalars, this is the override value)
|
49
|
+
attr_reader :trace
|
50
|
+
|
51
|
+
# @return [String, nil]
|
52
|
+
attr_reader :subscription_scope
|
53
|
+
|
36
54
|
# Create a field instance from a list of arguments, keyword arguments, and a block.
|
37
55
|
#
|
38
56
|
# This method implements prioritization between the `resolver` or `mutation` defaults
|
@@ -41,10 +59,21 @@ module GraphQL
|
|
41
59
|
# It also normalizes positional arguments into keywords for {Schema::Field#initialize}.
|
42
60
|
# @param resolver [Class] A {GraphQL::Schema::Resolver} class to use for field configuration
|
43
61
|
# @param mutation [Class] A {GraphQL::Schema::Mutation} class to use for field configuration
|
62
|
+
# @param subscription [Class] A {GraphQL::Schema::Subscription} class to use for field configuration
|
44
63
|
# @return [GraphQL::Schema:Field] an instance of `self
|
45
64
|
# @see {.initialize} for other options
|
46
|
-
def self.from_options(name = nil, type = nil, desc = nil, resolver: nil, mutation: nil,
|
47
|
-
if
|
65
|
+
def self.from_options(name = nil, type = nil, desc = nil, resolver: nil, mutation: nil, subscription: nil,**kwargs, &block)
|
66
|
+
if kwargs[:field]
|
67
|
+
if kwargs[:field] == GraphQL::Relay::Node.field
|
68
|
+
warn("Legacy-style `GraphQL::Relay::Node.field` is being added to a class-based type. See `GraphQL::Types::Relay::NodeField` for a replacement.")
|
69
|
+
return GraphQL::Types::Relay::NodeField
|
70
|
+
elsif kwargs[:field] == GraphQL::Relay::Node.plural_field
|
71
|
+
warn("Legacy-style `GraphQL::Relay::Node.plural_field` is being added to a class-based type. See `GraphQL::Types::Relay::NodesField` for a replacement.")
|
72
|
+
return GraphQL::Types::Relay::NodesField
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
if (parent_config = resolver || mutation || subscription)
|
48
77
|
# Get the parent config, merge in local overrides
|
49
78
|
kwargs = parent_config.field_options.merge(kwargs)
|
50
79
|
# Add a reference to that parent class
|
@@ -86,7 +115,8 @@ module GraphQL
|
|
86
115
|
elsif @return_type_expr
|
87
116
|
Member::BuildType.to_type_name(@return_type_expr)
|
88
117
|
else
|
89
|
-
|
118
|
+
# As a last ditch, try to force loading the return type:
|
119
|
+
type.unwrap.name
|
90
120
|
end
|
91
121
|
@connection = return_type_name.end_with?("Connection")
|
92
122
|
else
|
@@ -100,19 +130,19 @@ module GraphQL
|
|
100
130
|
# The default was overridden
|
101
131
|
@scope
|
102
132
|
else
|
103
|
-
@return_type_expr
|
133
|
+
@return_type_expr.is_a?(Array) || (@return_type_expr.is_a?(String) && @return_type_expr.include?("[")) || connection?
|
104
134
|
end
|
105
135
|
end
|
106
136
|
|
107
137
|
# @param name [Symbol] The underscore-cased version of this field name (will be camelized for the GraphQL API)
|
108
|
-
# @param
|
109
|
-
# @param desc [String] Field description
|
138
|
+
# @param type [Class, GraphQL::BaseType, Array] The return type of this field
|
110
139
|
# @param owner [Class] The type that this field belongs to
|
111
140
|
# @param null [Boolean] `true` if this field may return `null`, `false` if it is never `null`
|
112
141
|
# @param description [String] Field description
|
113
142
|
# @param deprecation_reason [String] If present, the field is marked "deprecated" with this message
|
114
|
-
# @param method [Symbol] The method to call to resolve this field (defaults to `name`)
|
115
|
-
# @param hash_key [
|
143
|
+
# @param method [Symbol] The method to call on the underlying object to resolve this field (defaults to `name`)
|
144
|
+
# @param hash_key [String, Symbol] The hash key to lookup on the underlying object (if its a Hash) to resolve this field (defaults to `name` or `name.to_s`)
|
145
|
+
# @param resolver_method [Symbol] The method on the type to call to resolve this field (defaults to `name`)
|
116
146
|
# @param connection [Boolean] `true` if this field should get automagic connection behavior; default is to infer by `*Connection` in the return type name
|
117
147
|
# @param max_page_size [Integer] For connections, the maximum number of items to return from this field
|
118
148
|
# @param introspection [Boolean] If true, this field will be marked as `#introspection?` and the name may begin with `__`
|
@@ -125,8 +155,9 @@ module GraphQL
|
|
125
155
|
# @param complexity [Numeric] When provided, set the complexity for this field
|
126
156
|
# @param scope [Boolean] If true, the return type's `.scope_items` method will be called on the return value
|
127
157
|
# @param subscription_scope [Symbol, String] A key in `context` which will be used to scope subscription payloads
|
128
|
-
|
129
|
-
|
158
|
+
# @param extensions [Array<Class>] Named extensions to apply to this field (see also {#extension})
|
159
|
+
# @param trace [Boolean] If true, a {GraphQL::Tracing} tracer will measure this scalar field
|
160
|
+
def initialize(type: nil, name: nil, owner: nil, null: nil, field: nil, function: nil, description: nil, deprecation_reason: nil, method: nil, hash_key: nil, resolver_method: nil, resolve: nil, connection: nil, max_page_size: nil, scope: nil, introspection: false, camelize: true, trace: nil, complexity: 1, extras: [], extensions: [], resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, arguments: {}, &definition_block)
|
130
161
|
if name.nil?
|
131
162
|
raise ArgumentError, "missing first `name` argument or keyword `name:`"
|
132
163
|
end
|
@@ -141,25 +172,40 @@ module GraphQL
|
|
141
172
|
if (field || function || resolve) && extras.any?
|
142
173
|
raise ArgumentError, "keyword `extras:` may only be used with method-based resolve and class-based field such as mutation class, please remove `field:`, `function:` or `resolve:`"
|
143
174
|
end
|
175
|
+
@original_name = name
|
176
|
+
@underscored_name = Member::BuildType.underscore(name.to_s)
|
144
177
|
@name = camelize ? Member::BuildType.camelize(name.to_s) : name.to_s
|
145
178
|
@description = description
|
146
179
|
if field.is_a?(GraphQL::Schema::Field)
|
147
|
-
|
180
|
+
raise ArgumentError, "Instead of passing a field as `field:`, use `add_field(field)` to add an already-defined field."
|
148
181
|
else
|
149
182
|
@field = field
|
150
183
|
end
|
151
184
|
@function = function
|
152
185
|
@resolve = resolve
|
153
186
|
@deprecation_reason = deprecation_reason
|
187
|
+
|
154
188
|
if method && hash_key
|
155
189
|
raise ArgumentError, "Provide `method:` _or_ `hash_key:`, not both. (called with: `method: #{method.inspect}, hash_key: #{hash_key.inspect}`)"
|
156
190
|
end
|
157
191
|
|
192
|
+
if resolver_method
|
193
|
+
if method
|
194
|
+
raise ArgumentError, "Provide `method:` _or_ `resolver_method:`, not both. (called with: `method: #{method.inspect}, resolver_method: #{resolver_method.inspect}`)"
|
195
|
+
end
|
196
|
+
|
197
|
+
if hash_key
|
198
|
+
raise ArgumentError, "Provide `hash_key:` _or_ `resolver_method:`, not both. (called with: `hash_key: #{hash_key.inspect}, resolver_method: #{resolver_method.inspect}`)"
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
158
202
|
# TODO: I think non-string/symbol hash keys are wrongly normalized (eg `1` will not work)
|
159
|
-
method_name = method || hash_key ||
|
203
|
+
method_name = method || hash_key || @underscored_name
|
204
|
+
resolver_method ||= @underscored_name
|
160
205
|
|
161
206
|
@method_str = method_name.to_s
|
162
207
|
@method_sym = method_name.to_sym
|
208
|
+
@resolver_method = resolver_method
|
163
209
|
@complexity = complexity
|
164
210
|
@return_type_expr = type
|
165
211
|
@return_type_null = null
|
@@ -169,6 +215,9 @@ module GraphQL
|
|
169
215
|
@extras = extras
|
170
216
|
@resolver_class = resolver_class
|
171
217
|
@scope = scope
|
218
|
+
@trace = trace
|
219
|
+
@relay_node_field = relay_node_field
|
220
|
+
@relay_nodes_field = relay_nodes_field
|
172
221
|
|
173
222
|
# Override the default from HasArguments
|
174
223
|
@own_arguments = {}
|
@@ -183,9 +232,25 @@ module GraphQL
|
|
183
232
|
@owner = owner
|
184
233
|
@subscription_scope = subscription_scope
|
185
234
|
|
235
|
+
# Do this last so we have as much context as possible when initializing them:
|
236
|
+
@extensions = []
|
237
|
+
if extensions.any?
|
238
|
+
self.extensions(extensions)
|
239
|
+
end
|
240
|
+
# This should run before connection extension,
|
241
|
+
# but should it run after the definition block?
|
242
|
+
if scoped?
|
243
|
+
self.extension(ScopeExtension)
|
244
|
+
end
|
245
|
+
# The problem with putting this after the definition_block
|
246
|
+
# is that it would override arguments
|
247
|
+
if connection?
|
248
|
+
self.extension(ConnectionExtension)
|
249
|
+
end
|
250
|
+
|
186
251
|
if definition_block
|
187
252
|
if definition_block.arity == 1
|
188
|
-
|
253
|
+
yield self
|
189
254
|
else
|
190
255
|
instance_eval(&definition_block)
|
191
256
|
end
|
@@ -202,6 +267,49 @@ module GraphQL
|
|
202
267
|
end
|
203
268
|
end
|
204
269
|
|
270
|
+
# Read extension instances from this field,
|
271
|
+
# or add new classes/options to be initialized on this field.
|
272
|
+
#
|
273
|
+
# @param extensions [Array<Class>, Hash<Class => Object>] Add extensions to this field
|
274
|
+
# @return [Array<GraphQL::Schema::FieldExtension>] extensions to apply to this field
|
275
|
+
def extensions(new_extensions = nil)
|
276
|
+
if new_extensions.nil?
|
277
|
+
# Read the value
|
278
|
+
@extensions
|
279
|
+
else
|
280
|
+
if @resolve || @function
|
281
|
+
raise ArgumentError, <<-MSG
|
282
|
+
Extensions are not supported with resolve procs or functions,
|
283
|
+
but #{owner.name}.#{name} has: #{@resolve || @function}
|
284
|
+
So, it can't have extensions: #{extensions}.
|
285
|
+
Use a method or a Schema::Resolver instead.
|
286
|
+
MSG
|
287
|
+
end
|
288
|
+
|
289
|
+
# Normalize to a Hash of {name => options}
|
290
|
+
extensions_with_options = if new_extensions.last.is_a?(Hash)
|
291
|
+
new_extensions.pop
|
292
|
+
else
|
293
|
+
{}
|
294
|
+
end
|
295
|
+
new_extensions.each do |f|
|
296
|
+
extensions_with_options[f] = nil
|
297
|
+
end
|
298
|
+
|
299
|
+
# Initialize each class and stash the instance
|
300
|
+
extensions_with_options.each do |extension_class, options|
|
301
|
+
@extensions << extension_class.new(field: self, options: options)
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
# Add `extension` to this field, initialized with `options` if provided.
|
307
|
+
# @param extension [Class] subclass of {Schema::Fieldextension}
|
308
|
+
# @param options [Object] if provided, given as `options:` when initializing `extension`.
|
309
|
+
def extension(extension, options = nil)
|
310
|
+
extensions([{extension => options}])
|
311
|
+
end
|
312
|
+
|
205
313
|
def complexity(new_complexity)
|
206
314
|
case new_complexity
|
207
315
|
when Proc
|
@@ -218,17 +326,13 @@ module GraphQL
|
|
218
326
|
else
|
219
327
|
raise("Invalid complexity: #{new_complexity.inspect} on #{@name}")
|
220
328
|
end
|
221
|
-
|
222
329
|
end
|
223
330
|
|
331
|
+
# @return [Integer, nil] Applied to connections if present
|
332
|
+
attr_reader :max_page_size
|
333
|
+
|
224
334
|
# @return [GraphQL::Field]
|
225
335
|
def to_graphql
|
226
|
-
# this field was previously defined and passed here, so delegate to it
|
227
|
-
if @field_instance
|
228
|
-
return @field_instance.to_graphql
|
229
|
-
end
|
230
|
-
|
231
|
-
|
232
336
|
field_defn = if @field
|
233
337
|
@field.dup
|
234
338
|
elsif @function
|
@@ -257,24 +361,25 @@ module GraphQL
|
|
257
361
|
field_defn.metadata[:resolver] = @resolver_class
|
258
362
|
end
|
259
363
|
|
364
|
+
if !@trace.nil?
|
365
|
+
field_defn.trace = @trace
|
366
|
+
end
|
367
|
+
|
368
|
+
if @relay_node_field
|
369
|
+
field_defn.relay_node_field = @relay_node_field
|
370
|
+
end
|
371
|
+
|
372
|
+
if @relay_nodes_field
|
373
|
+
field_defn.relay_nodes_field = @relay_nodes_field
|
374
|
+
end
|
375
|
+
|
260
376
|
field_defn.resolve = self.method(:resolve_field)
|
261
377
|
field_defn.connection = connection?
|
262
|
-
field_defn.connection_max_page_size =
|
378
|
+
field_defn.connection_max_page_size = max_page_size
|
263
379
|
field_defn.introspection = @introspection
|
264
380
|
field_defn.complexity = @complexity
|
265
381
|
field_defn.subscription_scope = @subscription_scope
|
266
382
|
|
267
|
-
# apply this first, so it can be overriden below
|
268
|
-
if connection?
|
269
|
-
# TODO: this could be a bit weird, because these fields won't be present
|
270
|
-
# after initialization, only in the `to_graphql` response.
|
271
|
-
# This calculation _could_ be moved up if need be.
|
272
|
-
argument :after, "String", "Returns the elements in the list that come after the specified cursor.", required: false
|
273
|
-
argument :before, "String", "Returns the elements in the list that come before the specified cursor.", required: false
|
274
|
-
argument :first, "Int", "Returns the first _n_ elements from the list.", required: false
|
275
|
-
argument :last, "Int", "Returns the last _n_ elements from the list.", required: false
|
276
|
-
end
|
277
|
-
|
278
383
|
arguments.each do |name, defn|
|
279
384
|
arg_graphql = defn.to_graphql
|
280
385
|
field_defn.arguments[arg_graphql.name] = arg_graphql
|
@@ -318,39 +423,99 @@ module GraphQL
|
|
318
423
|
end
|
319
424
|
|
320
425
|
def authorized?(object, context)
|
321
|
-
if @resolver_class
|
426
|
+
self_auth = if @resolver_class
|
322
427
|
@resolver_class.authorized?(object, context)
|
323
428
|
else
|
324
429
|
true
|
325
430
|
end
|
431
|
+
|
432
|
+
if self_auth
|
433
|
+
# Faster than `.any?`
|
434
|
+
arguments.each_value do |arg|
|
435
|
+
if !arg.authorized?(object, context)
|
436
|
+
return false
|
437
|
+
end
|
438
|
+
end
|
439
|
+
true
|
440
|
+
else
|
441
|
+
false
|
442
|
+
end
|
326
443
|
end
|
327
444
|
|
328
445
|
# Implement {GraphQL::Field}'s resolve API.
|
329
446
|
#
|
330
447
|
# Eventually, we might hook up field instances to execution in another way. TBD.
|
448
|
+
# @see #resolve for how the interpreter hooks up to it
|
331
449
|
def resolve_field(obj, args, ctx)
|
332
450
|
ctx.schema.after_lazy(obj) do |after_obj|
|
333
451
|
# First, apply auth ...
|
334
452
|
query_ctx = ctx.query.context
|
453
|
+
# Some legacy fields can have `nil` here, not exactly sure why.
|
454
|
+
# @see https://github.com/rmosolgo/graphql-ruby/issues/1990 before removing
|
335
455
|
inner_obj = after_obj && after_obj.object
|
336
|
-
if authorized?(inner_obj, query_ctx)
|
456
|
+
if authorized?(inner_obj, query_ctx)
|
337
457
|
# Then if it passed, resolve the field
|
338
|
-
|
458
|
+
if @resolve_proc
|
339
459
|
# Might be nil, still want to call the func in that case
|
340
460
|
@resolve_proc.call(inner_obj, args, ctx)
|
341
|
-
elsif @resolver_class
|
342
|
-
singleton_inst = @resolver_class.new(object: inner_obj, context: query_ctx)
|
343
|
-
public_send_field(singleton_inst, args, ctx)
|
344
461
|
else
|
345
462
|
public_send_field(after_obj, args, ctx)
|
346
463
|
end
|
347
|
-
apply_scope(v, ctx)
|
348
464
|
else
|
349
|
-
|
465
|
+
err = GraphQL::UnauthorizedFieldError.new(object: inner_obj, type: obj.class, context: ctx, field: self)
|
466
|
+
query_ctx.schema.unauthorized_field(err)
|
350
467
|
end
|
351
468
|
end
|
352
469
|
end
|
353
470
|
|
471
|
+
# This method is called by the interpreter for each field.
|
472
|
+
# You can extend it in your base field classes.
|
473
|
+
# @param object [GraphQL::Schema::Object] An instance of some type class, wrapping an application object
|
474
|
+
# @param args [Hash] A symbol-keyed hash of Ruby keyword arguments. (Empty if no args)
|
475
|
+
# @param ctx [GraphQL::Query::Context]
|
476
|
+
def resolve(object, args, ctx)
|
477
|
+
if @resolve_proc
|
478
|
+
raise "Can't run resolve proc for #{path} when using GraphQL::Execution::Interpreter"
|
479
|
+
end
|
480
|
+
begin
|
481
|
+
# Unwrap the GraphQL object to get the application object.
|
482
|
+
application_object = object.object
|
483
|
+
if self.authorized?(application_object, ctx)
|
484
|
+
# Apply field extensions
|
485
|
+
with_extensions(object, args, ctx) do |extended_obj, extended_args|
|
486
|
+
field_receiver = if @resolver_class
|
487
|
+
resolver_obj = if extended_obj.is_a?(GraphQL::Schema::Object)
|
488
|
+
extended_obj.object
|
489
|
+
else
|
490
|
+
extended_obj
|
491
|
+
end
|
492
|
+
@resolver_class.new(object: resolver_obj, context: ctx)
|
493
|
+
else
|
494
|
+
extended_obj
|
495
|
+
end
|
496
|
+
|
497
|
+
if field_receiver.respond_to?(@resolver_method)
|
498
|
+
# Call the method with kwargs, if there are any
|
499
|
+
if extended_args.any?
|
500
|
+
field_receiver.public_send(@resolver_method, **extended_args)
|
501
|
+
else
|
502
|
+
field_receiver.public_send(@resolver_method)
|
503
|
+
end
|
504
|
+
else
|
505
|
+
resolve_field_method(field_receiver, extended_args, ctx)
|
506
|
+
end
|
507
|
+
end
|
508
|
+
else
|
509
|
+
err = GraphQL::UnauthorizedFieldError.new(object: application_object, type: object.class, context: ctx, field: self)
|
510
|
+
ctx.schema.unauthorized_field(err)
|
511
|
+
end
|
512
|
+
rescue GraphQL::UnauthorizedError => err
|
513
|
+
ctx.schema.unauthorized_object(err)
|
514
|
+
end
|
515
|
+
rescue GraphQL::ExecutionError => err
|
516
|
+
err
|
517
|
+
end
|
518
|
+
|
354
519
|
# Find a way to resolve this field, checking:
|
355
520
|
#
|
356
521
|
# - Hash keys, if the wrapped object is a hash;
|
@@ -379,7 +544,7 @@ module GraphQL
|
|
379
544
|
raise <<-ERR
|
380
545
|
Failed to implement #{@owner.graphql_name}.#{@name}, tried:
|
381
546
|
|
382
|
-
- `#{obj.class}##{@
|
547
|
+
- `#{obj.class}##{@resolver_method}`, which did not exist
|
383
548
|
- `#{obj.object.class}##{@method_sym}`, which did not exist
|
384
549
|
- Looking up hash key `#{@method_sym.inspect}` or `#{@method_str.inspect}` on `#{obj.object}`, but it wasn't a Hash
|
385
550
|
|
@@ -388,18 +553,19 @@ module GraphQL
|
|
388
553
|
end
|
389
554
|
end
|
390
555
|
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
end
|
556
|
+
# @param ctx [GraphQL::Query::Context::FieldResolutionContext]
|
557
|
+
def fetch_extra(extra_name, ctx)
|
558
|
+
if extra_name != :path && respond_to?(extra_name)
|
559
|
+
self.public_send(extra_name)
|
560
|
+
elsif ctx.respond_to?(extra_name)
|
561
|
+
ctx.public_send(extra_name)
|
398
562
|
else
|
399
|
-
|
563
|
+
raise NotImplementedError, "Unknown field extra for #{self.path}: #{extra_name.inspect}"
|
400
564
|
end
|
401
565
|
end
|
402
566
|
|
567
|
+
private
|
568
|
+
|
403
569
|
NO_ARGS = {}.freeze
|
404
570
|
|
405
571
|
def public_send_field(obj, graphql_args, field_ctx)
|
@@ -414,27 +580,70 @@ module GraphQL
|
|
414
580
|
end
|
415
581
|
end
|
416
582
|
|
417
|
-
if connection?
|
418
|
-
# Remove pagination args before passing it to a user method
|
419
|
-
ruby_kwargs.delete(:first)
|
420
|
-
ruby_kwargs.delete(:last)
|
421
|
-
ruby_kwargs.delete(:before)
|
422
|
-
ruby_kwargs.delete(:after)
|
423
|
-
end
|
424
|
-
|
425
583
|
@extras.each do |extra_arg|
|
426
|
-
|
427
|
-
ruby_kwargs[extra_arg] = field_ctx.public_send(extra_arg)
|
584
|
+
ruby_kwargs[extra_arg] = fetch_extra(extra_arg, field_ctx)
|
428
585
|
end
|
429
586
|
else
|
430
587
|
ruby_kwargs = NO_ARGS
|
431
588
|
end
|
432
589
|
|
590
|
+
query_ctx = field_ctx.query.context
|
591
|
+
with_extensions(obj, ruby_kwargs, query_ctx) do |extended_obj, extended_args|
|
592
|
+
if @resolver_class
|
593
|
+
if extended_obj.is_a?(GraphQL::Schema::Object)
|
594
|
+
extended_obj = extended_obj.object
|
595
|
+
end
|
596
|
+
extended_obj = @resolver_class.new(object: extended_obj, context: query_ctx)
|
597
|
+
end
|
598
|
+
|
599
|
+
if extended_obj.respond_to?(@resolver_method)
|
600
|
+
if extended_args.any?
|
601
|
+
extended_obj.public_send(@resolver_method, **extended_args)
|
602
|
+
else
|
603
|
+
extended_obj.public_send(@resolver_method)
|
604
|
+
end
|
605
|
+
else
|
606
|
+
resolve_field_method(extended_obj, extended_args, query_ctx)
|
607
|
+
end
|
608
|
+
end
|
609
|
+
end
|
610
|
+
|
611
|
+
# Wrap execution with hooks.
|
612
|
+
# Written iteratively to avoid big stack traces.
|
613
|
+
# @return [Object] Whatever the
|
614
|
+
def with_extensions(obj, args, ctx)
|
615
|
+
if @extensions.empty?
|
616
|
+
yield(obj, args)
|
617
|
+
else
|
618
|
+
# Save these so that the originals can be re-given to `after_resolve` handlers.
|
619
|
+
original_args = args
|
620
|
+
original_obj = obj
|
621
|
+
|
622
|
+
memos = []
|
623
|
+
value = run_extensions_before_resolve(memos, obj, args, ctx) do |extended_obj, extended_args|
|
624
|
+
yield(extended_obj, extended_args)
|
625
|
+
end
|
433
626
|
|
434
|
-
|
435
|
-
|
627
|
+
ctx.schema.after_lazy(value) do |resolved_value|
|
628
|
+
@extensions.each_with_index do |ext, idx|
|
629
|
+
memo = memos[idx]
|
630
|
+
# TODO after_lazy?
|
631
|
+
resolved_value = ext.after_resolve(object: original_obj, arguments: original_args, context: ctx, value: resolved_value, memo: memo)
|
632
|
+
end
|
633
|
+
resolved_value
|
634
|
+
end
|
635
|
+
end
|
636
|
+
end
|
637
|
+
|
638
|
+
def run_extensions_before_resolve(memos, obj, args, ctx, idx: 0)
|
639
|
+
extension = @extensions[idx]
|
640
|
+
if extension
|
641
|
+
extension.resolve(object: obj, arguments: args, context: ctx) do |extended_obj, extended_args, memo|
|
642
|
+
memos << memo
|
643
|
+
run_extensions_before_resolve(memos, extended_obj, extended_args, ctx, idx: idx + 1) { |o, a| yield(o, a) }
|
644
|
+
end
|
436
645
|
else
|
437
|
-
obj
|
646
|
+
yield(obj, args)
|
438
647
|
end
|
439
648
|
end
|
440
649
|
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
class Schema
|
4
|
+
# Extend this class to make field-level customizations to resolve behavior.
|
5
|
+
#
|
6
|
+
# When a extension is added to a field with `extension(MyExtension)`, a `MyExtension` instance
|
7
|
+
# is created, and its hooks are applied whenever that field is called.
|
8
|
+
#
|
9
|
+
# The instance is frozen so that instance variables aren't modified during query execution,
|
10
|
+
# which could cause all kinds of issues due to race conditions.
|
11
|
+
class FieldExtension
|
12
|
+
# @return [GraphQL::Schema::Field]
|
13
|
+
attr_reader :field
|
14
|
+
|
15
|
+
# @return [Object]
|
16
|
+
attr_reader :options
|
17
|
+
|
18
|
+
# Called when the extension is mounted with `extension(name, options)`.
|
19
|
+
# The instance is frozen to avoid improper use of state during execution.
|
20
|
+
# @param field [GraphQL::Schema::Field] The field where this extension was mounted
|
21
|
+
# @param options [Object] The second argument to `extension`, or `nil` if nothing was passed.
|
22
|
+
def initialize(field:, options:)
|
23
|
+
@field = field
|
24
|
+
@options = options
|
25
|
+
apply
|
26
|
+
freeze
|
27
|
+
end
|
28
|
+
|
29
|
+
# Called when this extension is attached to a field.
|
30
|
+
# The field definition may be extended during this method.
|
31
|
+
# @return [void]
|
32
|
+
def apply
|
33
|
+
end
|
34
|
+
|
35
|
+
# Called before resolving {#field}. It should either:
|
36
|
+
#
|
37
|
+
# - `yield` values to continue execution; OR
|
38
|
+
# - return something else to shortcut field execution.
|
39
|
+
#
|
40
|
+
# Whatever this method returns will be used for execution.
|
41
|
+
#
|
42
|
+
# @param object [Object] The object the field is being resolved on
|
43
|
+
# @param arguments [Hash] Ruby keyword arguments for resolving this field
|
44
|
+
# @param context [Query::Context] the context for this query
|
45
|
+
# @yieldparam object [Object] The object to continue resolving the field on
|
46
|
+
# @yieldparam arguments [Hash] The keyword arguments to continue resolving with
|
47
|
+
# @yieldparam memo [Object] Any extension-specific value which will be passed to {#after_resolve} later
|
48
|
+
# @return [Object] The return value for this field.
|
49
|
+
def resolve(object:, arguments:, context:)
|
50
|
+
yield(object, arguments, nil)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Called after {#field} was resolved, and after any lazy values (like `Promise`s) were synced,
|
54
|
+
# but before the value was added to the GraphQL response.
|
55
|
+
#
|
56
|
+
# Whatever this hook returns will be used as the return value.
|
57
|
+
#
|
58
|
+
# @param object [Object] The object the field is being resolved on
|
59
|
+
# @param arguments [Hash] Ruby keyword arguments for resolving this field
|
60
|
+
# @param context [Query::Context] the context for this query
|
61
|
+
# @param value [Object] Whatever the field previously returned
|
62
|
+
# @param memo [Object] The third value yielded by {#resolve}, or `nil` if there wasn't one
|
63
|
+
# @return [Object] The return value for this field.
|
64
|
+
def after_resolve(object:, arguments:, context:, value:, memo:)
|
65
|
+
value
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|