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,122 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "fiber"
|
3
|
+
require "graphql/execution/interpreter/argument_value"
|
4
|
+
require "graphql/execution/interpreter/arguments"
|
5
|
+
require "graphql/execution/interpreter/arguments_cache"
|
6
|
+
require "graphql/execution/interpreter/execution_errors"
|
7
|
+
require "graphql/execution/interpreter/runtime"
|
8
|
+
require "graphql/execution/interpreter/resolve"
|
9
|
+
require "graphql/execution/interpreter/handles_raw_value"
|
10
|
+
|
11
|
+
module GraphQL
|
12
|
+
module Execution
|
13
|
+
class Interpreter
|
14
|
+
def initialize
|
15
|
+
end
|
16
|
+
|
17
|
+
# Support `Executor` :S
|
18
|
+
def execute(_operation, _root_type, query)
|
19
|
+
runtime = evaluate(query)
|
20
|
+
sync_lazies(query: query)
|
21
|
+
runtime.final_result
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.use(schema_class)
|
25
|
+
if schema_class.interpreter?
|
26
|
+
definition_line = caller(2, 1).first
|
27
|
+
GraphQL::Deprecation.warn("GraphQL::Execution::Interpreter is now the default; remove `use GraphQL::Execution::Interpreter` from the schema definition (#{definition_line})")
|
28
|
+
else
|
29
|
+
schema_class.query_execution_strategy(self)
|
30
|
+
schema_class.mutation_execution_strategy(self)
|
31
|
+
schema_class.subscription_execution_strategy(self)
|
32
|
+
schema_class.add_subscription_extension_if_necessary
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.begin_multiplex(multiplex)
|
37
|
+
# Since this is basically the batching context,
|
38
|
+
# share it for a whole multiplex
|
39
|
+
multiplex.context[:interpreter_instance] ||= self.new
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.begin_query(query, multiplex)
|
43
|
+
# The batching context is shared by the multiplex,
|
44
|
+
# so fetch it out and use that instance.
|
45
|
+
interpreter =
|
46
|
+
query.context.namespace(:interpreter)[:interpreter_instance] =
|
47
|
+
multiplex.context[:interpreter_instance]
|
48
|
+
interpreter.evaluate(query)
|
49
|
+
query
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.finish_multiplex(_results, multiplex)
|
53
|
+
interpreter = multiplex.context[:interpreter_instance]
|
54
|
+
interpreter.sync_lazies(multiplex: multiplex)
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.finish_query(query, _multiplex)
|
58
|
+
{
|
59
|
+
"data" => query.context.namespace(:interpreter)[:runtime].final_result
|
60
|
+
}
|
61
|
+
end
|
62
|
+
|
63
|
+
# Run the eager part of `query`
|
64
|
+
# @return {Interpreter::Runtime}
|
65
|
+
def evaluate(query)
|
66
|
+
# Although queries in a multiplex _share_ an Interpreter instance,
|
67
|
+
# they also have another item of state, which is private to that query
|
68
|
+
# in particular, assign it here:
|
69
|
+
runtime = Runtime.new(query: query)
|
70
|
+
query.context.namespace(:interpreter)[:runtime] = runtime
|
71
|
+
|
72
|
+
query.trace("execute_query", {query: query}) do
|
73
|
+
runtime.run_eager
|
74
|
+
end
|
75
|
+
|
76
|
+
runtime
|
77
|
+
end
|
78
|
+
|
79
|
+
# Run the lazy part of `query` or `multiplex`.
|
80
|
+
# @return [void]
|
81
|
+
def sync_lazies(query: nil, multiplex: nil)
|
82
|
+
tracer = query || multiplex
|
83
|
+
if query.nil? && multiplex.queries.length == 1
|
84
|
+
query = multiplex.queries[0]
|
85
|
+
end
|
86
|
+
queries = multiplex ? multiplex.queries : [query]
|
87
|
+
final_values = queries.map do |query|
|
88
|
+
runtime = query.context.namespace(:interpreter)[:runtime]
|
89
|
+
# it might not be present if the query has an error
|
90
|
+
runtime ? runtime.final_result : nil
|
91
|
+
end
|
92
|
+
final_values.compact!
|
93
|
+
tracer.trace("execute_query_lazy", {multiplex: multiplex, query: query}) do
|
94
|
+
Interpreter::Resolve.resolve_all(final_values, multiplex.dataloader)
|
95
|
+
end
|
96
|
+
queries.each do |query|
|
97
|
+
runtime = query.context.namespace(:interpreter)[:runtime]
|
98
|
+
if runtime
|
99
|
+
runtime.delete_interpreter_context(:current_path)
|
100
|
+
runtime.delete_interpreter_context(:current_field)
|
101
|
+
runtime.delete_interpreter_context(:current_object)
|
102
|
+
runtime.delete_interpreter_context(:current_arguments)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
nil
|
106
|
+
end
|
107
|
+
|
108
|
+
class ListResultFailedError < GraphQL::Error
|
109
|
+
def initialize(value:, path:, field:)
|
110
|
+
message = "Failed to build a GraphQL list result for field `#{field.path}` at path `#{path.join(".")}`.\n".dup
|
111
|
+
|
112
|
+
message << "Expected `#{value.inspect}` (#{value.class}) to implement `.each` to satisfy the GraphQL return type `#{field.type.to_type_signature}`.\n"
|
113
|
+
|
114
|
+
if field.connection?
|
115
|
+
message << "\nThis field was treated as a Relay-style connection; add `connection: false` to the `field(...)` to disable this behavior."
|
116
|
+
end
|
117
|
+
super(message)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'thread'
|
3
|
+
begin
|
4
|
+
require 'concurrent'
|
5
|
+
rescue LoadError
|
6
|
+
# no problem, we'll fallback to our own map
|
7
|
+
end
|
8
|
+
|
9
|
+
module GraphQL
|
10
|
+
module Execution
|
11
|
+
class Lazy
|
12
|
+
# {GraphQL::Schema} uses this to match returned values to lazy resolution methods.
|
13
|
+
# Methods may be registered for classes, they apply to its subclasses also.
|
14
|
+
# The result of this lookup is cached for future resolutions.
|
15
|
+
# Instances of this class are thread-safe.
|
16
|
+
# @api private
|
17
|
+
# @see {Schema#lazy?} looks up values from this map
|
18
|
+
class LazyMethodMap
|
19
|
+
def initialize(use_concurrent: defined?(Concurrent::Map))
|
20
|
+
@storage = use_concurrent ? Concurrent::Map.new : ConcurrentishMap.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize_copy(other)
|
24
|
+
@storage = other.storage.dup
|
25
|
+
end
|
26
|
+
|
27
|
+
# @param lazy_class [Class] A class which represents a lazy value (subclasses may also be used)
|
28
|
+
# @param lazy_value_method [Symbol] The method to call on this class to get its value
|
29
|
+
def set(lazy_class, lazy_value_method)
|
30
|
+
@storage[lazy_class] = lazy_value_method
|
31
|
+
end
|
32
|
+
|
33
|
+
# @param value [Object] an object which may have a `lazy_value_method` registered for its class or superclasses
|
34
|
+
# @return [Symbol, nil] The `lazy_value_method` for this object, or nil
|
35
|
+
def get(value)
|
36
|
+
@storage.compute_if_absent(value.class) { find_superclass_method(value.class) }
|
37
|
+
end
|
38
|
+
|
39
|
+
def each
|
40
|
+
@storage.each_pair { |k, v| yield(k, v) }
|
41
|
+
end
|
42
|
+
|
43
|
+
protected
|
44
|
+
|
45
|
+
attr_reader :storage
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def find_superclass_method(value_class)
|
50
|
+
@storage.each_pair { |lazy_class, lazy_value_method|
|
51
|
+
return lazy_value_method if value_class < lazy_class
|
52
|
+
}
|
53
|
+
nil
|
54
|
+
end
|
55
|
+
|
56
|
+
# Mock the Concurrent::Map API
|
57
|
+
class ConcurrentishMap
|
58
|
+
extend Forwardable
|
59
|
+
# Technically this should be under the mutex too,
|
60
|
+
# but I know it's only used when the lock is already acquired.
|
61
|
+
def_delegators :@storage, :each_pair, :size
|
62
|
+
|
63
|
+
def initialize
|
64
|
+
@semaphore = Mutex.new
|
65
|
+
# Access to this hash must always be managed by the mutex
|
66
|
+
# since it may be modified at runtime
|
67
|
+
@storage = {}
|
68
|
+
end
|
69
|
+
|
70
|
+
def []=(key, value)
|
71
|
+
@semaphore.synchronize {
|
72
|
+
@storage[key] = value
|
73
|
+
}
|
74
|
+
end
|
75
|
+
|
76
|
+
def compute_if_absent(key)
|
77
|
+
@semaphore.synchronize {
|
78
|
+
@storage.fetch(key) { @storage[key] = yield }
|
79
|
+
}
|
80
|
+
end
|
81
|
+
|
82
|
+
def initialize_copy(other)
|
83
|
+
@semaphore = Mutex.new
|
84
|
+
@storage = other.copy_storage
|
85
|
+
end
|
86
|
+
|
87
|
+
protected
|
88
|
+
|
89
|
+
def copy_storage
|
90
|
+
@semaphore.synchronize {
|
91
|
+
@storage.dup
|
92
|
+
}
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
module Execution
|
4
|
+
class Lazy
|
5
|
+
# Helpers for dealing with data structures containing {Lazy} instances
|
6
|
+
# @api private
|
7
|
+
module Resolve
|
8
|
+
# Mutate `value`, replacing {Lazy} instances in place with their resolved values
|
9
|
+
# @return [void]
|
10
|
+
|
11
|
+
# This object can be passed like an array, but it doesn't allocate an
|
12
|
+
# array until it's used.
|
13
|
+
#
|
14
|
+
# There's one crucial difference: you have to _capture_ the result
|
15
|
+
# of `#<<`. (This _works_ with arrays but isn't required, since it has a side-effect.)
|
16
|
+
# @api private
|
17
|
+
module NullAccumulator
|
18
|
+
def self.<<(item)
|
19
|
+
[item]
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.empty?
|
23
|
+
true
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.resolve(value)
|
28
|
+
lazies = resolve_in_place(value)
|
29
|
+
deep_sync(lazies)
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.resolve_in_place(value)
|
33
|
+
acc = each_lazy(NullAccumulator, value)
|
34
|
+
|
35
|
+
if acc.empty?
|
36
|
+
Lazy::NullResult
|
37
|
+
else
|
38
|
+
Lazy.new {
|
39
|
+
acc.each_with_index { |ctx, idx|
|
40
|
+
acc[idx] = ctx.value.value
|
41
|
+
}
|
42
|
+
resolve_in_place(acc)
|
43
|
+
}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# If `value` is a collection,
|
48
|
+
# add any {Lazy} instances in the collection
|
49
|
+
# to `acc`
|
50
|
+
# @return [void]
|
51
|
+
def self.each_lazy(acc, value)
|
52
|
+
case value
|
53
|
+
when Hash
|
54
|
+
value.each do |key, field_result|
|
55
|
+
acc = each_lazy(acc, field_result)
|
56
|
+
end
|
57
|
+
when Array
|
58
|
+
value.each do |field_result|
|
59
|
+
acc = each_lazy(acc, field_result)
|
60
|
+
end
|
61
|
+
when Query::Context::SharedMethods
|
62
|
+
field_value = value.value
|
63
|
+
case field_value
|
64
|
+
when Lazy
|
65
|
+
acc = acc << value
|
66
|
+
when Enumerable # shortcut for Hash & Array
|
67
|
+
acc = each_lazy(acc, field_value)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
acc
|
72
|
+
end
|
73
|
+
|
74
|
+
# Traverse `val`, triggering resolution for each {Lazy}.
|
75
|
+
# These {Lazy}s are expected to mutate their owner data structures
|
76
|
+
# during resolution! (They're created with the `.then` calls in `resolve_in_place`).
|
77
|
+
# @return [void]
|
78
|
+
def self.deep_sync(val)
|
79
|
+
case val
|
80
|
+
when Lazy
|
81
|
+
deep_sync(val.value)
|
82
|
+
when Array
|
83
|
+
val.each { |v| deep_sync(v.value) }
|
84
|
+
when Hash
|
85
|
+
val.each { |k, v| deep_sync(v.value) }
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "graphql/execution/lazy/lazy_method_map"
|
3
|
+
require "graphql/execution/lazy/resolve"
|
4
|
+
|
5
|
+
module GraphQL
|
6
|
+
module Execution
|
7
|
+
# This wraps a value which is available, but not yet calculated, like a promise or future.
|
8
|
+
#
|
9
|
+
# Calling `#value` will trigger calculation & return the "lazy" value.
|
10
|
+
#
|
11
|
+
# This is an itty-bitty promise-like object, with key differences:
|
12
|
+
# - It has only two states, not-resolved and resolved
|
13
|
+
# - It has no error-catching functionality
|
14
|
+
# @api private
|
15
|
+
class Lazy
|
16
|
+
# Traverse `val`, lazily resolving any values along the way
|
17
|
+
# @param val [Object] A data structure containing mixed plain values and `Lazy` instances
|
18
|
+
# @return void
|
19
|
+
def self.resolve(val)
|
20
|
+
Resolve.resolve(val)
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :path, :field
|
24
|
+
|
25
|
+
# Create a {Lazy} which will get its inner value by calling the block
|
26
|
+
# @param path [Array<String, Integer>]
|
27
|
+
# @param field [GraphQL::Schema::Field]
|
28
|
+
# @param get_value_func [Proc] a block to get the inner value (later)
|
29
|
+
def initialize(path: nil, field: nil, &get_value_func)
|
30
|
+
@get_value_func = get_value_func
|
31
|
+
@resolved = false
|
32
|
+
@path = path
|
33
|
+
@field = field
|
34
|
+
end
|
35
|
+
|
36
|
+
# @return [Object] The wrapped value, calling the lazy block if necessary
|
37
|
+
def value
|
38
|
+
if !@resolved
|
39
|
+
@resolved = true
|
40
|
+
@value = begin
|
41
|
+
v = @get_value_func.call
|
42
|
+
if v.is_a?(Lazy)
|
43
|
+
v = v.value
|
44
|
+
end
|
45
|
+
v
|
46
|
+
rescue GraphQL::ExecutionError => err
|
47
|
+
err
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# `SKIP` was made into a subclass of `GraphQL::Error` to improve runtime performance
|
52
|
+
# (fewer clauses in a hot `case` block), but now it requires special handling here.
|
53
|
+
# I think it's still worth it for the performance win, but if the number of special
|
54
|
+
# cases grows, then maybe it's worth rethinking somehow.
|
55
|
+
if @value.is_a?(StandardError) && @value != GraphQL::Execution::Execute::SKIP
|
56
|
+
raise @value
|
57
|
+
else
|
58
|
+
@value
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# @return [Lazy] A {Lazy} whose value depends on another {Lazy}, plus any transformations in `block`
|
63
|
+
def then
|
64
|
+
self.class.new {
|
65
|
+
yield(value)
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
# @param lazies [Array<Object>] Maybe-lazy objects
|
70
|
+
# @return [Lazy] A lazy which will sync all of `lazies`
|
71
|
+
def self.all(lazies)
|
72
|
+
self.new {
|
73
|
+
lazies.map { |l| l.is_a?(Lazy) ? l.value : l }
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
# This can be used for fields which _had no_ lazy results
|
78
|
+
# @api private
|
79
|
+
NullResult = Lazy.new(){}
|
80
|
+
NullResult.value
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,307 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
module Execution
|
4
|
+
# Lookahead creates a uniform interface to inspect the forthcoming selections.
|
5
|
+
#
|
6
|
+
# It assumes that the AST it's working with is valid. (So, it's safe to use
|
7
|
+
# during execution, but if you're using it directly, be sure to validate first.)
|
8
|
+
#
|
9
|
+
# A field may get access to its lookahead by adding `extras: [:lookahead]`
|
10
|
+
# to its configuration.
|
11
|
+
#
|
12
|
+
# @example looking ahead in a field
|
13
|
+
# field :articles, [Types::Article], null: false,
|
14
|
+
# extras: [:lookahead]
|
15
|
+
#
|
16
|
+
# # For example, imagine a faster database call
|
17
|
+
# # may be issued when only some fields are requested.
|
18
|
+
# #
|
19
|
+
# # Imagine that _full_ fetch must be made to satisfy `fullContent`,
|
20
|
+
# # we can look ahead to see if we need that field. If we do,
|
21
|
+
# # we make the expensive database call instead of the cheap one.
|
22
|
+
# def articles(lookahead:)
|
23
|
+
# if lookahead.selects?(:full_content)
|
24
|
+
# fetch_full_articles(object)
|
25
|
+
# else
|
26
|
+
# fetch_preview_articles(object)
|
27
|
+
# end
|
28
|
+
# end
|
29
|
+
class Lookahead
|
30
|
+
# @param query [GraphQL::Query]
|
31
|
+
# @param ast_nodes [Array<GraphQL::Language::Nodes::Field>, Array<GraphQL::Language::Nodes::OperationDefinition>]
|
32
|
+
# @param field [GraphQL::Schema::Field] if `ast_nodes` are fields, this is the field definition matching those nodes
|
33
|
+
# @param root_type [Class] if `ast_nodes` are operation definition, this is the root type for that operation
|
34
|
+
def initialize(query:, ast_nodes:, field: nil, root_type: nil, owner_type: nil)
|
35
|
+
@ast_nodes = ast_nodes.freeze
|
36
|
+
@field = field
|
37
|
+
@root_type = root_type
|
38
|
+
@query = query
|
39
|
+
@selected_type = @field ? @field.type.unwrap : root_type
|
40
|
+
@owner_type = owner_type
|
41
|
+
end
|
42
|
+
|
43
|
+
# @return [Array<GraphQL::Language::Nodes::Field>]
|
44
|
+
attr_reader :ast_nodes
|
45
|
+
|
46
|
+
# @return [GraphQL::Schema::Field]
|
47
|
+
attr_reader :field
|
48
|
+
|
49
|
+
# @return [GraphQL::Schema::Object, GraphQL::Schema::Union, GraphQL::Schema::Interface]
|
50
|
+
attr_reader :owner_type
|
51
|
+
|
52
|
+
# @return [Hash<Symbol, Object>]
|
53
|
+
def arguments
|
54
|
+
if defined?(@arguments)
|
55
|
+
@arguments
|
56
|
+
else
|
57
|
+
@arguments = if @field
|
58
|
+
@query.schema.after_lazy(@query.arguments_for(@ast_nodes.first, @field)) do |args|
|
59
|
+
args.is_a?(Execution::Interpreter::Arguments) ? args.keyword_arguments : args
|
60
|
+
end
|
61
|
+
else
|
62
|
+
nil
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# True if this node has a selection on `field_name`.
|
68
|
+
# If `field_name` is a String, it is treated as a GraphQL-style (camelized)
|
69
|
+
# field name and used verbatim. If `field_name` is a Symbol, it is
|
70
|
+
# treated as a Ruby-style (underscored) name and camelized before comparing.
|
71
|
+
#
|
72
|
+
# If `arguments:` is provided, each provided key/value will be matched
|
73
|
+
# against the arguments in the next selection. This method will return false
|
74
|
+
# if any of the given `arguments:` are not present and matching in the next selection.
|
75
|
+
# (But, the next selection may contain _more_ than the given arguments.)
|
76
|
+
# @param field_name [String, Symbol]
|
77
|
+
# @param arguments [Hash] Arguments which must match in the selection
|
78
|
+
# @return [Boolean]
|
79
|
+
def selects?(field_name, arguments: nil)
|
80
|
+
selection(field_name, arguments: arguments).selected?
|
81
|
+
end
|
82
|
+
|
83
|
+
# @return [Boolean] True if this lookahead represents a field that was requested
|
84
|
+
def selected?
|
85
|
+
true
|
86
|
+
end
|
87
|
+
|
88
|
+
# Like {#selects?}, but can be used for chaining.
|
89
|
+
# It returns a null object (check with {#selected?})
|
90
|
+
# @return [GraphQL::Execution::Lookahead]
|
91
|
+
def selection(field_name, selected_type: @selected_type, arguments: nil)
|
92
|
+
next_field_name = normalize_name(field_name)
|
93
|
+
|
94
|
+
next_field_defn = get_class_based_field(selected_type, next_field_name)
|
95
|
+
if next_field_defn
|
96
|
+
next_nodes = []
|
97
|
+
@ast_nodes.each do |ast_node|
|
98
|
+
ast_node.selections.each do |selection|
|
99
|
+
find_selected_nodes(selection, next_field_name, next_field_defn, arguments: arguments, matches: next_nodes)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
if next_nodes.any?
|
104
|
+
Lookahead.new(query: @query, ast_nodes: next_nodes, field: next_field_defn, owner_type: selected_type)
|
105
|
+
else
|
106
|
+
NULL_LOOKAHEAD
|
107
|
+
end
|
108
|
+
else
|
109
|
+
NULL_LOOKAHEAD
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# Like {#selection}, but for all nodes.
|
114
|
+
# It returns a list of Lookaheads for all Selections
|
115
|
+
#
|
116
|
+
# If `arguments:` is provided, each provided key/value will be matched
|
117
|
+
# against the arguments in each selection. This method will filter the selections
|
118
|
+
# if any of the given `arguments:` do not match the given selection.
|
119
|
+
#
|
120
|
+
# @example getting the name of a selection
|
121
|
+
# def articles(lookahead:)
|
122
|
+
# next_lookaheads = lookahead.selections # => [#<GraphQL::Execution::Lookahead ...>, ...]
|
123
|
+
# next_lookaheads.map(&:name) #=> [:full_content, :title]
|
124
|
+
# end
|
125
|
+
#
|
126
|
+
# @param arguments [Hash] Arguments which must match in the selection
|
127
|
+
# @return [Array<GraphQL::Execution::Lookahead>]
|
128
|
+
def selections(arguments: nil)
|
129
|
+
subselections_by_type = {}
|
130
|
+
subselections_on_type = subselections_by_type[@selected_type] = {}
|
131
|
+
|
132
|
+
@ast_nodes.each do |node|
|
133
|
+
find_selections(subselections_by_type, subselections_on_type, @selected_type, node.selections, arguments)
|
134
|
+
end
|
135
|
+
|
136
|
+
subselections = []
|
137
|
+
|
138
|
+
subselections_by_type.each do |type, ast_nodes_by_response_key|
|
139
|
+
ast_nodes_by_response_key.each do |response_key, ast_nodes|
|
140
|
+
field_defn = get_class_based_field(type, ast_nodes.first.name)
|
141
|
+
lookahead = Lookahead.new(query: @query, ast_nodes: ast_nodes, field: field_defn, owner_type: type)
|
142
|
+
subselections.push(lookahead)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
subselections
|
147
|
+
end
|
148
|
+
|
149
|
+
# The method name of the field.
|
150
|
+
# It returns the method_sym of the Lookahead's field.
|
151
|
+
#
|
152
|
+
# @example getting the name of a selection
|
153
|
+
# def articles(lookahead:)
|
154
|
+
# article.selection(:full_content).name # => :full_content
|
155
|
+
# # ...
|
156
|
+
# end
|
157
|
+
#
|
158
|
+
# @return [Symbol]
|
159
|
+
def name
|
160
|
+
@field && @field.original_name
|
161
|
+
end
|
162
|
+
|
163
|
+
def inspect
|
164
|
+
"#<GraphQL::Execution::Lookahead #{@field ? "@field=#{@field.path.inspect}": "@root_type=#{@root_type}"} @ast_nodes.size=#{@ast_nodes.size}>"
|
165
|
+
end
|
166
|
+
|
167
|
+
# This is returned for {Lookahead#selection} when a non-existent field is passed
|
168
|
+
class NullLookahead < Lookahead
|
169
|
+
# No inputs required here.
|
170
|
+
def initialize
|
171
|
+
end
|
172
|
+
|
173
|
+
def selected?
|
174
|
+
false
|
175
|
+
end
|
176
|
+
|
177
|
+
def selects?(*)
|
178
|
+
false
|
179
|
+
end
|
180
|
+
|
181
|
+
def selection(*)
|
182
|
+
NULL_LOOKAHEAD
|
183
|
+
end
|
184
|
+
|
185
|
+
def selections(*)
|
186
|
+
[]
|
187
|
+
end
|
188
|
+
|
189
|
+
def inspect
|
190
|
+
"#<GraphQL::Execution::Lookahead::NullLookahead>"
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
# A singleton, so that misses don't come with overhead.
|
195
|
+
NULL_LOOKAHEAD = NullLookahead.new
|
196
|
+
|
197
|
+
private
|
198
|
+
|
199
|
+
# If it's a symbol, stringify and camelize it
|
200
|
+
def normalize_name(name)
|
201
|
+
if name.is_a?(Symbol)
|
202
|
+
Schema::Member::BuildType.camelize(name.to_s)
|
203
|
+
else
|
204
|
+
name
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def normalize_keyword(keyword)
|
209
|
+
if keyword.is_a?(String)
|
210
|
+
Schema::Member::BuildType.underscore(keyword).to_sym
|
211
|
+
else
|
212
|
+
keyword
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
# Wrap get_field and ensure that it returns a GraphQL::Schema::Field.
|
217
|
+
# Remove this when legacy execution is removed.
|
218
|
+
def get_class_based_field(type, name)
|
219
|
+
f = @query.get_field(type, name)
|
220
|
+
f && f.type_class
|
221
|
+
end
|
222
|
+
|
223
|
+
def skipped_by_directive?(ast_selection)
|
224
|
+
ast_selection.directives.each do |directive|
|
225
|
+
dir_defn = @query.schema.directives.fetch(directive.name)
|
226
|
+
directive_class = dir_defn.type_class
|
227
|
+
if directive_class
|
228
|
+
dir_args = @query.arguments_for(directive, dir_defn)
|
229
|
+
return true unless directive_class.static_include?(dir_args, @query.context)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
false
|
233
|
+
end
|
234
|
+
|
235
|
+
def find_selections(subselections_by_type, selections_on_type, selected_type, ast_selections, arguments)
|
236
|
+
ast_selections.each do |ast_selection|
|
237
|
+
next if skipped_by_directive?(ast_selection)
|
238
|
+
|
239
|
+
case ast_selection
|
240
|
+
when GraphQL::Language::Nodes::Field
|
241
|
+
response_key = ast_selection.alias || ast_selection.name
|
242
|
+
if selections_on_type.key?(response_key)
|
243
|
+
selections_on_type[response_key] << ast_selection
|
244
|
+
elsif arguments.nil? || arguments.empty?
|
245
|
+
selections_on_type[response_key] = [ast_selection]
|
246
|
+
else
|
247
|
+
field_defn = get_class_based_field(selected_type, ast_selection.name)
|
248
|
+
if arguments_match?(arguments, field_defn, ast_selection)
|
249
|
+
selections_on_type[response_key] = [ast_selection]
|
250
|
+
end
|
251
|
+
end
|
252
|
+
when GraphQL::Language::Nodes::InlineFragment
|
253
|
+
on_type = selected_type
|
254
|
+
subselections_on_type = selections_on_type
|
255
|
+
if (t = ast_selection.type)
|
256
|
+
# Assuming this is valid, that `t` will be found.
|
257
|
+
on_type = @query.get_type(t.name).type_class
|
258
|
+
subselections_on_type = subselections_by_type[on_type] ||= {}
|
259
|
+
end
|
260
|
+
find_selections(subselections_by_type, subselections_on_type, on_type, ast_selection.selections, arguments)
|
261
|
+
when GraphQL::Language::Nodes::FragmentSpread
|
262
|
+
frag_defn = @query.fragments[ast_selection.name] || raise("Invariant: Can't look ahead to nonexistent fragment #{ast_selection.name} (found: #{@query.fragments.keys})")
|
263
|
+
# Again, assuming a valid AST
|
264
|
+
on_type = @query.get_type(frag_defn.type.name).type_class
|
265
|
+
subselections_on_type = subselections_by_type[on_type] ||= {}
|
266
|
+
find_selections(subselections_by_type, subselections_on_type, on_type, frag_defn.selections, arguments)
|
267
|
+
else
|
268
|
+
raise "Invariant: Unexpected selection type: #{ast_selection.class}"
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
# If a selection on `node` matches `field_name` (which is backed by `field_defn`)
|
274
|
+
# and matches the `arguments:` constraints, then add that node to `matches`
|
275
|
+
def find_selected_nodes(node, field_name, field_defn, arguments:, matches:)
|
276
|
+
return if skipped_by_directive?(node)
|
277
|
+
case node
|
278
|
+
when GraphQL::Language::Nodes::Field
|
279
|
+
if node.name == field_name
|
280
|
+
if arguments.nil? || arguments.empty?
|
281
|
+
# No constraint applied
|
282
|
+
matches << node
|
283
|
+
elsif arguments_match?(arguments, field_defn, node)
|
284
|
+
matches << node
|
285
|
+
end
|
286
|
+
end
|
287
|
+
when GraphQL::Language::Nodes::InlineFragment
|
288
|
+
node.selections.each { |s| find_selected_nodes(s, field_name, field_defn, arguments: arguments, matches: matches) }
|
289
|
+
when GraphQL::Language::Nodes::FragmentSpread
|
290
|
+
frag_defn = @query.fragments[node.name] || raise("Invariant: Can't look ahead to nonexistent fragment #{node.name} (found: #{@query.fragments.keys})")
|
291
|
+
frag_defn.selections.each { |s| find_selected_nodes(s, field_name, field_defn, arguments: arguments, matches: matches) }
|
292
|
+
else
|
293
|
+
raise "Unexpected selection comparison on #{node.class.name} (#{node})"
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
def arguments_match?(arguments, field_defn, field_node)
|
298
|
+
query_kwargs = @query.arguments_for(field_node, field_defn)
|
299
|
+
arguments.all? do |arg_name, arg_value|
|
300
|
+
arg_name = normalize_keyword(arg_name)
|
301
|
+
# Make sure the constraint is present with a matching value
|
302
|
+
query_kwargs.key?(arg_name) && query_kwargs[arg_name] == arg_value
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|