graphql_cody 1.13.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 +7 -0
- data/.yardopts +5 -0
- data/MIT-LICENSE +20 -0
- data/lib/generators/graphql/core.rb +74 -0
- data/lib/generators/graphql/enum_generator.rb +33 -0
- data/lib/generators/graphql/install_generator.rb +190 -0
- data/lib/generators/graphql/interface_generator.rb +27 -0
- data/lib/generators/graphql/loader_generator.rb +21 -0
- data/lib/generators/graphql/mutation_generator.rb +55 -0
- data/lib/generators/graphql/object_generator.rb +79 -0
- data/lib/generators/graphql/relay.rb +63 -0
- data/lib/generators/graphql/relay_generator.rb +21 -0
- data/lib/generators/graphql/scalar_generator.rb +20 -0
- data/lib/generators/graphql/templates/base_argument.erb +6 -0
- data/lib/generators/graphql/templates/base_connection.erb +8 -0
- data/lib/generators/graphql/templates/base_edge.erb +8 -0
- data/lib/generators/graphql/templates/base_enum.erb +6 -0
- data/lib/generators/graphql/templates/base_field.erb +7 -0
- data/lib/generators/graphql/templates/base_input_object.erb +7 -0
- data/lib/generators/graphql/templates/base_interface.erb +9 -0
- data/lib/generators/graphql/templates/base_mutation.erb +10 -0
- data/lib/generators/graphql/templates/base_object.erb +7 -0
- data/lib/generators/graphql/templates/base_scalar.erb +6 -0
- data/lib/generators/graphql/templates/base_union.erb +6 -0
- data/lib/generators/graphql/templates/enum.erb +7 -0
- data/lib/generators/graphql/templates/graphql_controller.erb +52 -0
- data/lib/generators/graphql/templates/interface.erb +8 -0
- data/lib/generators/graphql/templates/loader.erb +19 -0
- data/lib/generators/graphql/templates/mutation.erb +16 -0
- data/lib/generators/graphql/templates/mutation_type.erb +12 -0
- data/lib/generators/graphql/templates/node_type.erb +9 -0
- data/lib/generators/graphql/templates/object.erb +8 -0
- data/lib/generators/graphql/templates/query_type.erb +15 -0
- data/lib/generators/graphql/templates/scalar.erb +15 -0
- data/lib/generators/graphql/templates/schema.erb +27 -0
- data/lib/generators/graphql/templates/union.erb +7 -0
- data/lib/generators/graphql/type_generator.rb +98 -0
- data/lib/generators/graphql/union_generator.rb +33 -0
- data/lib/graphql/analysis/analyze_query.rb +98 -0
- data/lib/graphql/analysis/ast/analyzer.rb +84 -0
- data/lib/graphql/analysis/ast/field_usage.rb +51 -0
- data/lib/graphql/analysis/ast/max_query_complexity.rb +23 -0
- data/lib/graphql/analysis/ast/max_query_depth.rb +22 -0
- data/lib/graphql/analysis/ast/query_complexity.rb +230 -0
- data/lib/graphql/analysis/ast/query_depth.rb +56 -0
- data/lib/graphql/analysis/ast/visitor.rb +268 -0
- data/lib/graphql/analysis/ast.rb +91 -0
- data/lib/graphql/analysis/field_usage.rb +45 -0
- data/lib/graphql/analysis/max_query_complexity.rb +26 -0
- data/lib/graphql/analysis/max_query_depth.rb +26 -0
- data/lib/graphql/analysis/query_complexity.rb +88 -0
- data/lib/graphql/analysis/query_depth.rb +43 -0
- data/lib/graphql/analysis/reducer_state.rb +48 -0
- data/lib/graphql/analysis.rb +9 -0
- data/lib/graphql/analysis_error.rb +5 -0
- data/lib/graphql/argument.rb +131 -0
- data/lib/graphql/authorization.rb +82 -0
- data/lib/graphql/backtrace/inspect_result.rb +50 -0
- data/lib/graphql/backtrace/legacy_tracer.rb +56 -0
- data/lib/graphql/backtrace/table.rb +159 -0
- data/lib/graphql/backtrace/traced_error.rb +54 -0
- data/lib/graphql/backtrace/tracer.rb +81 -0
- data/lib/graphql/backtrace.rb +64 -0
- data/lib/graphql/backwards_compatibility.rb +61 -0
- data/lib/graphql/base_type.rb +230 -0
- data/lib/graphql/boolean_type.rb +2 -0
- data/lib/graphql/coercion_error.rb +13 -0
- data/lib/graphql/compatibility/execution_specification/counter_schema.rb +53 -0
- data/lib/graphql/compatibility/execution_specification/specification_schema.rb +200 -0
- data/lib/graphql/compatibility/execution_specification.rb +436 -0
- data/lib/graphql/compatibility/lazy_execution_specification/lazy_schema.rb +111 -0
- data/lib/graphql/compatibility/lazy_execution_specification.rb +215 -0
- data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +87 -0
- data/lib/graphql/compatibility/query_parser_specification/query_assertions.rb +79 -0
- data/lib/graphql/compatibility/query_parser_specification.rb +266 -0
- data/lib/graphql/compatibility/schema_parser_specification.rb +682 -0
- data/lib/graphql/compatibility.rb +5 -0
- data/lib/graphql/dataloader/null_dataloader.rb +22 -0
- data/lib/graphql/dataloader/request.rb +19 -0
- data/lib/graphql/dataloader/request_all.rb +19 -0
- data/lib/graphql/dataloader/source.rb +155 -0
- data/lib/graphql/dataloader.rb +308 -0
- data/lib/graphql/define/assign_argument.rb +12 -0
- data/lib/graphql/define/assign_connection.rb +13 -0
- data/lib/graphql/define/assign_enum_value.rb +18 -0
- data/lib/graphql/define/assign_global_id_field.rb +11 -0
- data/lib/graphql/define/assign_mutation_function.rb +34 -0
- data/lib/graphql/define/assign_object_field.rb +42 -0
- data/lib/graphql/define/defined_object_proxy.rb +53 -0
- data/lib/graphql/define/instance_definable.rb +240 -0
- data/lib/graphql/define/no_definition_error.rb +7 -0
- data/lib/graphql/define/non_null_with_bang.rb +16 -0
- data/lib/graphql/define/type_definer.rb +31 -0
- data/lib/graphql/define.rb +31 -0
- data/lib/graphql/deprecated_dsl.rb +55 -0
- data/lib/graphql/deprecation.rb +9 -0
- data/lib/graphql/dig.rb +19 -0
- data/lib/graphql/directive/deprecated_directive.rb +2 -0
- data/lib/graphql/directive/include_directive.rb +2 -0
- data/lib/graphql/directive/skip_directive.rb +2 -0
- data/lib/graphql/directive.rb +107 -0
- data/lib/graphql/enum_type.rb +133 -0
- data/lib/graphql/execution/directive_checks.rb +37 -0
- data/lib/graphql/execution/errors.rb +163 -0
- data/lib/graphql/execution/execute.rb +333 -0
- data/lib/graphql/execution/flatten.rb +40 -0
- data/lib/graphql/execution/instrumentation.rb +92 -0
- data/lib/graphql/execution/interpreter/argument_value.rb +28 -0
- data/lib/graphql/execution/interpreter/arguments.rb +88 -0
- data/lib/graphql/execution/interpreter/arguments_cache.rb +103 -0
- data/lib/graphql/execution/interpreter/execution_errors.rb +29 -0
- data/lib/graphql/execution/interpreter/handles_raw_value.rb +18 -0
- data/lib/graphql/execution/interpreter/resolve.rb +70 -0
- data/lib/graphql/execution/interpreter/runtime.rb +949 -0
- data/lib/graphql/execution/interpreter.rb +122 -0
- data/lib/graphql/execution/lazy/lazy_method_map.rb +98 -0
- data/lib/graphql/execution/lazy/resolve.rb +91 -0
- data/lib/graphql/execution/lazy.rb +83 -0
- data/lib/graphql/execution/lookahead.rb +307 -0
- data/lib/graphql/execution/multiplex.rb +214 -0
- data/lib/graphql/execution/typecast.rb +50 -0
- data/lib/graphql/execution.rb +11 -0
- data/lib/graphql/execution_error.rb +58 -0
- data/lib/graphql/field/resolve.rb +59 -0
- data/lib/graphql/field.rb +226 -0
- data/lib/graphql/filter.rb +53 -0
- data/lib/graphql/float_type.rb +2 -0
- data/lib/graphql/function.rb +128 -0
- data/lib/graphql/id_type.rb +2 -0
- data/lib/graphql/input_object_type.rb +138 -0
- data/lib/graphql/int_type.rb +2 -0
- data/lib/graphql/integer_decoding_error.rb +17 -0
- data/lib/graphql/integer_encoding_error.rb +36 -0
- data/lib/graphql/interface_type.rb +72 -0
- data/lib/graphql/internal_representation/document.rb +27 -0
- data/lib/graphql/internal_representation/node.rb +206 -0
- data/lib/graphql/internal_representation/print.rb +51 -0
- data/lib/graphql/internal_representation/rewrite.rb +184 -0
- data/lib/graphql/internal_representation/scope.rb +88 -0
- data/lib/graphql/internal_representation/visit.rb +36 -0
- data/lib/graphql/internal_representation.rb +7 -0
- data/lib/graphql/introspection/base_object.rb +13 -0
- data/lib/graphql/introspection/directive_location_enum.rb +15 -0
- data/lib/graphql/introspection/directive_type.rb +29 -0
- data/lib/graphql/introspection/dynamic_fields.rb +17 -0
- data/lib/graphql/introspection/entry_points.rb +35 -0
- data/lib/graphql/introspection/enum_value_type.rb +23 -0
- data/lib/graphql/introspection/field_type.rb +28 -0
- data/lib/graphql/introspection/input_value_type.rb +67 -0
- data/lib/graphql/introspection/introspection_query.rb +7 -0
- data/lib/graphql/introspection/schema_type.rb +44 -0
- data/lib/graphql/introspection/type_kind_enum.rb +13 -0
- data/lib/graphql/introspection/type_type.rb +95 -0
- data/lib/graphql/introspection.rb +114 -0
- data/lib/graphql/invalid_name_error.rb +11 -0
- data/lib/graphql/invalid_null_error.rb +50 -0
- data/lib/graphql/language/block_string.rb +99 -0
- data/lib/graphql/language/cache.rb +37 -0
- data/lib/graphql/language/definition_slice.rb +41 -0
- data/lib/graphql/language/document_from_schema_definition.rb +347 -0
- data/lib/graphql/language/generation.rb +24 -0
- data/lib/graphql/language/lexer.rb +1467 -0
- data/lib/graphql/language/lexer.rl +258 -0
- data/lib/graphql/language/nodes.rb +707 -0
- data/lib/graphql/language/parser.rb +1974 -0
- data/lib/graphql/language/parser.y +544 -0
- data/lib/graphql/language/printer.rb +366 -0
- data/lib/graphql/language/sanitized_printer.rb +222 -0
- data/lib/graphql/language/token.rb +34 -0
- data/lib/graphql/language/visitor.rb +242 -0
- data/lib/graphql/language.rb +36 -0
- data/lib/graphql/list_type.rb +80 -0
- data/lib/graphql/load_application_object_failed_error.rb +22 -0
- data/lib/graphql/name_validator.rb +11 -0
- data/lib/graphql/non_null_type.rb +71 -0
- data/lib/graphql/object_type.rb +130 -0
- data/lib/graphql/pagination/active_record_relation_connection.rb +48 -0
- data/lib/graphql/pagination/array_connection.rb +77 -0
- data/lib/graphql/pagination/connection.rb +226 -0
- data/lib/graphql/pagination/connections.rb +160 -0
- data/lib/graphql/pagination/mongoid_relation_connection.rb +25 -0
- data/lib/graphql/pagination/relation_connection.rb +196 -0
- data/lib/graphql/pagination/sequel_dataset_connection.rb +28 -0
- data/lib/graphql/pagination.rb +6 -0
- data/lib/graphql/parse_error.rb +24 -0
- data/lib/graphql/query/arguments.rb +189 -0
- data/lib/graphql/query/arguments_cache.rb +24 -0
- data/lib/graphql/query/context.rb +371 -0
- data/lib/graphql/query/executor.rb +52 -0
- data/lib/graphql/query/fingerprint.rb +26 -0
- data/lib/graphql/query/input_validation_result.rb +43 -0
- data/lib/graphql/query/literal_input.rb +136 -0
- data/lib/graphql/query/null_context.rb +55 -0
- data/lib/graphql/query/result.rb +63 -0
- data/lib/graphql/query/serial_execution/field_resolution.rb +92 -0
- data/lib/graphql/query/serial_execution/operation_resolution.rb +19 -0
- data/lib/graphql/query/serial_execution/selection_resolution.rb +23 -0
- data/lib/graphql/query/serial_execution/value_resolution.rb +87 -0
- data/lib/graphql/query/serial_execution.rb +40 -0
- data/lib/graphql/query/validation_pipeline.rb +139 -0
- data/lib/graphql/query/variable_validation_error.rb +44 -0
- data/lib/graphql/query/variables.rb +78 -0
- data/lib/graphql/query.rb +454 -0
- data/lib/graphql/railtie.rb +117 -0
- data/lib/graphql/rake_task/validate.rb +63 -0
- data/lib/graphql/rake_task.rb +145 -0
- data/lib/graphql/relay/array_connection.rb +83 -0
- data/lib/graphql/relay/base_connection.rb +189 -0
- data/lib/graphql/relay/connection_instrumentation.rb +54 -0
- data/lib/graphql/relay/connection_resolve.rb +43 -0
- data/lib/graphql/relay/connection_type.rb +41 -0
- data/lib/graphql/relay/edge.rb +27 -0
- data/lib/graphql/relay/edge_type.rb +19 -0
- data/lib/graphql/relay/edges_instrumentation.rb +39 -0
- data/lib/graphql/relay/global_id_resolve.rb +18 -0
- data/lib/graphql/relay/mongo_relation_connection.rb +50 -0
- data/lib/graphql/relay/mutation/instrumentation.rb +23 -0
- data/lib/graphql/relay/mutation/resolve.rb +56 -0
- data/lib/graphql/relay/mutation/result.rb +38 -0
- data/lib/graphql/relay/mutation.rb +106 -0
- data/lib/graphql/relay/node.rb +39 -0
- data/lib/graphql/relay/page_info.rb +7 -0
- data/lib/graphql/relay/range_add.rb +59 -0
- data/lib/graphql/relay/relation_connection.rb +188 -0
- data/lib/graphql/relay/type_extensions.rb +32 -0
- data/lib/graphql/relay.rb +18 -0
- data/lib/graphql/rubocop/graphql/base_cop.rb +36 -0
- data/lib/graphql/rubocop/graphql/default_null_true.rb +43 -0
- data/lib/graphql/rubocop/graphql/default_required_true.rb +43 -0
- data/lib/graphql/rubocop.rb +4 -0
- data/lib/graphql/runtime_type_error.rb +5 -0
- data/lib/graphql/scalar_type.rb +91 -0
- data/lib/graphql/schema/addition.rb +247 -0
- data/lib/graphql/schema/argument.rb +383 -0
- data/lib/graphql/schema/base_64_bp.rb +26 -0
- data/lib/graphql/schema/base_64_encoder.rb +21 -0
- data/lib/graphql/schema/build_from_definition/resolve_map/default_resolve.rb +47 -0
- data/lib/graphql/schema/build_from_definition/resolve_map.rb +78 -0
- data/lib/graphql/schema/build_from_definition.rb +477 -0
- data/lib/graphql/schema/built_in_types.rb +12 -0
- data/lib/graphql/schema/catchall_middleware.rb +35 -0
- data/lib/graphql/schema/default_parse_error.rb +10 -0
- data/lib/graphql/schema/default_type_error.rb +17 -0
- data/lib/graphql/schema/directive/deprecated.rb +18 -0
- data/lib/graphql/schema/directive/feature.rb +66 -0
- data/lib/graphql/schema/directive/flagged.rb +57 -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 +60 -0
- data/lib/graphql/schema/directive.rb +210 -0
- data/lib/graphql/schema/enum.rb +193 -0
- data/lib/graphql/schema/enum_value.rb +97 -0
- data/lib/graphql/schema/field/connection_extension.rb +76 -0
- data/lib/graphql/schema/field/scope_extension.rb +22 -0
- data/lib/graphql/schema/field.rb +880 -0
- data/lib/graphql/schema/field_extension.rb +69 -0
- data/lib/graphql/schema/find_inherited_value.rb +36 -0
- data/lib/graphql/schema/finder.rb +155 -0
- data/lib/graphql/schema/input_object.rb +253 -0
- data/lib/graphql/schema/interface.rb +136 -0
- data/lib/graphql/schema/introspection_system.rb +169 -0
- data/lib/graphql/schema/invalid_type_error.rb +7 -0
- data/lib/graphql/schema/late_bound_type.rb +33 -0
- data/lib/graphql/schema/list.rb +75 -0
- data/lib/graphql/schema/loader.rb +226 -0
- data/lib/graphql/schema/member/accepts_definition.rb +159 -0
- data/lib/graphql/schema/member/base_dsl_methods.rb +129 -0
- data/lib/graphql/schema/member/build_type.rb +180 -0
- data/lib/graphql/schema/member/cached_graphql_definition.rb +31 -0
- data/lib/graphql/schema/member/graphql_type_names.rb +21 -0
- data/lib/graphql/schema/member/has_arguments.rb +332 -0
- data/lib/graphql/schema/member/has_ast_node.rb +20 -0
- data/lib/graphql/schema/member/has_deprecation_reason.rb +25 -0
- data/lib/graphql/schema/member/has_directives.rb +98 -0
- data/lib/graphql/schema/member/has_fields.rb +163 -0
- data/lib/graphql/schema/member/has_interfaces.rb +90 -0
- data/lib/graphql/schema/member/has_path.rb +25 -0
- data/lib/graphql/schema/member/has_unresolved_type_error.rb +15 -0
- data/lib/graphql/schema/member/has_validators.rb +31 -0
- data/lib/graphql/schema/member/instrumentation.rb +131 -0
- data/lib/graphql/schema/member/relay_shortcuts.rb +47 -0
- data/lib/graphql/schema/member/scoped.rb +21 -0
- data/lib/graphql/schema/member/type_system_helpers.rb +38 -0
- data/lib/graphql/schema/member/validates_input.rb +33 -0
- data/lib/graphql/schema/member.rb +161 -0
- data/lib/graphql/schema/middleware_chain.rb +82 -0
- data/lib/graphql/schema/mutation.rb +94 -0
- data/lib/graphql/schema/non_null.rb +67 -0
- data/lib/graphql/schema/null_mask.rb +11 -0
- data/lib/graphql/schema/object.rb +150 -0
- data/lib/graphql/schema/possible_types.rb +44 -0
- data/lib/graphql/schema/printer.rb +100 -0
- data/lib/graphql/schema/relay_classic_mutation.rb +160 -0
- data/lib/graphql/schema/rescue_middleware.rb +60 -0
- data/lib/graphql/schema/resolver/has_payload_type.rb +96 -0
- data/lib/graphql/schema/resolver.rb +397 -0
- data/lib/graphql/schema/scalar.rb +69 -0
- data/lib/graphql/schema/subscription.rb +155 -0
- data/lib/graphql/schema/timeout.rb +123 -0
- data/lib/graphql/schema/timeout_middleware.rb +88 -0
- data/lib/graphql/schema/traversal.rb +228 -0
- data/lib/graphql/schema/type_expression.rb +43 -0
- data/lib/graphql/schema/type_membership.rb +48 -0
- data/lib/graphql/schema/union.rb +95 -0
- data/lib/graphql/schema/unique_within_type.rb +34 -0
- data/lib/graphql/schema/validation.rb +313 -0
- data/lib/graphql/schema/validator/allow_blank_validator.rb +29 -0
- data/lib/graphql/schema/validator/allow_null_validator.rb +26 -0
- data/lib/graphql/schema/validator/exclusion_validator.rb +33 -0
- data/lib/graphql/schema/validator/format_validator.rb +48 -0
- data/lib/graphql/schema/validator/inclusion_validator.rb +35 -0
- data/lib/graphql/schema/validator/length_validator.rb +59 -0
- data/lib/graphql/schema/validator/numericality_validator.rb +82 -0
- data/lib/graphql/schema/validator/required_validator.rb +68 -0
- data/lib/graphql/schema/validator.rb +174 -0
- data/lib/graphql/schema/warden.rb +409 -0
- data/lib/graphql/schema/wrapper.rb +29 -0
- data/lib/graphql/schema.rb +1945 -0
- data/lib/graphql/static_validation/all_rules.rb +40 -0
- data/lib/graphql/static_validation/base_visitor.rb +217 -0
- data/lib/graphql/static_validation/default_visitor.rb +15 -0
- data/lib/graphql/static_validation/definition_dependencies.rb +198 -0
- data/lib/graphql/static_validation/error.rb +46 -0
- data/lib/graphql/static_validation/interpreter_visitor.rb +14 -0
- data/lib/graphql/static_validation/literal_validator.rb +139 -0
- data/lib/graphql/static_validation/no_validate_visitor.rb +10 -0
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +66 -0
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible_error.rb +48 -0
- data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +31 -0
- data/lib/graphql/static_validation/rules/argument_names_are_unique_error.rb +30 -0
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +71 -0
- data/lib/graphql/static_validation/rules/arguments_are_defined_error.rb +37 -0
- data/lib/graphql/static_validation/rules/directives_are_defined.rb +23 -0
- 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 +65 -0
- 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 +30 -0
- 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 +73 -0
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections_error.rb +31 -0
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +418 -0
- data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +53 -0
- data/lib/graphql/static_validation/rules/fragment_names_are_unique.rb +30 -0
- 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 +73 -0
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible_error.rb +35 -0
- data/lib/graphql/static_validation/rules/fragment_types_exist.rb +39 -0
- data/lib/graphql/static_validation/rules/fragment_types_exist_error.rb +29 -0
- data/lib/graphql/static_validation/rules/fragments_are_finite.rb +21 -0
- data/lib/graphql/static_validation/rules/fragments_are_finite_error.rb +29 -0
- data/lib/graphql/static_validation/rules/fragments_are_named.rb +16 -0
- 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 +37 -0
- 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 +32 -0
- data/lib/graphql/static_validation/rules/fragments_are_used_error.rb +29 -0
- data/lib/graphql/static_validation/rules/input_object_names_are_unique.rb +30 -0
- data/lib/graphql/static_validation/rules/input_object_names_are_unique_error.rb +30 -0
- data/lib/graphql/static_validation/rules/mutation_root_exists.rb +17 -0
- data/lib/graphql/static_validation/rules/mutation_root_exists_error.rb +26 -0
- data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +41 -0
- 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 +36 -0
- 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 +37 -0
- 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 +59 -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 +17 -0
- data/lib/graphql/static_validation/rules/subscription_root_exists_error.rb +26 -0
- data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +50 -0
- 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 +46 -0
- 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 +24 -0
- 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 +153 -0
- 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 +39 -0
- 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 +155 -0
- data/lib/graphql/static_validation/rules/variables_are_used_and_defined_error.rb +37 -0
- data/lib/graphql/static_validation/type_stack.rb +216 -0
- data/lib/graphql/static_validation/validation_context.rb +49 -0
- data/lib/graphql/static_validation/validation_timeout_error.rb +25 -0
- data/lib/graphql/static_validation/validator.rb +96 -0
- data/lib/graphql/static_validation.rb +19 -0
- data/lib/graphql/string_encoding_error.rb +20 -0
- data/lib/graphql/string_type.rb +2 -0
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +245 -0
- data/lib/graphql/subscriptions/broadcast_analyzer.rb +81 -0
- data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +21 -0
- data/lib/graphql/subscriptions/event.rb +144 -0
- data/lib/graphql/subscriptions/instrumentation.rb +79 -0
- data/lib/graphql/subscriptions/serialize.rb +138 -0
- data/lib/graphql/subscriptions/subscription_root.rb +76 -0
- data/lib/graphql/subscriptions.rb +299 -0
- data/lib/graphql/tracing/active_support_notifications_tracing.rb +35 -0
- data/lib/graphql/tracing/appoptics_tracing.rb +173 -0
- data/lib/graphql/tracing/appsignal_tracing.rb +51 -0
- data/lib/graphql/tracing/data_dog_tracing.rb +76 -0
- data/lib/graphql/tracing/new_relic_tracing.rb +51 -0
- data/lib/graphql/tracing/platform_tracing.rb +139 -0
- data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +32 -0
- data/lib/graphql/tracing/prometheus_tracing.rb +67 -0
- data/lib/graphql/tracing/scout_tracing.rb +54 -0
- data/lib/graphql/tracing/skylight_tracing.rb +70 -0
- data/lib/graphql/tracing/statsd_tracing.rb +42 -0
- data/lib/graphql/tracing.rb +95 -0
- data/lib/graphql/type_kinds.rb +77 -0
- data/lib/graphql/types/big_int.rb +23 -0
- data/lib/graphql/types/boolean.rb +18 -0
- data/lib/graphql/types/float.rb +19 -0
- data/lib/graphql/types/id.rb +24 -0
- data/lib/graphql/types/int.rb +36 -0
- data/lib/graphql/types/iso_8601_date.rb +34 -0
- data/lib/graphql/types/iso_8601_date_time.rb +65 -0
- data/lib/graphql/types/json.rb +25 -0
- data/lib/graphql/types/relay/base_connection.rb +39 -0
- data/lib/graphql/types/relay/base_edge.rb +29 -0
- data/lib/graphql/types/relay/connection_behaviors.rb +156 -0
- data/lib/graphql/types/relay/default_relay.rb +27 -0
- data/lib/graphql/types/relay/edge_behaviors.rb +53 -0
- data/lib/graphql/types/relay/has_node_field.rb +41 -0
- data/lib/graphql/types/relay/has_nodes_field.rb +41 -0
- data/lib/graphql/types/relay/node.rb +15 -0
- data/lib/graphql/types/relay/node_behaviors.rb +15 -0
- data/lib/graphql/types/relay/node_field.rb +25 -0
- data/lib/graphql/types/relay/nodes_field.rb +27 -0
- data/lib/graphql/types/relay/page_info.rb +11 -0
- data/lib/graphql/types/relay/page_info_behaviors.rb +25 -0
- data/lib/graphql/types/relay.rb +41 -0
- data/lib/graphql/types/string.rb +29 -0
- data/lib/graphql/types.rb +11 -0
- data/lib/graphql/unauthorized_error.rb +29 -0
- data/lib/graphql/unauthorized_field_error.rb +23 -0
- data/lib/graphql/union_type.rb +115 -0
- data/lib/graphql/unresolved_type_error.rb +35 -0
- data/lib/graphql/upgrader/member.rb +937 -0
- data/lib/graphql/upgrader/schema.rb +38 -0
- data/lib/graphql/version.rb +4 -0
- data/lib/graphql.rb +168 -0
- data/readme.md +49 -0
- metadata +714 -0
@@ -0,0 +1,880 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "graphql/schema/field/connection_extension"
|
3
|
+
require "graphql/schema/field/scope_extension"
|
4
|
+
|
5
|
+
module GraphQL
|
6
|
+
class Schema
|
7
|
+
class Field
|
8
|
+
include GraphQL::Schema::Member::CachedGraphQLDefinition
|
9
|
+
include GraphQL::Schema::Member::AcceptsDefinition
|
10
|
+
include GraphQL::Schema::Member::HasArguments
|
11
|
+
include GraphQL::Schema::Member::HasAstNode
|
12
|
+
include GraphQL::Schema::Member::HasPath
|
13
|
+
include GraphQL::Schema::Member::HasValidators
|
14
|
+
extend GraphQL::Schema::FindInheritedValue
|
15
|
+
include GraphQL::Schema::FindInheritedValue::EmptyObjects
|
16
|
+
include GraphQL::Schema::Member::HasDirectives
|
17
|
+
include GraphQL::Schema::Member::HasDeprecationReason
|
18
|
+
|
19
|
+
# @return [String] the GraphQL name for this field, camelized unless `camelize: false` is provided
|
20
|
+
attr_reader :name
|
21
|
+
alias :graphql_name :name
|
22
|
+
|
23
|
+
attr_writer :description
|
24
|
+
|
25
|
+
# @return [Symbol] Method or hash key on the underlying object to look up
|
26
|
+
attr_reader :method_sym
|
27
|
+
|
28
|
+
# @return [String] Method or hash key on the underlying object to look up
|
29
|
+
attr_reader :method_str
|
30
|
+
|
31
|
+
# @return [Symbol] The method on the type to look up
|
32
|
+
attr_reader :resolver_method
|
33
|
+
|
34
|
+
# @return [Class] The thing this field was defined on (type, mutation, resolver)
|
35
|
+
attr_accessor :owner
|
36
|
+
|
37
|
+
# @return [Class] The GraphQL type this field belongs to. (For fields defined on mutations, it's the payload type)
|
38
|
+
def owner_type
|
39
|
+
@owner_type ||= if owner < GraphQL::Schema::Mutation
|
40
|
+
owner.payload_type
|
41
|
+
else
|
42
|
+
owner
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# @return [Symbol] the original name of the field, passed in by the user
|
47
|
+
attr_reader :original_name
|
48
|
+
|
49
|
+
# @return [Class, nil] The {Schema::Resolver} this field was derived from, if there is one
|
50
|
+
def resolver
|
51
|
+
@resolver_class
|
52
|
+
end
|
53
|
+
|
54
|
+
# @return [Boolean] Is this field a predefined introspection field?
|
55
|
+
def introspection?
|
56
|
+
@introspection
|
57
|
+
end
|
58
|
+
|
59
|
+
def inspect
|
60
|
+
"#<#{self.class} #{path}#{all_argument_definitions.any? ? "(...)" : ""}: #{type.to_type_signature}>"
|
61
|
+
end
|
62
|
+
|
63
|
+
alias :mutation :resolver
|
64
|
+
|
65
|
+
# @return [Boolean] Apply tracing to this field? (Default: skip scalars, this is the override value)
|
66
|
+
attr_reader :trace
|
67
|
+
|
68
|
+
# @return [String, nil]
|
69
|
+
attr_accessor :subscription_scope
|
70
|
+
|
71
|
+
# Create a field instance from a list of arguments, keyword arguments, and a block.
|
72
|
+
#
|
73
|
+
# This method implements prioritization between the `resolver` or `mutation` defaults
|
74
|
+
# and the local overrides via other keywords.
|
75
|
+
#
|
76
|
+
# It also normalizes positional arguments into keywords for {Schema::Field#initialize}.
|
77
|
+
# @param resolver [Class] A {GraphQL::Schema::Resolver} class to use for field configuration
|
78
|
+
# @param mutation [Class] A {GraphQL::Schema::Mutation} class to use for field configuration
|
79
|
+
# @param subscription [Class] A {GraphQL::Schema::Subscription} class to use for field configuration
|
80
|
+
# @return [GraphQL::Schema:Field] an instance of `self
|
81
|
+
# @see {.initialize} for other options
|
82
|
+
def self.from_options(name = nil, type = nil, desc = nil, resolver: nil, mutation: nil, subscription: nil,**kwargs, &block)
|
83
|
+
if kwargs[:field]
|
84
|
+
if kwargs[:field].is_a?(GraphQL::Field) && kwargs[:field] == GraphQL::Types::Relay::NodeField.graphql_definition
|
85
|
+
GraphQL::Deprecation.warn("Legacy-style `GraphQL::Relay::Node.field` is being added to a class-based type. See `GraphQL::Types::Relay::NodeField` for a replacement.")
|
86
|
+
return GraphQL::Types::Relay::NodeField
|
87
|
+
elsif kwargs[:field].is_a?(GraphQL::Field) && kwargs[:field] == GraphQL::Types::Relay::NodesField.graphql_definition
|
88
|
+
GraphQL::Deprecation.warn("Legacy-style `GraphQL::Relay::Node.plural_field` is being added to a class-based type. See `GraphQL::Types::Relay::NodesField` for a replacement.")
|
89
|
+
return GraphQL::Types::Relay::NodesField
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
if (parent_config = resolver || mutation || subscription)
|
94
|
+
# Get the parent config, merge in local overrides
|
95
|
+
kwargs = parent_config.field_options.merge(kwargs)
|
96
|
+
# Add a reference to that parent class
|
97
|
+
kwargs[:resolver_class] = parent_config
|
98
|
+
end
|
99
|
+
|
100
|
+
if name
|
101
|
+
kwargs[:name] = name
|
102
|
+
end
|
103
|
+
|
104
|
+
if !type.nil?
|
105
|
+
if type.is_a?(GraphQL::Field)
|
106
|
+
raise ArgumentError, "A GraphQL::Field was passed as the second argument, use the `field:` keyword for this instead."
|
107
|
+
end
|
108
|
+
if desc
|
109
|
+
if kwargs[:description]
|
110
|
+
raise ArgumentError, "Provide description as a positional argument or `description:` keyword, but not both (#{desc.inspect}, #{kwargs[:description].inspect})"
|
111
|
+
end
|
112
|
+
|
113
|
+
kwargs[:description] = desc
|
114
|
+
kwargs[:type] = type
|
115
|
+
elsif (kwargs[:field] || kwargs[:function] || resolver || mutation) && type.is_a?(String)
|
116
|
+
# The return type should be copied from `field` or `function`, and the second positional argument is the description
|
117
|
+
kwargs[:description] = type
|
118
|
+
else
|
119
|
+
kwargs[:type] = type
|
120
|
+
end
|
121
|
+
if type.is_a?(Class) && type < GraphQL::Schema::Mutation
|
122
|
+
raise ArgumentError, "Use `field #{name.inspect}, mutation: Mutation, ...` to provide a mutation to this field instead"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
new(**kwargs, &block)
|
126
|
+
end
|
127
|
+
|
128
|
+
# Can be set with `connection: true|false` or inferred from a type name ending in `*Connection`
|
129
|
+
# @return [Boolean] if true, this field will be wrapped with Relay connection behavior
|
130
|
+
def connection?
|
131
|
+
if @connection.nil?
|
132
|
+
# Provide default based on type name
|
133
|
+
return_type_name = if (contains_type = @field || @function)
|
134
|
+
Member::BuildType.to_type_name(contains_type.type)
|
135
|
+
elsif @return_type_expr
|
136
|
+
Member::BuildType.to_type_name(@return_type_expr)
|
137
|
+
else
|
138
|
+
# As a last ditch, try to force loading the return type:
|
139
|
+
type.unwrap.name
|
140
|
+
end
|
141
|
+
@connection = return_type_name.end_with?("Connection")
|
142
|
+
else
|
143
|
+
@connection
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# @return [Boolean] if true, the return type's `.scope_items` method will be applied to this field's return value
|
148
|
+
def scoped?
|
149
|
+
if !@scope.nil?
|
150
|
+
# The default was overridden
|
151
|
+
@scope
|
152
|
+
else
|
153
|
+
@return_type_expr && (@return_type_expr.is_a?(Array) || (@return_type_expr.is_a?(String) && @return_type_expr.include?("[")) || connection?)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
# This extension is applied to fields when {#connection?} is true.
|
158
|
+
#
|
159
|
+
# You can override it in your base field definition.
|
160
|
+
# @return [Class] A {FieldExtension} subclass for implementing pagination behavior.
|
161
|
+
# @example Configuring a custom extension
|
162
|
+
# class Types::BaseField < GraphQL::Schema::Field
|
163
|
+
# connection_extension(MyCustomExtension)
|
164
|
+
# end
|
165
|
+
def self.connection_extension(new_extension_class = nil)
|
166
|
+
if new_extension_class
|
167
|
+
@connection_extension = new_extension_class
|
168
|
+
else
|
169
|
+
@connection_extension ||= find_inherited_value(:connection_extension, ConnectionExtension)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
# @return Boolean
|
174
|
+
attr_reader :relay_node_field
|
175
|
+
|
176
|
+
# @return [Boolean] Should we warn if this field's name conflicts with a built-in method?
|
177
|
+
def method_conflict_warning?
|
178
|
+
@method_conflict_warning
|
179
|
+
end
|
180
|
+
|
181
|
+
# @param name [Symbol] The underscore-cased version of this field name (will be camelized for the GraphQL API)
|
182
|
+
# @param type [Class, GraphQL::BaseType, Array] The return type of this field
|
183
|
+
# @param owner [Class] The type that this field belongs to
|
184
|
+
# @param null [Boolean] `true` if this field may return `null`, `false` if it is never `null`
|
185
|
+
# @param description [String] Field description
|
186
|
+
# @param deprecation_reason [String] If present, the field is marked "deprecated" with this message
|
187
|
+
# @param method [Symbol] The method to call on the underlying object to resolve this field (defaults to `name`)
|
188
|
+
# @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`)
|
189
|
+
# @param resolver_method [Symbol] The method on the type to call to resolve this field (defaults to `name`)
|
190
|
+
# @param connection [Boolean] `true` if this field should get automagic connection behavior; default is to infer by `*Connection` in the return type name
|
191
|
+
# @param connection_extension [Class] The extension to add, to implement connections. If `nil`, no extension is added.
|
192
|
+
# @param max_page_size [Integer, nil] For connections, the maximum number of items to return from this field, or `nil` to allow unlimited results.
|
193
|
+
# @param introspection [Boolean] If true, this field will be marked as `#introspection?` and the name may begin with `__`
|
194
|
+
# @param resolve [<#call(obj, args, ctx)>] **deprecated** for compatibility with <1.8.0
|
195
|
+
# @param field [GraphQL::Field, GraphQL::Schema::Field] **deprecated** for compatibility with <1.8.0
|
196
|
+
# @param function [GraphQL::Function] **deprecated** for compatibility with <1.8.0
|
197
|
+
# @param resolver_class [Class] (Private) A {Schema::Resolver} which this field was derived from. Use `resolver:` to create a field with a resolver.
|
198
|
+
# @param arguments [{String=>GraphQL::Schema::Argument, Hash}] Arguments for this field (may be added in the block, also)
|
199
|
+
# @param camelize [Boolean] If true, the field name will be camelized when building the schema
|
200
|
+
# @param complexity [Numeric] When provided, set the complexity for this field
|
201
|
+
# @param scope [Boolean] If true, the return type's `.scope_items` method will be called on the return value
|
202
|
+
# @param subscription_scope [Symbol, String] A key in `context` which will be used to scope subscription payloads
|
203
|
+
# @param extensions [Array<Class, Hash<Class => Object>>] Named extensions to apply to this field (see also {#extension})
|
204
|
+
# @param directives [Hash{Class => Hash}] Directives to apply to this field
|
205
|
+
# @param trace [Boolean] If true, a {GraphQL::Tracing} tracer will measure this scalar field
|
206
|
+
# @param broadcastable [Boolean] Whether or not this field can be distributed in subscription broadcasts
|
207
|
+
# @param ast_node [Language::Nodes::FieldDefinition, nil] If this schema was parsed from definition, this AST node defined the field
|
208
|
+
# @param method_conflict_warning [Boolean] If false, skip the warning if this field's method conflicts with a built-in method
|
209
|
+
# @param validates [Array<Hash>] Configurations for validating this field
|
210
|
+
# @param legacy_edge_class [Class, nil] (DEPRECATED) If present, pass this along to the legacy field definition
|
211
|
+
def initialize(type: nil, name: nil, owner: nil, null: true, field: nil, function: nil, description: nil, deprecation_reason: nil, method: nil, hash_key: nil, resolver_method: nil, resolve: nil, connection: nil, max_page_size: :not_given, scope: nil, introspection: false, camelize: true, trace: nil, complexity: 1, ast_node: nil, extras: EMPTY_ARRAY, extensions: EMPTY_ARRAY, connection_extension: self.class.connection_extension, resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, method_conflict_warning: true, broadcastable: nil, arguments: EMPTY_HASH, directives: EMPTY_HASH, validates: EMPTY_ARRAY, legacy_edge_class: nil, &definition_block)
|
212
|
+
if name.nil?
|
213
|
+
raise ArgumentError, "missing first `name` argument or keyword `name:`"
|
214
|
+
end
|
215
|
+
if !(field || function || resolver_class)
|
216
|
+
if type.nil?
|
217
|
+
raise ArgumentError, "missing second `type` argument or keyword `type:`"
|
218
|
+
end
|
219
|
+
end
|
220
|
+
if (field || function || resolve) && extras.any?
|
221
|
+
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:`"
|
222
|
+
end
|
223
|
+
@original_name = name
|
224
|
+
name_s = -name.to_s
|
225
|
+
@underscored_name = -Member::BuildType.underscore(name_s)
|
226
|
+
@name = -(camelize ? Member::BuildType.camelize(name_s) : name_s)
|
227
|
+
@description = description
|
228
|
+
if field.is_a?(GraphQL::Schema::Field)
|
229
|
+
raise ArgumentError, "Instead of passing a field as `field:`, use `add_field(field)` to add an already-defined field."
|
230
|
+
else
|
231
|
+
@field = field
|
232
|
+
end
|
233
|
+
@function = function
|
234
|
+
@resolve = resolve
|
235
|
+
self.deprecation_reason = deprecation_reason
|
236
|
+
|
237
|
+
if method && hash_key
|
238
|
+
raise ArgumentError, "Provide `method:` _or_ `hash_key:`, not both. (called with: `method: #{method.inspect}, hash_key: #{hash_key.inspect}`)"
|
239
|
+
end
|
240
|
+
|
241
|
+
if resolver_method
|
242
|
+
if method
|
243
|
+
raise ArgumentError, "Provide `method:` _or_ `resolver_method:`, not both. (called with: `method: #{method.inspect}, resolver_method: #{resolver_method.inspect}`)"
|
244
|
+
end
|
245
|
+
|
246
|
+
if hash_key
|
247
|
+
raise ArgumentError, "Provide `hash_key:` _or_ `resolver_method:`, not both. (called with: `hash_key: #{hash_key.inspect}, resolver_method: #{resolver_method.inspect}`)"
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
# TODO: I think non-string/symbol hash keys are wrongly normalized (eg `1` will not work)
|
252
|
+
method_name = method || hash_key || name_s
|
253
|
+
resolver_method ||= name_s.to_sym
|
254
|
+
|
255
|
+
@method_str = -method_name.to_s
|
256
|
+
@method_sym = method_name.to_sym
|
257
|
+
@resolver_method = resolver_method
|
258
|
+
@complexity = complexity
|
259
|
+
@return_type_expr = type
|
260
|
+
@return_type_null = null
|
261
|
+
@connection = connection
|
262
|
+
@has_max_page_size = max_page_size != :not_given
|
263
|
+
@max_page_size = max_page_size == :not_given ? nil : max_page_size
|
264
|
+
@introspection = introspection
|
265
|
+
@extras = extras
|
266
|
+
@broadcastable = broadcastable
|
267
|
+
@resolver_class = resolver_class
|
268
|
+
@scope = scope
|
269
|
+
@trace = trace
|
270
|
+
@relay_node_field = relay_node_field
|
271
|
+
@relay_nodes_field = relay_nodes_field
|
272
|
+
@ast_node = ast_node
|
273
|
+
@method_conflict_warning = method_conflict_warning
|
274
|
+
@legacy_edge_class = legacy_edge_class
|
275
|
+
|
276
|
+
arguments.each do |name, arg|
|
277
|
+
case arg
|
278
|
+
when Hash
|
279
|
+
argument(name: name, **arg)
|
280
|
+
when GraphQL::Schema::Argument
|
281
|
+
add_argument(arg)
|
282
|
+
when Array
|
283
|
+
arg.each { |a| add_argument(a) }
|
284
|
+
else
|
285
|
+
raise ArgumentError, "Unexpected argument config (#{arg.class}): #{arg.inspect}"
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
@owner = owner
|
290
|
+
@subscription_scope = subscription_scope
|
291
|
+
|
292
|
+
@extensions = EMPTY_ARRAY
|
293
|
+
# This should run before connection extension,
|
294
|
+
# but should it run after the definition block?
|
295
|
+
if scoped?
|
296
|
+
self.extension(ScopeExtension)
|
297
|
+
end
|
298
|
+
|
299
|
+
# The problem with putting this after the definition_block
|
300
|
+
# is that it would override arguments
|
301
|
+
if connection? && connection_extension
|
302
|
+
self.extension(connection_extension)
|
303
|
+
end
|
304
|
+
|
305
|
+
# Do this last so we have as much context as possible when initializing them:
|
306
|
+
if extensions.any?
|
307
|
+
self.extensions(extensions)
|
308
|
+
end
|
309
|
+
|
310
|
+
if directives.any?
|
311
|
+
directives.each do |(dir_class, options)|
|
312
|
+
self.directive(dir_class, **options)
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
self.validates(validates)
|
317
|
+
|
318
|
+
if definition_block
|
319
|
+
if definition_block.arity == 1
|
320
|
+
yield self
|
321
|
+
else
|
322
|
+
instance_eval(&definition_block)
|
323
|
+
end
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
# If true, subscription updates with this field can be shared between viewers
|
328
|
+
# @return [Boolean, nil]
|
329
|
+
# @see GraphQL::Subscriptions::BroadcastAnalyzer
|
330
|
+
def broadcastable?
|
331
|
+
@broadcastable
|
332
|
+
end
|
333
|
+
|
334
|
+
# @param text [String]
|
335
|
+
# @return [String]
|
336
|
+
def description(text = nil)
|
337
|
+
if text
|
338
|
+
@description = text
|
339
|
+
else
|
340
|
+
@description
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
# Read extension instances from this field,
|
345
|
+
# or add new classes/options to be initialized on this field.
|
346
|
+
# Extensions are executed in the order they are added.
|
347
|
+
#
|
348
|
+
# @example adding an extension
|
349
|
+
# extensions([MyExtensionClass])
|
350
|
+
#
|
351
|
+
# @example adding multiple extensions
|
352
|
+
# extensions([MyExtensionClass, AnotherExtensionClass])
|
353
|
+
#
|
354
|
+
# @example adding an extension with options
|
355
|
+
# extensions([MyExtensionClass, { AnotherExtensionClass => { filter: true } }])
|
356
|
+
#
|
357
|
+
# @param extensions [Array<Class, Hash<Class => Object>>] Add extensions to this field. For hash elements, only the first key/value is used.
|
358
|
+
# @return [Array<GraphQL::Schema::FieldExtension>] extensions to apply to this field
|
359
|
+
def extensions(new_extensions = nil)
|
360
|
+
if new_extensions.nil?
|
361
|
+
# Read the value
|
362
|
+
@extensions
|
363
|
+
else
|
364
|
+
if @extensions.frozen?
|
365
|
+
@extensions = @extensions.dup
|
366
|
+
end
|
367
|
+
new_extensions.each do |extension|
|
368
|
+
if extension.is_a?(Hash)
|
369
|
+
extension = extension.to_a[0]
|
370
|
+
extension_class, options = *extension
|
371
|
+
@extensions << extension_class.new(field: self, options: options)
|
372
|
+
else
|
373
|
+
extension_class = extension
|
374
|
+
@extensions << extension_class.new(field: self, options: nil)
|
375
|
+
end
|
376
|
+
end
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
380
|
+
# Add `extension` to this field, initialized with `options` if provided.
|
381
|
+
#
|
382
|
+
# @example adding an extension
|
383
|
+
# extension(MyExtensionClass)
|
384
|
+
#
|
385
|
+
# @example adding an extension with options
|
386
|
+
# extension(MyExtensionClass, filter: true)
|
387
|
+
#
|
388
|
+
# @param extension [Class] subclass of {Schema::Fieldextension}
|
389
|
+
# @param options [Object] if provided, given as `options:` when initializing `extension`.
|
390
|
+
def extension(extension, options = nil)
|
391
|
+
extensions([{extension => options}])
|
392
|
+
end
|
393
|
+
|
394
|
+
# Read extras (as symbols) from this field,
|
395
|
+
# or add new extras to be opted into by this field's resolver.
|
396
|
+
#
|
397
|
+
# @param new_extras [Array<Symbol>] Add extras to this field
|
398
|
+
# @return [Array<Symbol>]
|
399
|
+
def extras(new_extras = nil)
|
400
|
+
if new_extras.nil?
|
401
|
+
# Read the value
|
402
|
+
@extras
|
403
|
+
else
|
404
|
+
if @extras.frozen?
|
405
|
+
@extras = @extras.dup
|
406
|
+
end
|
407
|
+
# Append to the set of extras on this field
|
408
|
+
@extras.concat(new_extras)
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
def calculate_complexity(query:, nodes:, child_complexity:)
|
413
|
+
if respond_to?(:complexity_for)
|
414
|
+
lookahead = GraphQL::Execution::Lookahead.new(query: query, field: self, ast_nodes: nodes, owner_type: owner)
|
415
|
+
complexity_for(child_complexity: child_complexity, query: query, lookahead: lookahead)
|
416
|
+
elsif connection?
|
417
|
+
arguments = query.arguments_for(nodes.first, self)
|
418
|
+
max_possible_page_size = nil
|
419
|
+
if arguments[:first]
|
420
|
+
max_possible_page_size = arguments[:first]
|
421
|
+
end
|
422
|
+
if arguments[:last] && (max_possible_page_size.nil? || arguments[:last] > max_possible_page_size)
|
423
|
+
max_possible_page_size = arguments[:last]
|
424
|
+
end
|
425
|
+
|
426
|
+
if max_possible_page_size.nil?
|
427
|
+
max_possible_page_size = max_page_size || query.schema.default_max_page_size
|
428
|
+
end
|
429
|
+
|
430
|
+
if max_possible_page_size.nil?
|
431
|
+
raise GraphQL::Error, "Can't calculate complexity for #{path}, no `first:`, `last:`, `max_page_size` or `default_max_page_size`"
|
432
|
+
else
|
433
|
+
metadata_complexity = 0
|
434
|
+
lookahead = GraphQL::Execution::Lookahead.new(query: query, field: self, ast_nodes: nodes, owner_type: owner)
|
435
|
+
|
436
|
+
if (page_info_lookahead = lookahead.selection(:page_info)).selected?
|
437
|
+
metadata_complexity += 1 # pageInfo
|
438
|
+
metadata_complexity += page_info_lookahead.selections.size # subfields
|
439
|
+
end
|
440
|
+
|
441
|
+
if lookahead.selects?(:total) || lookahead.selects?(:total_count) || lookahead.selects?(:count)
|
442
|
+
metadata_complexity += 1
|
443
|
+
end
|
444
|
+
# Possible bug: selections on `edges` and `nodes` are _both_ multiplied here. Should they be?
|
445
|
+
items_complexity = child_complexity - metadata_complexity
|
446
|
+
# Add 1 for _this_ field
|
447
|
+
1 + (max_possible_page_size * items_complexity) + metadata_complexity
|
448
|
+
end
|
449
|
+
else
|
450
|
+
defined_complexity = complexity
|
451
|
+
case defined_complexity
|
452
|
+
when Proc
|
453
|
+
arguments = query.arguments_for(nodes.first, self)
|
454
|
+
defined_complexity.call(query.context, arguments.keyword_arguments, child_complexity)
|
455
|
+
when Numeric
|
456
|
+
defined_complexity + child_complexity
|
457
|
+
else
|
458
|
+
raise("Invalid complexity: #{defined_complexity.inspect} on #{path} (#{inspect})")
|
459
|
+
end
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
def complexity(new_complexity = nil)
|
464
|
+
case new_complexity
|
465
|
+
when Proc
|
466
|
+
if new_complexity.parameters.size != 3
|
467
|
+
fail(
|
468
|
+
"A complexity proc should always accept 3 parameters: ctx, args, child_complexity. "\
|
469
|
+
"E.g.: complexity ->(ctx, args, child_complexity) { child_complexity * args[:limit] }"
|
470
|
+
)
|
471
|
+
else
|
472
|
+
@complexity = new_complexity
|
473
|
+
end
|
474
|
+
when Numeric
|
475
|
+
@complexity = new_complexity
|
476
|
+
when nil
|
477
|
+
@complexity
|
478
|
+
else
|
479
|
+
raise("Invalid complexity: #{new_complexity.inspect} on #{@name}")
|
480
|
+
end
|
481
|
+
end
|
482
|
+
|
483
|
+
# @return [Boolean] True if this field's {#max_page_size} should override the schema default.
|
484
|
+
def has_max_page_size?
|
485
|
+
@has_max_page_size
|
486
|
+
end
|
487
|
+
|
488
|
+
# @return [Integer, nil] Applied to connections if {#has_max_page_size?}
|
489
|
+
attr_reader :max_page_size
|
490
|
+
|
491
|
+
# @return [GraphQL::Field]
|
492
|
+
def to_graphql
|
493
|
+
field_defn = if @field
|
494
|
+
@field.dup
|
495
|
+
elsif @function
|
496
|
+
GraphQL::Function.build_field(@function)
|
497
|
+
else
|
498
|
+
GraphQL::Field.new
|
499
|
+
end
|
500
|
+
|
501
|
+
field_defn.name = @name
|
502
|
+
if @return_type_expr
|
503
|
+
field_defn.type = -> { type }
|
504
|
+
end
|
505
|
+
|
506
|
+
if @description
|
507
|
+
field_defn.description = @description
|
508
|
+
end
|
509
|
+
|
510
|
+
if self.deprecation_reason
|
511
|
+
field_defn.deprecation_reason = self.deprecation_reason
|
512
|
+
end
|
513
|
+
|
514
|
+
if @resolver_class
|
515
|
+
if @resolver_class < GraphQL::Schema::Mutation
|
516
|
+
field_defn.mutation = @resolver_class
|
517
|
+
end
|
518
|
+
field_defn.metadata[:resolver] = @resolver_class
|
519
|
+
end
|
520
|
+
|
521
|
+
if !@trace.nil?
|
522
|
+
field_defn.trace = @trace
|
523
|
+
end
|
524
|
+
|
525
|
+
if @relay_node_field
|
526
|
+
field_defn.relay_node_field = @relay_node_field
|
527
|
+
end
|
528
|
+
|
529
|
+
if @relay_nodes_field
|
530
|
+
field_defn.relay_nodes_field = @relay_nodes_field
|
531
|
+
end
|
532
|
+
|
533
|
+
if @legacy_edge_class
|
534
|
+
field_defn.edge_class = @legacy_edge_class
|
535
|
+
end
|
536
|
+
|
537
|
+
field_defn.resolve = self.method(:resolve_field)
|
538
|
+
field_defn.connection = connection?
|
539
|
+
field_defn.connection_max_page_size = max_page_size
|
540
|
+
field_defn.introspection = @introspection
|
541
|
+
field_defn.complexity = @complexity
|
542
|
+
field_defn.subscription_scope = @subscription_scope
|
543
|
+
field_defn.ast_node = ast_node
|
544
|
+
|
545
|
+
all_argument_definitions.each do |defn|
|
546
|
+
arg_graphql = defn.to_graphql
|
547
|
+
field_defn.arguments[arg_graphql.name] = arg_graphql # rubocop:disable Development/ContextIsPassedCop -- legacy-related
|
548
|
+
end
|
549
|
+
|
550
|
+
# Support a passed-in proc, one way or another
|
551
|
+
@resolve_proc = if @resolve
|
552
|
+
@resolve
|
553
|
+
elsif @function
|
554
|
+
@function
|
555
|
+
elsif @field
|
556
|
+
@field.resolve_proc
|
557
|
+
end
|
558
|
+
|
559
|
+
# Ok, `self` isn't a class, but this is for consistency with the classes
|
560
|
+
field_defn.metadata[:type_class] = self
|
561
|
+
field_defn.arguments_class = GraphQL::Query::Arguments.construct_arguments_class(field_defn)
|
562
|
+
field_defn
|
563
|
+
end
|
564
|
+
|
565
|
+
class MissingReturnTypeError < GraphQL::Error; end
|
566
|
+
attr_writer :type
|
567
|
+
|
568
|
+
def type
|
569
|
+
@type ||= if @function
|
570
|
+
Member::BuildType.parse_type(@function.type, null: false)
|
571
|
+
elsif @field
|
572
|
+
Member::BuildType.parse_type(@field.type, null: false)
|
573
|
+
elsif @return_type_expr.nil?
|
574
|
+
# Not enough info to determine type
|
575
|
+
message = "Can't determine the return type for #{self.path}"
|
576
|
+
if @resolver_class
|
577
|
+
message += " (it has `resolver: #{@resolver_class}`, consider configuration a `type ...` for that class)"
|
578
|
+
end
|
579
|
+
raise MissingReturnTypeError, message
|
580
|
+
else
|
581
|
+
Member::BuildType.parse_type(@return_type_expr, null: @return_type_null)
|
582
|
+
end
|
583
|
+
rescue GraphQL::Schema::InvalidDocumentError, MissingReturnTypeError => err
|
584
|
+
# Let this propagate up
|
585
|
+
raise err
|
586
|
+
rescue StandardError => err
|
587
|
+
raise MissingReturnTypeError, "Failed to build return type for #{@owner.graphql_name}.#{name} from #{@return_type_expr.inspect}: (#{err.class}) #{err.message}", err.backtrace
|
588
|
+
end
|
589
|
+
|
590
|
+
def visible?(context)
|
591
|
+
if @resolver_class
|
592
|
+
@resolver_class.visible?(context)
|
593
|
+
else
|
594
|
+
true
|
595
|
+
end
|
596
|
+
end
|
597
|
+
|
598
|
+
def accessible?(context)
|
599
|
+
if @resolver_class
|
600
|
+
@resolver_class.accessible?(context)
|
601
|
+
else
|
602
|
+
true
|
603
|
+
end
|
604
|
+
end
|
605
|
+
|
606
|
+
def authorized?(object, args, context)
|
607
|
+
if @resolver_class
|
608
|
+
# The resolver will check itself during `resolve()`
|
609
|
+
@resolver_class.authorized?(object, context)
|
610
|
+
else
|
611
|
+
if (arg_values = context[:current_arguments])
|
612
|
+
# ^^ that's provided by the interpreter at runtime, and includes info about whether the default value was used or not.
|
613
|
+
using_arg_values = true
|
614
|
+
arg_values = arg_values.argument_values
|
615
|
+
else
|
616
|
+
arg_values = args
|
617
|
+
using_arg_values = false
|
618
|
+
end
|
619
|
+
# Faster than `.any?`
|
620
|
+
arguments(context).each_value do |arg|
|
621
|
+
arg_key = arg.keyword
|
622
|
+
if arg_values.key?(arg_key)
|
623
|
+
arg_value = arg_values[arg_key]
|
624
|
+
if using_arg_values
|
625
|
+
if arg_value.default_used?
|
626
|
+
# pass -- no auth required for default used
|
627
|
+
next
|
628
|
+
else
|
629
|
+
application_arg_value = arg_value.value
|
630
|
+
if application_arg_value.is_a?(GraphQL::Execution::Interpreter::Arguments)
|
631
|
+
application_arg_value.keyword_arguments
|
632
|
+
end
|
633
|
+
end
|
634
|
+
else
|
635
|
+
application_arg_value = arg_value
|
636
|
+
end
|
637
|
+
|
638
|
+
if !arg.authorized?(object, application_arg_value, context)
|
639
|
+
return false
|
640
|
+
end
|
641
|
+
end
|
642
|
+
end
|
643
|
+
true
|
644
|
+
end
|
645
|
+
end
|
646
|
+
|
647
|
+
# Implement {GraphQL::Field}'s resolve API.
|
648
|
+
#
|
649
|
+
# Eventually, we might hook up field instances to execution in another way. TBD.
|
650
|
+
# @see #resolve for how the interpreter hooks up to it
|
651
|
+
def resolve_field(obj, args, ctx)
|
652
|
+
ctx.schema.after_lazy(obj) do |after_obj|
|
653
|
+
# First, apply auth ...
|
654
|
+
query_ctx = ctx.query.context
|
655
|
+
# Some legacy fields can have `nil` here, not exactly sure why.
|
656
|
+
# @see https://github.com/rmosolgo/graphql-ruby/issues/1990 before removing
|
657
|
+
inner_obj = after_obj && after_obj.object
|
658
|
+
ctx.schema.after_lazy(to_ruby_args(after_obj, args, ctx)) do |ruby_args|
|
659
|
+
if authorized?(inner_obj, ruby_args, query_ctx)
|
660
|
+
# Then if it passed, resolve the field
|
661
|
+
if @resolve_proc
|
662
|
+
# Might be nil, still want to call the func in that case
|
663
|
+
with_extensions(inner_obj, ruby_args, query_ctx) do |extended_obj, extended_args|
|
664
|
+
# Pass the GraphQL args here for compatibility:
|
665
|
+
@resolve_proc.call(extended_obj, args, ctx)
|
666
|
+
end
|
667
|
+
else
|
668
|
+
public_send_field(after_obj, ruby_args, query_ctx)
|
669
|
+
end
|
670
|
+
else
|
671
|
+
err = GraphQL::UnauthorizedFieldError.new(object: inner_obj, type: obj.class, context: ctx, field: self)
|
672
|
+
query_ctx.schema.unauthorized_field(err)
|
673
|
+
end
|
674
|
+
end
|
675
|
+
end
|
676
|
+
end
|
677
|
+
|
678
|
+
# This method is called by the interpreter for each field.
|
679
|
+
# You can extend it in your base field classes.
|
680
|
+
# @param object [GraphQL::Schema::Object] An instance of some type class, wrapping an application object
|
681
|
+
# @param args [Hash] A symbol-keyed hash of Ruby keyword arguments. (Empty if no args)
|
682
|
+
# @param ctx [GraphQL::Query::Context]
|
683
|
+
def resolve(object, args, ctx)
|
684
|
+
if @resolve_proc
|
685
|
+
raise "Can't run resolve proc for #{path} when using GraphQL::Execution::Interpreter"
|
686
|
+
end
|
687
|
+
begin
|
688
|
+
# Unwrap the GraphQL object to get the application object.
|
689
|
+
application_object = object.object
|
690
|
+
|
691
|
+
Schema::Validator.validate!(validators, application_object, ctx, args)
|
692
|
+
|
693
|
+
ctx.schema.after_lazy(self.authorized?(application_object, args, ctx)) do |is_authorized|
|
694
|
+
if is_authorized
|
695
|
+
public_send_field(object, args, ctx)
|
696
|
+
else
|
697
|
+
raise GraphQL::UnauthorizedFieldError.new(object: application_object, type: object.class, context: ctx, field: self)
|
698
|
+
end
|
699
|
+
end
|
700
|
+
rescue GraphQL::UnauthorizedFieldError => err
|
701
|
+
err.field ||= self
|
702
|
+
ctx.schema.unauthorized_field(err)
|
703
|
+
rescue GraphQL::UnauthorizedError => err
|
704
|
+
ctx.schema.unauthorized_object(err)
|
705
|
+
end
|
706
|
+
rescue GraphQL::ExecutionError => err
|
707
|
+
err
|
708
|
+
end
|
709
|
+
|
710
|
+
# @param ctx [GraphQL::Query::Context::FieldResolutionContext]
|
711
|
+
def fetch_extra(extra_name, ctx)
|
712
|
+
if extra_name != :path && extra_name != :ast_node && respond_to?(extra_name)
|
713
|
+
self.public_send(extra_name)
|
714
|
+
elsif ctx.respond_to?(extra_name)
|
715
|
+
ctx.public_send(extra_name)
|
716
|
+
else
|
717
|
+
raise GraphQL::RequiredImplementationMissingError, "Unknown field extra for #{self.path}: #{extra_name.inspect}"
|
718
|
+
end
|
719
|
+
end
|
720
|
+
|
721
|
+
private
|
722
|
+
|
723
|
+
NO_ARGS = {}.freeze
|
724
|
+
|
725
|
+
# Convert a GraphQL arguments instance into a Ruby-style hash.
|
726
|
+
#
|
727
|
+
# @param obj [GraphQL::Schema::Object] The object where this field is being resolved
|
728
|
+
# @param graphql_args [GraphQL::Query::Arguments]
|
729
|
+
# @param field_ctx [GraphQL::Query::Context::FieldResolutionContext]
|
730
|
+
# @return [Hash<Symbol => Any>]
|
731
|
+
def to_ruby_args(obj, graphql_args, field_ctx)
|
732
|
+
if graphql_args.any? || @extras.any?
|
733
|
+
# Splat the GraphQL::Arguments to Ruby keyword arguments
|
734
|
+
ruby_kwargs = graphql_args.to_kwargs
|
735
|
+
maybe_lazies = []
|
736
|
+
# Apply any `prepare` methods. Not great code organization, can this go somewhere better?
|
737
|
+
arguments(field_ctx).each do |name, arg_defn|
|
738
|
+
ruby_kwargs_key = arg_defn.keyword
|
739
|
+
|
740
|
+
if ruby_kwargs.key?(ruby_kwargs_key)
|
741
|
+
loads = arg_defn.loads
|
742
|
+
value = ruby_kwargs[ruby_kwargs_key]
|
743
|
+
loaded_value = if loads && !arg_defn.from_resolver?
|
744
|
+
if arg_defn.type.list?
|
745
|
+
loaded_values = value.map { |val| load_application_object(arg_defn, loads, val, field_ctx.query.context) }
|
746
|
+
field_ctx.schema.after_any_lazies(loaded_values) { |result| result }
|
747
|
+
else
|
748
|
+
load_application_object(arg_defn, loads, value, field_ctx.query.context)
|
749
|
+
end
|
750
|
+
elsif arg_defn.type.list? && value.is_a?(Array)
|
751
|
+
field_ctx.schema.after_any_lazies(value, &:itself)
|
752
|
+
else
|
753
|
+
value
|
754
|
+
end
|
755
|
+
|
756
|
+
maybe_lazies << field_ctx.schema.after_lazy(loaded_value) do |loaded_value|
|
757
|
+
prepared_value = if arg_defn.prepare
|
758
|
+
arg_defn.prepare_value(obj, loaded_value)
|
759
|
+
else
|
760
|
+
loaded_value
|
761
|
+
end
|
762
|
+
|
763
|
+
ruby_kwargs[ruby_kwargs_key] = prepared_value
|
764
|
+
end
|
765
|
+
end
|
766
|
+
end
|
767
|
+
|
768
|
+
@extras.each do |extra_arg|
|
769
|
+
ruby_kwargs[extra_arg] = fetch_extra(extra_arg, field_ctx)
|
770
|
+
end
|
771
|
+
|
772
|
+
field_ctx.schema.after_any_lazies(maybe_lazies) do
|
773
|
+
ruby_kwargs
|
774
|
+
end
|
775
|
+
else
|
776
|
+
NO_ARGS
|
777
|
+
end
|
778
|
+
end
|
779
|
+
|
780
|
+
def public_send_field(unextended_obj, unextended_ruby_kwargs, query_ctx)
|
781
|
+
with_extensions(unextended_obj, unextended_ruby_kwargs, query_ctx) do |obj, ruby_kwargs|
|
782
|
+
if @resolver_class
|
783
|
+
if obj.is_a?(GraphQL::Schema::Object)
|
784
|
+
obj = obj.object
|
785
|
+
end
|
786
|
+
obj = @resolver_class.new(object: obj, context: query_ctx, field: self)
|
787
|
+
end
|
788
|
+
|
789
|
+
# Find a way to resolve this field, checking:
|
790
|
+
#
|
791
|
+
# - A method on the type instance;
|
792
|
+
# - Hash keys, if the wrapped object is a hash;
|
793
|
+
# - A method on the wrapped object;
|
794
|
+
# - Or, raise not implemented.
|
795
|
+
#
|
796
|
+
if obj.respond_to?(@resolver_method)
|
797
|
+
# Call the method with kwargs, if there are any
|
798
|
+
if ruby_kwargs.any?
|
799
|
+
obj.public_send(@resolver_method, **ruby_kwargs)
|
800
|
+
else
|
801
|
+
obj.public_send(@resolver_method)
|
802
|
+
end
|
803
|
+
elsif obj.object.is_a?(Hash)
|
804
|
+
inner_object = obj.object
|
805
|
+
if inner_object.key?(@method_sym)
|
806
|
+
inner_object[@method_sym]
|
807
|
+
else
|
808
|
+
inner_object[@method_str]
|
809
|
+
end
|
810
|
+
elsif obj.object.respond_to?(@method_sym)
|
811
|
+
if ruby_kwargs.any?
|
812
|
+
obj.object.public_send(@method_sym, **ruby_kwargs)
|
813
|
+
else
|
814
|
+
obj.object.public_send(@method_sym)
|
815
|
+
end
|
816
|
+
else
|
817
|
+
raise <<-ERR
|
818
|
+
Failed to implement #{@owner.graphql_name}.#{@name}, tried:
|
819
|
+
|
820
|
+
- `#{obj.class}##{@resolver_method}`, which did not exist
|
821
|
+
- `#{obj.object.class}##{@method_sym}`, which did not exist
|
822
|
+
- Looking up hash key `#{@method_sym.inspect}` or `#{@method_str.inspect}` on `#{obj.object}`, but it wasn't a Hash
|
823
|
+
|
824
|
+
To implement this field, define one of the methods above (and check for typos)
|
825
|
+
ERR
|
826
|
+
end
|
827
|
+
end
|
828
|
+
end
|
829
|
+
|
830
|
+
# Wrap execution with hooks.
|
831
|
+
# Written iteratively to avoid big stack traces.
|
832
|
+
# @return [Object] Whatever the
|
833
|
+
def with_extensions(obj, args, ctx)
|
834
|
+
if @extensions.empty?
|
835
|
+
yield(obj, args)
|
836
|
+
else
|
837
|
+
# This is a hack to get the _last_ value for extended obj and args,
|
838
|
+
# in case one of the extensions doesn't `yield`.
|
839
|
+
# (There's another implementation that uses multiple-return, but I'm wary of the perf cost of the extra arrays)
|
840
|
+
extended = { args: args, obj: obj, memos: nil }
|
841
|
+
value = run_extensions_before_resolve(obj, args, ctx, extended) do |obj, args|
|
842
|
+
yield(obj, args)
|
843
|
+
end
|
844
|
+
|
845
|
+
extended_obj = extended[:obj]
|
846
|
+
extended_args = extended[:args]
|
847
|
+
memos = extended[:memos] || EMPTY_HASH
|
848
|
+
|
849
|
+
ctx.schema.after_lazy(value) do |resolved_value|
|
850
|
+
idx = 0
|
851
|
+
@extensions.each do |ext|
|
852
|
+
memo = memos[idx]
|
853
|
+
# TODO after_lazy?
|
854
|
+
resolved_value = ext.after_resolve(object: extended_obj, arguments: extended_args, context: ctx, value: resolved_value, memo: memo)
|
855
|
+
idx += 1
|
856
|
+
end
|
857
|
+
resolved_value
|
858
|
+
end
|
859
|
+
end
|
860
|
+
end
|
861
|
+
|
862
|
+
def run_extensions_before_resolve(obj, args, ctx, extended, idx: 0)
|
863
|
+
extension = @extensions[idx]
|
864
|
+
if extension
|
865
|
+
extension.resolve(object: obj, arguments: args, context: ctx) do |extended_obj, extended_args, memo|
|
866
|
+
if memo
|
867
|
+
memos = extended[:memos] ||= {}
|
868
|
+
memos[idx] = memo
|
869
|
+
end
|
870
|
+
extended[:obj] = extended_obj
|
871
|
+
extended[:args] = extended_args
|
872
|
+
run_extensions_before_resolve(extended_obj, extended_args, ctx, extended, idx: idx + 1) { |o, a| yield(o, a) }
|
873
|
+
end
|
874
|
+
else
|
875
|
+
yield(obj, args)
|
876
|
+
end
|
877
|
+
end
|
878
|
+
end
|
879
|
+
end
|
880
|
+
end
|