graphql 1.12.12 → 2.2.14
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/generators/graphql/core.rb +3 -8
- data/lib/generators/graphql/enum_generator.rb +4 -10
- data/lib/generators/graphql/field_extractor.rb +31 -0
- data/lib/generators/graphql/input_generator.rb +50 -0
- data/lib/generators/graphql/install/mutation_root_generator.rb +34 -0
- data/lib/generators/graphql/{templates → install/templates}/base_mutation.erb +2 -0
- data/lib/generators/graphql/{templates → install/templates}/mutation_type.erb +2 -0
- data/lib/generators/graphql/install_generator.rb +14 -4
- data/lib/generators/graphql/interface_generator.rb +7 -7
- data/lib/generators/graphql/mutation_create_generator.rb +22 -0
- data/lib/generators/graphql/mutation_delete_generator.rb +22 -0
- data/lib/generators/graphql/mutation_generator.rb +5 -30
- data/lib/generators/graphql/mutation_update_generator.rb +22 -0
- data/lib/generators/graphql/object_generator.rb +10 -38
- data/lib/generators/graphql/orm_mutations_base.rb +40 -0
- data/lib/generators/graphql/relay.rb +23 -12
- data/lib/generators/graphql/scalar_generator.rb +4 -2
- data/lib/generators/graphql/templates/base_argument.erb +2 -0
- data/lib/generators/graphql/templates/base_connection.erb +2 -0
- data/lib/generators/graphql/templates/base_edge.erb +2 -0
- data/lib/generators/graphql/templates/base_enum.erb +2 -0
- data/lib/generators/graphql/templates/base_field.erb +2 -0
- data/lib/generators/graphql/templates/base_input_object.erb +2 -0
- data/lib/generators/graphql/templates/base_interface.erb +2 -0
- data/lib/generators/graphql/templates/base_object.erb +2 -0
- data/lib/generators/graphql/templates/base_resolver.erb +6 -0
- data/lib/generators/graphql/templates/base_scalar.erb +2 -0
- data/lib/generators/graphql/templates/base_union.erb +2 -0
- data/lib/generators/graphql/templates/enum.erb +5 -1
- data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
- data/lib/generators/graphql/templates/input.erb +9 -0
- data/lib/generators/graphql/templates/interface.erb +4 -2
- data/lib/generators/graphql/templates/loader.erb +2 -0
- data/lib/generators/graphql/templates/mutation.erb +3 -1
- data/lib/generators/graphql/templates/mutation_create.erb +20 -0
- data/lib/generators/graphql/templates/mutation_delete.erb +20 -0
- data/lib/generators/graphql/templates/mutation_update.erb +21 -0
- data/lib/generators/graphql/templates/node_type.erb +2 -0
- data/lib/generators/graphql/templates/object.erb +4 -2
- data/lib/generators/graphql/templates/query_type.erb +2 -0
- data/lib/generators/graphql/templates/scalar.erb +3 -1
- data/lib/generators/graphql/templates/schema.erb +19 -2
- data/lib/generators/graphql/templates/union.erb +4 -2
- data/lib/generators/graphql/type_generator.rb +46 -10
- data/lib/generators/graphql/union_generator.rb +5 -5
- data/lib/graphql/analysis/ast/analyzer.rb +7 -0
- data/lib/graphql/analysis/ast/field_usage.rb +55 -1
- data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -1
- data/lib/graphql/analysis/ast/query_complexity.rb +88 -140
- data/lib/graphql/analysis/ast/query_depth.rb +7 -3
- data/lib/graphql/analysis/ast/visitor.rb +50 -42
- data/lib/graphql/analysis/ast.rb +26 -23
- data/lib/graphql/analysis.rb +0 -7
- data/lib/graphql/backtrace/table.rb +4 -22
- data/lib/graphql/backtrace/trace.rb +93 -0
- data/lib/graphql/backtrace/tracer.rb +8 -6
- data/lib/graphql/backtrace.rb +3 -8
- data/lib/graphql/coercion_error.rb +1 -9
- data/lib/graphql/dataloader/async_dataloader.rb +85 -0
- data/lib/graphql/dataloader/null_dataloader.rb +3 -1
- data/lib/graphql/dataloader/request.rb +5 -0
- data/lib/graphql/dataloader/source.rb +120 -31
- data/lib/graphql/dataloader.rb +168 -142
- data/lib/graphql/date_encoding_error.rb +16 -0
- data/lib/graphql/dig.rb +1 -1
- data/lib/graphql/duration_encoding_error.rb +16 -0
- data/lib/graphql/execution/errors.rb +12 -81
- data/lib/graphql/execution/interpreter/arguments.rb +2 -2
- data/lib/graphql/execution/interpreter/arguments_cache.rb +36 -34
- data/lib/graphql/execution/interpreter/resolve.rb +32 -2
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +170 -0
- data/lib/graphql/execution/interpreter/runtime.rb +414 -341
- data/lib/graphql/execution/interpreter.rb +122 -80
- data/lib/graphql/execution/lazy.rb +11 -21
- data/lib/graphql/execution/lookahead.rb +125 -54
- data/lib/graphql/execution/multiplex.rb +4 -172
- data/lib/graphql/execution.rb +11 -4
- data/lib/graphql/integer_encoding_error.rb +18 -2
- data/lib/graphql/introspection/directive_location_enum.rb +2 -2
- data/lib/graphql/introspection/directive_type.rb +5 -3
- data/lib/graphql/introspection/dynamic_fields.rb +3 -8
- data/lib/graphql/introspection/entry_points.rb +11 -18
- data/lib/graphql/introspection/enum_value_type.rb +2 -2
- data/lib/graphql/introspection/field_type.rb +3 -3
- data/lib/graphql/introspection/input_value_type.rb +10 -4
- data/lib/graphql/introspection/schema_type.rb +12 -5
- data/lib/graphql/introspection/type_type.rb +25 -12
- data/lib/graphql/introspection.rb +6 -2
- data/lib/graphql/language/block_string.rb +37 -25
- data/lib/graphql/language/definition_slice.rb +1 -1
- data/lib/graphql/language/document_from_schema_definition.rb +78 -65
- data/lib/graphql/language/lexer.rb +345 -1467
- data/lib/graphql/language/nodes.rb +145 -91
- data/lib/graphql/language/parser.rb +701 -1921
- data/lib/graphql/language/printer.rb +351 -155
- data/lib/graphql/language/sanitized_printer.rb +25 -27
- data/lib/graphql/language/static_visitor.rb +167 -0
- data/lib/graphql/language/token.rb +0 -4
- data/lib/graphql/language/visitor.rb +188 -141
- data/lib/graphql/language.rb +1 -0
- data/lib/graphql/load_application_object_failed_error.rb +5 -1
- data/lib/graphql/name_validator.rb +0 -4
- data/lib/graphql/pagination/active_record_relation_connection.rb +37 -8
- data/lib/graphql/pagination/array_connection.rb +8 -6
- data/lib/graphql/pagination/connection.rb +61 -7
- data/lib/graphql/pagination/connections.rb +22 -23
- data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
- data/lib/graphql/pagination/relation_connection.rb +60 -28
- data/lib/graphql/query/context/scoped_context.rb +101 -0
- data/lib/graphql/query/context.rb +109 -189
- data/lib/graphql/query/input_validation_result.rb +10 -1
- data/lib/graphql/query/null_context.rb +14 -29
- data/lib/graphql/query/validation_pipeline.rb +15 -39
- data/lib/graphql/query/variable_validation_error.rb +2 -2
- data/lib/graphql/query/variables.rb +35 -17
- data/lib/graphql/query.rb +78 -65
- data/lib/graphql/railtie.rb +8 -109
- data/lib/graphql/rake_task/validate.rb +1 -1
- data/lib/graphql/rake_task.rb +30 -11
- data/lib/graphql/relay/range_add.rb +9 -16
- data/lib/graphql/relay.rb +0 -15
- 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/schema/addition.rb +78 -45
- data/lib/graphql/schema/always_visible.rb +10 -0
- data/lib/graphql/schema/argument.rb +134 -80
- data/lib/graphql/schema/base_64_encoder.rb +3 -5
- data/lib/graphql/schema/build_from_definition.rb +60 -38
- data/lib/graphql/schema/directive/feature.rb +1 -1
- data/lib/graphql/schema/directive/flagged.rb +2 -2
- data/lib/graphql/schema/directive/include.rb +1 -1
- data/lib/graphql/schema/directive/one_of.rb +24 -0
- data/lib/graphql/schema/directive/skip.rb +1 -1
- data/lib/graphql/schema/directive/specified_by.rb +14 -0
- data/lib/graphql/schema/directive/transform.rb +2 -2
- data/lib/graphql/schema/directive.rb +33 -21
- data/lib/graphql/schema/enum.rb +93 -46
- data/lib/graphql/schema/enum_value.rb +4 -21
- data/lib/graphql/schema/field/connection_extension.rb +6 -16
- data/lib/graphql/schema/field/scope_extension.rb +8 -1
- data/lib/graphql/schema/field.rb +432 -337
- data/lib/graphql/schema/field_extension.rb +86 -2
- data/lib/graphql/schema/find_inherited_value.rb +3 -7
- data/lib/graphql/schema/finder.rb +5 -5
- data/lib/graphql/schema/has_single_input_argument.rb +156 -0
- data/lib/graphql/schema/input_object.rb +88 -90
- data/lib/graphql/schema/interface.rb +19 -59
- data/lib/graphql/schema/introspection_system.rb +6 -9
- data/lib/graphql/schema/late_bound_type.rb +8 -2
- data/lib/graphql/schema/list.rb +18 -7
- data/lib/graphql/schema/loader.rb +3 -3
- data/lib/graphql/schema/member/base_dsl_methods.rb +18 -19
- data/lib/graphql/schema/member/build_type.rb +16 -13
- data/lib/graphql/schema/member/has_arguments.rb +288 -84
- data/lib/graphql/schema/member/has_ast_node.rb +12 -0
- data/lib/graphql/schema/member/has_deprecation_reason.rb +3 -4
- data/lib/graphql/schema/member/has_directives.rb +81 -61
- data/lib/graphql/schema/member/has_fields.rb +149 -31
- data/lib/graphql/schema/member/has_interfaces.rb +143 -0
- data/lib/graphql/schema/member/has_validators.rb +32 -6
- data/lib/graphql/schema/member/relay_shortcuts.rb +47 -2
- data/lib/graphql/schema/member/scoped.rb +19 -0
- data/lib/graphql/schema/member/type_system_helpers.rb +17 -0
- data/lib/graphql/schema/member/validates_input.rb +6 -6
- data/lib/graphql/schema/member.rb +1 -6
- data/lib/graphql/schema/mutation.rb +0 -9
- data/lib/graphql/schema/non_null.rb +7 -7
- data/lib/graphql/schema/object.rb +30 -119
- data/lib/graphql/schema/printer.rb +23 -25
- data/lib/graphql/schema/relay_classic_mutation.rb +13 -90
- data/lib/graphql/schema/resolver/has_payload_type.rb +46 -11
- data/lib/graphql/schema/resolver.rb +101 -102
- data/lib/graphql/schema/scalar.rb +20 -21
- data/lib/graphql/schema/subscription.rb +45 -17
- data/lib/graphql/schema/timeout.rb +25 -29
- data/lib/graphql/schema/type_expression.rb +1 -1
- data/lib/graphql/schema/type_membership.rb +21 -4
- data/lib/graphql/schema/union.rb +15 -15
- data/lib/graphql/schema/unique_within_type.rb +1 -1
- 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 +3 -1
- data/lib/graphql/schema/validator/format_validator.rb +4 -5
- data/lib/graphql/schema/validator/inclusion_validator.rb +3 -1
- data/lib/graphql/schema/validator/length_validator.rb +5 -3
- data/lib/graphql/schema/validator/numericality_validator.rb +13 -2
- data/lib/graphql/schema/validator/required_validator.rb +29 -15
- data/lib/graphql/schema/validator.rb +35 -27
- data/lib/graphql/schema/warden.rb +259 -132
- data/lib/graphql/schema/wrapper.rb +0 -5
- data/lib/graphql/schema.rb +658 -989
- data/lib/graphql/static_validation/all_rules.rb +3 -1
- data/lib/graphql/static_validation/base_visitor.rb +14 -28
- data/lib/graphql/static_validation/definition_dependencies.rb +7 -2
- data/lib/graphql/static_validation/error.rb +3 -1
- data/lib/graphql/static_validation/literal_validator.rb +21 -4
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +1 -1
- data/lib/graphql/static_validation/rules/directives_are_defined.rb +11 -5
- data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +13 -13
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +12 -4
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +54 -28
- data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +25 -4
- data/lib/graphql/static_validation/rules/fragments_are_finite.rb +2 -2
- data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid.rb +66 -0
- data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid_error.rb +29 -0
- data/lib/graphql/static_validation/rules/query_root_exists.rb +17 -0
- data/lib/graphql/static_validation/rules/query_root_exists_error.rb +26 -0
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +5 -3
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +4 -4
- data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +13 -7
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +1 -1
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +13 -7
- data/lib/graphql/static_validation/validation_context.rb +16 -6
- data/lib/graphql/static_validation/validator.rb +11 -27
- data/lib/graphql/static_validation.rb +0 -3
- data/lib/graphql/string_encoding_error.rb +13 -3
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +46 -9
- data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +38 -1
- data/lib/graphql/subscriptions/event.rb +75 -37
- data/lib/graphql/subscriptions/serialize.rb +25 -3
- data/lib/graphql/subscriptions.rb +59 -47
- data/lib/graphql/testing/helpers.rb +129 -0
- data/lib/graphql/testing.rb +2 -0
- data/lib/graphql/tracing/active_support_notifications_trace.rb +16 -0
- data/lib/graphql/tracing/active_support_notifications_tracing.rb +6 -20
- data/lib/graphql/tracing/appoptics_trace.rb +251 -0
- data/lib/graphql/tracing/appoptics_tracing.rb +2 -2
- data/lib/graphql/tracing/appsignal_trace.rb +77 -0
- data/lib/graphql/tracing/appsignal_tracing.rb +15 -0
- data/lib/graphql/tracing/data_dog_trace.rb +183 -0
- data/lib/graphql/tracing/data_dog_tracing.rb +25 -15
- data/lib/graphql/{execution/instrumentation.rb → tracing/legacy_hooks_trace.rb} +10 -28
- data/lib/graphql/tracing/legacy_trace.rb +69 -0
- data/lib/graphql/tracing/new_relic_trace.rb +75 -0
- data/lib/graphql/tracing/notifications_trace.rb +45 -0
- data/lib/graphql/tracing/notifications_tracing.rb +59 -0
- data/lib/graphql/tracing/platform_trace.rb +118 -0
- data/lib/graphql/tracing/platform_tracing.rb +46 -49
- data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +4 -2
- data/lib/graphql/tracing/prometheus_trace.rb +89 -0
- data/lib/graphql/tracing/prometheus_tracing.rb +3 -3
- data/lib/graphql/tracing/scout_trace.rb +72 -0
- data/lib/graphql/tracing/sentry_trace.rb +112 -0
- data/lib/graphql/tracing/statsd_trace.rb +56 -0
- data/lib/graphql/tracing/trace.rb +76 -0
- data/lib/graphql/tracing.rb +20 -41
- data/lib/graphql/type_kinds.rb +6 -3
- data/lib/graphql/types/big_int.rb +5 -1
- data/lib/graphql/types/int.rb +1 -1
- data/lib/graphql/types/iso_8601_date.rb +17 -6
- data/lib/graphql/types/iso_8601_date_time.rb +12 -1
- data/lib/graphql/types/iso_8601_duration.rb +77 -0
- data/lib/graphql/types/relay/base_connection.rb +16 -6
- data/lib/graphql/types/relay/connection_behaviors.rb +82 -32
- data/lib/graphql/types/relay/edge_behaviors.rb +36 -7
- data/lib/graphql/types/relay/has_node_field.rb +2 -2
- data/lib/graphql/types/relay/has_nodes_field.rb +2 -2
- data/lib/graphql/types/relay/node_behaviors.rb +12 -2
- data/lib/graphql/types/relay/page_info_behaviors.rb +7 -2
- data/lib/graphql/types/relay.rb +0 -3
- data/lib/graphql/types/string.rb +2 -2
- data/lib/graphql/types.rb +1 -0
- data/lib/graphql/unauthorized_error.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +33 -95
- data/readme.md +13 -6
- metadata +102 -185
- data/lib/graphql/analysis/analyze_query.rb +0 -98
- data/lib/graphql/analysis/field_usage.rb +0 -45
- data/lib/graphql/analysis/max_query_complexity.rb +0 -26
- data/lib/graphql/analysis/max_query_depth.rb +0 -26
- data/lib/graphql/analysis/query_complexity.rb +0 -88
- data/lib/graphql/analysis/query_depth.rb +0 -43
- data/lib/graphql/analysis/reducer_state.rb +0 -48
- data/lib/graphql/argument.rb +0 -131
- data/lib/graphql/authorization.rb +0 -82
- data/lib/graphql/backtrace/legacy_tracer.rb +0 -56
- data/lib/graphql/backwards_compatibility.rb +0 -61
- data/lib/graphql/base_type.rb +0 -230
- data/lib/graphql/boolean_type.rb +0 -2
- data/lib/graphql/compatibility/execution_specification/counter_schema.rb +0 -53
- data/lib/graphql/compatibility/execution_specification/specification_schema.rb +0 -200
- data/lib/graphql/compatibility/execution_specification.rb +0 -436
- data/lib/graphql/compatibility/lazy_execution_specification/lazy_schema.rb +0 -111
- data/lib/graphql/compatibility/lazy_execution_specification.rb +0 -215
- data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +0 -87
- data/lib/graphql/compatibility/query_parser_specification/query_assertions.rb +0 -79
- data/lib/graphql/compatibility/query_parser_specification.rb +0 -266
- data/lib/graphql/compatibility/schema_parser_specification.rb +0 -682
- data/lib/graphql/compatibility.rb +0 -5
- data/lib/graphql/define/assign_argument.rb +0 -12
- data/lib/graphql/define/assign_connection.rb +0 -13
- data/lib/graphql/define/assign_enum_value.rb +0 -18
- data/lib/graphql/define/assign_global_id_field.rb +0 -11
- data/lib/graphql/define/assign_mutation_function.rb +0 -34
- data/lib/graphql/define/assign_object_field.rb +0 -42
- data/lib/graphql/define/defined_object_proxy.rb +0 -53
- data/lib/graphql/define/instance_definable.rb +0 -240
- data/lib/graphql/define/no_definition_error.rb +0 -7
- data/lib/graphql/define/non_null_with_bang.rb +0 -16
- data/lib/graphql/define/type_definer.rb +0 -31
- data/lib/graphql/define.rb +0 -31
- data/lib/graphql/deprecated_dsl.rb +0 -47
- data/lib/graphql/deprecation.rb +0 -13
- data/lib/graphql/directive/deprecated_directive.rb +0 -2
- data/lib/graphql/directive/include_directive.rb +0 -2
- data/lib/graphql/directive/skip_directive.rb +0 -2
- data/lib/graphql/directive.rb +0 -111
- data/lib/graphql/enum_type.rb +0 -129
- data/lib/graphql/execution/execute.rb +0 -333
- data/lib/graphql/execution/flatten.rb +0 -40
- data/lib/graphql/execution/lazy/resolve.rb +0 -91
- data/lib/graphql/execution/typecast.rb +0 -50
- data/lib/graphql/field/resolve.rb +0 -59
- data/lib/graphql/field.rb +0 -226
- data/lib/graphql/filter.rb +0 -53
- data/lib/graphql/float_type.rb +0 -2
- data/lib/graphql/function.rb +0 -128
- data/lib/graphql/id_type.rb +0 -2
- data/lib/graphql/input_object_type.rb +0 -138
- data/lib/graphql/int_type.rb +0 -2
- data/lib/graphql/interface_type.rb +0 -72
- data/lib/graphql/internal_representation/document.rb +0 -27
- data/lib/graphql/internal_representation/node.rb +0 -206
- data/lib/graphql/internal_representation/print.rb +0 -51
- data/lib/graphql/internal_representation/rewrite.rb +0 -184
- data/lib/graphql/internal_representation/scope.rb +0 -88
- data/lib/graphql/internal_representation/visit.rb +0 -36
- data/lib/graphql/internal_representation.rb +0 -7
- data/lib/graphql/language/lexer.rl +0 -262
- data/lib/graphql/language/parser.y +0 -543
- data/lib/graphql/list_type.rb +0 -80
- data/lib/graphql/non_null_type.rb +0 -71
- data/lib/graphql/object_type.rb +0 -130
- data/lib/graphql/query/arguments.rb +0 -189
- data/lib/graphql/query/arguments_cache.rb +0 -24
- data/lib/graphql/query/executor.rb +0 -52
- data/lib/graphql/query/literal_input.rb +0 -136
- data/lib/graphql/query/serial_execution/field_resolution.rb +0 -92
- data/lib/graphql/query/serial_execution/operation_resolution.rb +0 -19
- data/lib/graphql/query/serial_execution/selection_resolution.rb +0 -23
- data/lib/graphql/query/serial_execution/value_resolution.rb +0 -87
- data/lib/graphql/query/serial_execution.rb +0 -40
- data/lib/graphql/relay/array_connection.rb +0 -83
- data/lib/graphql/relay/base_connection.rb +0 -189
- data/lib/graphql/relay/connection_instrumentation.rb +0 -54
- data/lib/graphql/relay/connection_resolve.rb +0 -43
- data/lib/graphql/relay/connection_type.rb +0 -41
- data/lib/graphql/relay/edge.rb +0 -27
- data/lib/graphql/relay/edge_type.rb +0 -19
- data/lib/graphql/relay/edges_instrumentation.rb +0 -40
- data/lib/graphql/relay/global_id_resolve.rb +0 -18
- data/lib/graphql/relay/mongo_relation_connection.rb +0 -50
- data/lib/graphql/relay/mutation/instrumentation.rb +0 -23
- data/lib/graphql/relay/mutation/resolve.rb +0 -56
- data/lib/graphql/relay/mutation/result.rb +0 -38
- data/lib/graphql/relay/mutation.rb +0 -106
- data/lib/graphql/relay/node.rb +0 -39
- data/lib/graphql/relay/page_info.rb +0 -7
- data/lib/graphql/relay/relation_connection.rb +0 -188
- data/lib/graphql/relay/type_extensions.rb +0 -32
- data/lib/graphql/scalar_type.rb +0 -91
- data/lib/graphql/schema/base_64_bp.rb +0 -26
- data/lib/graphql/schema/catchall_middleware.rb +0 -35
- data/lib/graphql/schema/default_parse_error.rb +0 -10
- data/lib/graphql/schema/default_type_error.rb +0 -17
- data/lib/graphql/schema/member/accepts_definition.rb +0 -152
- data/lib/graphql/schema/member/cached_graphql_definition.rb +0 -31
- data/lib/graphql/schema/member/instrumentation.rb +0 -131
- data/lib/graphql/schema/middleware_chain.rb +0 -82
- data/lib/graphql/schema/possible_types.rb +0 -44
- data/lib/graphql/schema/rescue_middleware.rb +0 -60
- data/lib/graphql/schema/timeout_middleware.rb +0 -88
- data/lib/graphql/schema/traversal.rb +0 -228
- data/lib/graphql/schema/validation.rb +0 -313
- data/lib/graphql/static_validation/default_visitor.rb +0 -15
- data/lib/graphql/static_validation/no_validate_visitor.rb +0 -10
- data/lib/graphql/static_validation/type_stack.rb +0 -216
- data/lib/graphql/string_type.rb +0 -2
- data/lib/graphql/subscriptions/instrumentation.rb +0 -79
- data/lib/graphql/subscriptions/subscription_root.rb +0 -76
- data/lib/graphql/tracing/skylight_tracing.rb +0 -70
- data/lib/graphql/types/relay/default_relay.rb +0 -27
- data/lib/graphql/types/relay/node_field.rb +0 -25
- data/lib/graphql/types/relay/nodes_field.rb +0 -27
- data/lib/graphql/union_type.rb +0 -115
- data/lib/graphql/upgrader/member.rb +0 -937
- data/lib/graphql/upgrader/schema.rb +0 -38
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
module StaticValidation
|
4
|
+
class QueryRootExistsError < StaticValidation::Error
|
5
|
+
|
6
|
+
def initialize(message, path: nil, nodes: [])
|
7
|
+
super(message, path: path, nodes: nodes)
|
8
|
+
end
|
9
|
+
|
10
|
+
# A hash representation of this Message
|
11
|
+
def to_h
|
12
|
+
extensions = {
|
13
|
+
"code" => code,
|
14
|
+
}
|
15
|
+
|
16
|
+
super.merge({
|
17
|
+
"extensions" => extensions
|
18
|
+
})
|
19
|
+
end
|
20
|
+
|
21
|
+
def code
|
22
|
+
"missingQueryConfiguration"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -8,7 +8,7 @@ module GraphQL
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def on_directive(node, _parent)
|
11
|
-
directive_defn = context.
|
11
|
+
directive_defn = context.schema_directives[node.name]
|
12
12
|
assert_required_args(node, directive_defn)
|
13
13
|
super
|
14
14
|
end
|
@@ -16,10 +16,12 @@ module GraphQL
|
|
16
16
|
private
|
17
17
|
|
18
18
|
def assert_required_args(ast_node, defn)
|
19
|
+
args = defn.arguments(context.query.context)
|
20
|
+
return if args.empty?
|
19
21
|
present_argument_names = ast_node.arguments.map(&:name)
|
20
|
-
required_argument_names =
|
22
|
+
required_argument_names = context.warden.arguments(defn)
|
21
23
|
.select { |a| a.type.kind.non_null? && !a.default_value? && context.warden.get_argument(defn, a.name) }
|
22
|
-
.map(&:name)
|
24
|
+
.map!(&:name)
|
23
25
|
|
24
26
|
missing_names = required_argument_names - present_argument_names
|
25
27
|
if missing_names.any?
|
@@ -34,16 +34,16 @@ module GraphQL
|
|
34
34
|
parent_type = get_parent_type(context, parent)
|
35
35
|
return unless parent_type && parent_type.kind.input_object?
|
36
36
|
|
37
|
-
required_fields =
|
38
|
-
.select{|
|
39
|
-
.
|
37
|
+
required_fields = context.warden.arguments(parent_type)
|
38
|
+
.select{ |arg| arg.type.kind.non_null? && !arg.default_value? }
|
39
|
+
.map!(&:graphql_name)
|
40
40
|
|
41
41
|
present_fields = ast_node.arguments.map(&:name)
|
42
42
|
missing_fields = required_fields - present_fields
|
43
43
|
|
44
44
|
missing_fields.each do |missing_field|
|
45
45
|
path = [*context.path, missing_field]
|
46
|
-
missing_field_type = parent_type
|
46
|
+
missing_field_type = context.warden.get_argument(parent_type, missing_field).type
|
47
47
|
add_error(RequiredInputObjectAttributesArePresentError.new(
|
48
48
|
"Argument '#{missing_field}' on InputObject '#{parent_type.to_type_signature}' is required. Expected type #{missing_field_type.to_type_signature}",
|
49
49
|
argument_name: missing_field,
|
@@ -34,13 +34,19 @@ module GraphQL
|
|
34
34
|
used_directives = {}
|
35
35
|
node.directives.each do |ast_directive|
|
36
36
|
directive_name = ast_directive.name
|
37
|
-
if used_directives[directive_name]
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
37
|
+
if (first_node = used_directives[directive_name])
|
38
|
+
@directives_are_unique_errors_by_first_node ||= {}
|
39
|
+
err = @directives_are_unique_errors_by_first_node[first_node] ||= begin
|
40
|
+
error = GraphQL::StaticValidation::UniqueDirectivesPerLocationError.new(
|
41
|
+
"The directive \"#{directive_name}\" can only be used once at this location.",
|
42
|
+
nodes: [used_directives[directive_name]],
|
43
|
+
directive: directive_name,
|
44
|
+
)
|
45
|
+
add_error(error)
|
46
|
+
error
|
47
|
+
end
|
48
|
+
err.nodes << ast_directive
|
49
|
+
elsif !((dir_defn = context.schema_directives[directive_name]) && dir_defn.repeatable?)
|
44
50
|
used_directives[directive_name] = ast_directive
|
45
51
|
end
|
46
52
|
end
|
@@ -23,7 +23,7 @@ module GraphQL
|
|
23
23
|
problems = validation_result.problems
|
24
24
|
first_problem = problems && problems.first
|
25
25
|
if first_problem
|
26
|
-
error_message = first_problem["
|
26
|
+
error_message = first_problem["explanation"]
|
27
27
|
end
|
28
28
|
|
29
29
|
error_message ||= "Default value for $#{node.name} doesn't match type #{type.to_type_signature}"
|
@@ -22,15 +22,15 @@ module GraphQL
|
|
22
22
|
node_values = node_values.select { |value| value.is_a? GraphQL::Language::Nodes::VariableIdentifier }
|
23
23
|
|
24
24
|
if node_values.any?
|
25
|
-
|
25
|
+
argument_owner = case parent
|
26
26
|
when GraphQL::Language::Nodes::Field
|
27
|
-
context.field_definition
|
27
|
+
context.field_definition
|
28
28
|
when GraphQL::Language::Nodes::Directive
|
29
|
-
context.directive_definition
|
29
|
+
context.directive_definition
|
30
30
|
when GraphQL::Language::Nodes::InputObject
|
31
31
|
arg_type = context.argument_definition.type.unwrap
|
32
32
|
if arg_type.kind.input_object?
|
33
|
-
|
33
|
+
arg_type
|
34
34
|
else
|
35
35
|
# This is some kind of error
|
36
36
|
nil
|
@@ -43,7 +43,7 @@ module GraphQL
|
|
43
43
|
var_defn_ast = @declared_variables[node_value.name]
|
44
44
|
# Might be undefined :(
|
45
45
|
# VariablesAreUsedAndDefined can't finalize its search until the end of the document.
|
46
|
-
var_defn_ast &&
|
46
|
+
var_defn_ast && argument_owner && validate_usage(argument_owner, node, var_defn_ast)
|
47
47
|
end
|
48
48
|
end
|
49
49
|
super
|
@@ -51,7 +51,7 @@ module GraphQL
|
|
51
51
|
|
52
52
|
private
|
53
53
|
|
54
|
-
def validate_usage(
|
54
|
+
def validate_usage(argument_owner, arg_node, ast_var)
|
55
55
|
var_type = context.schema.type_from_ast(ast_var.type, context: context)
|
56
56
|
if var_type.nil?
|
57
57
|
return
|
@@ -65,9 +65,15 @@ module GraphQL
|
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
68
|
-
arg_defn =
|
68
|
+
arg_defn = context.warden.get_argument(argument_owner, arg_node.name)
|
69
69
|
arg_defn_type = arg_defn.type
|
70
70
|
|
71
|
+
# If the argument is non-null, but it was given a default value,
|
72
|
+
# then treat it as nullable in practice, see https://github.com/rmosolgo/graphql-ruby/issues/3793
|
73
|
+
if arg_defn_type.non_null? && arg_defn.default_value?
|
74
|
+
arg_defn_type = arg_defn_type.of_type
|
75
|
+
end
|
76
|
+
|
71
77
|
var_inner_type = var_type.unwrap
|
72
78
|
arg_inner_type = arg_defn_type.unwrap
|
73
79
|
|
@@ -8,21 +8,23 @@ module GraphQL
|
|
8
8
|
# It provides access to the schema & fragments which validators may read from.
|
9
9
|
#
|
10
10
|
# It holds a list of errors which each validator may add to.
|
11
|
-
#
|
12
|
-
# It also provides limited access to the {TypeStack} instance,
|
13
|
-
# which tracks state as you climb in and out of different fields.
|
14
11
|
class ValidationContext
|
15
12
|
extend Forwardable
|
16
13
|
|
17
14
|
attr_reader :query, :errors, :visitor,
|
18
|
-
:on_dependency_resolve_handlers
|
15
|
+
:on_dependency_resolve_handlers,
|
16
|
+
:max_errors, :warden, :schema
|
17
|
+
|
19
18
|
|
20
|
-
def_delegators :@query, :
|
19
|
+
def_delegators :@query, :document, :fragments, :operations
|
21
20
|
|
22
|
-
def initialize(query, visitor_class)
|
21
|
+
def initialize(query, visitor_class, max_errors)
|
23
22
|
@query = query
|
23
|
+
@warden = query.warden
|
24
|
+
@schema = query.schema
|
24
25
|
@literal_validator = LiteralValidator.new(context: query.context)
|
25
26
|
@errors = []
|
27
|
+
@max_errors = max_errors || Float::INFINITY
|
26
28
|
@on_dependency_resolve_handlers = []
|
27
29
|
@visitor = visitor_class.new(document, self)
|
28
30
|
end
|
@@ -38,6 +40,14 @@ module GraphQL
|
|
38
40
|
def validate_literal(ast_value, type)
|
39
41
|
@literal_validator.validate(ast_value, type)
|
40
42
|
end
|
43
|
+
|
44
|
+
def too_many_errors?
|
45
|
+
@errors.length >= @max_errors
|
46
|
+
end
|
47
|
+
|
48
|
+
def schema_directives
|
49
|
+
@schema_directives ||= schema.directives
|
50
|
+
end
|
41
51
|
end
|
42
52
|
end
|
43
53
|
end
|
@@ -24,35 +24,26 @@ module GraphQL
|
|
24
24
|
# @param query [GraphQL::Query]
|
25
25
|
# @param validate [Boolean]
|
26
26
|
# @param timeout [Float] Number of seconds to wait before aborting validation. Any positive number may be used, including Floats to specify fractional seconds.
|
27
|
+
# @param max_errors [Integer] Maximum number of errors before aborting validation. Any positive number will limit the number of errors. Defaults to nil for no limit.
|
27
28
|
# @return [Array<Hash>]
|
28
|
-
def validate(query, validate: true, timeout: nil)
|
29
|
-
query.
|
30
|
-
|
31
|
-
errors = if validate == false
|
29
|
+
def validate(query, validate: true, timeout: nil, max_errors: nil)
|
30
|
+
query.current_trace.validate(validate: validate, query: query) do
|
31
|
+
begin_t = Time.now
|
32
|
+
errors = if validate == false
|
32
33
|
[]
|
33
34
|
else
|
34
35
|
rules_to_use = validate ? @rules : []
|
35
|
-
visitor_class = BaseVisitor.including_rules(rules_to_use
|
36
|
+
visitor_class = BaseVisitor.including_rules(rules_to_use)
|
36
37
|
|
37
|
-
context = GraphQL::StaticValidation::ValidationContext.new(query, visitor_class)
|
38
|
+
context = GraphQL::StaticValidation::ValidationContext.new(query, visitor_class, max_errors)
|
38
39
|
|
39
40
|
begin
|
40
41
|
# CAUTION: Usage of the timeout module makes the assumption that validation rules are stateless Ruby code that requires no cleanup if process was interrupted. This means no blocking IO calls, native gems, locks, or `rescue` clauses that must be reached.
|
41
42
|
# A timeout value of 0 or nil will execute the block without any timeout.
|
42
43
|
Timeout::timeout(timeout) do
|
43
|
-
|
44
|
-
|
45
|
-
unless (legacy_rules = rules_to_use - GraphQL::StaticValidation::ALL_RULES).empty?
|
46
|
-
legacy_rules.each do |rule_class_or_module|
|
47
|
-
if rule_class_or_module.method_defined?(:validate)
|
48
|
-
GraphQL::Deprecation.warn "Legacy validator rules will be removed from GraphQL-Ruby 2.0, use a module instead (see the built-in rules: https://github.com/rmosolgo/graphql-ruby/tree/master/lib/graphql/static_validation/rules)"
|
49
|
-
GraphQL::Deprecation.warn " -> Legacy validator: #{rule_class_or_module}"
|
50
|
-
rule_class_or_module.new.validate(context)
|
51
|
-
end
|
52
|
-
end
|
44
|
+
catch(:too_many_validation_errors) do
|
45
|
+
context.visitor.visit
|
53
46
|
end
|
54
|
-
|
55
|
-
context.visitor.visit
|
56
47
|
end
|
57
48
|
rescue Timeout::Error
|
58
49
|
handle_timeout(query, context)
|
@@ -61,22 +52,15 @@ module GraphQL
|
|
61
52
|
context.errors
|
62
53
|
end
|
63
54
|
|
64
|
-
irep = if errors.empty? && context
|
65
|
-
# Only return this if there are no errors and validation was actually run
|
66
|
-
context.visitor.rewrite_document
|
67
|
-
else
|
68
|
-
nil
|
69
|
-
end
|
70
|
-
|
71
55
|
{
|
56
|
+
remaining_timeout: timeout ? (timeout - (Time.now - begin_t)) : nil,
|
72
57
|
errors: errors,
|
73
|
-
irep: irep,
|
74
58
|
}
|
75
59
|
end
|
76
60
|
rescue GraphQL::ExecutionError => e
|
77
61
|
{
|
62
|
+
remaining_timeout: nil,
|
78
63
|
errors: [e],
|
79
|
-
irep: nil,
|
80
64
|
}
|
81
65
|
end
|
82
66
|
|
@@ -1,13 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require "graphql/static_validation/error"
|
3
3
|
require "graphql/static_validation/definition_dependencies"
|
4
|
-
require "graphql/static_validation/type_stack"
|
5
4
|
require "graphql/static_validation/validator"
|
6
5
|
require "graphql/static_validation/validation_context"
|
7
6
|
require "graphql/static_validation/validation_timeout_error"
|
8
7
|
require "graphql/static_validation/literal_validator"
|
9
8
|
require "graphql/static_validation/base_visitor"
|
10
|
-
require "graphql/static_validation/no_validate_visitor"
|
11
9
|
|
12
10
|
rules_glob = File.expand_path("../static_validation/rules/*.rb", __FILE__)
|
13
11
|
Dir.glob(rules_glob).each do |file|
|
@@ -15,5 +13,4 @@ Dir.glob(rules_glob).each do |file|
|
|
15
13
|
end
|
16
14
|
|
17
15
|
require "graphql/static_validation/all_rules"
|
18
|
-
require "graphql/static_validation/default_visitor"
|
19
16
|
require "graphql/static_validation/interpreter_visitor"
|
@@ -1,10 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module GraphQL
|
3
3
|
class StringEncodingError < GraphQL::RuntimeTypeError
|
4
|
-
attr_reader :string
|
5
|
-
def initialize(str)
|
4
|
+
attr_reader :string, :field, :path
|
5
|
+
def initialize(str, context:)
|
6
6
|
@string = str
|
7
|
-
|
7
|
+
@field = context[:current_field]
|
8
|
+
@path = context[:current_path]
|
9
|
+
message = "String #{str.inspect} was encoded as #{str.encoding}".dup
|
10
|
+
if @path
|
11
|
+
message << " @ #{@path.join(".")}"
|
12
|
+
end
|
13
|
+
if @field
|
14
|
+
message << " (#{@field.path})"
|
15
|
+
end
|
16
|
+
message << ". GraphQL requires an encoding compatible with UTF-8."
|
17
|
+
super(message)
|
8
18
|
end
|
9
19
|
end
|
10
20
|
end
|
@@ -35,7 +35,7 @@ module GraphQL
|
|
35
35
|
# }
|
36
36
|
#
|
37
37
|
# result = MySchema.execute(
|
38
|
-
# query
|
38
|
+
# query,
|
39
39
|
# context: context,
|
40
40
|
# variables: variables,
|
41
41
|
# operation_name: operation_name
|
@@ -91,10 +91,24 @@ module GraphQL
|
|
91
91
|
# A per-process map of subscriptions to deliver.
|
92
92
|
# This is provided by Rails, so let's use it
|
93
93
|
@subscriptions = Concurrent::Map.new
|
94
|
-
@events = Concurrent::Map.new
|
94
|
+
@events = Concurrent::Map.new do |h, k|
|
95
|
+
h.compute_if_absent(k) do
|
96
|
+
Concurrent::Map.new do |h2, k2|
|
97
|
+
h2.compute_if_absent(k2) { Concurrent::Array.new }
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
95
101
|
@action_cable = action_cable
|
96
102
|
@action_cable_coder = action_cable_coder
|
97
103
|
@serializer = serializer
|
104
|
+
@serialize_with_context = case @serializer.method(:load).arity
|
105
|
+
when 1
|
106
|
+
false
|
107
|
+
when 2
|
108
|
+
true
|
109
|
+
else
|
110
|
+
raise ArgumentError, "#{@serializer} must repond to `.load` accepting one or two arguments"
|
111
|
+
end
|
98
112
|
@transmit_ns = namespace
|
99
113
|
super
|
100
114
|
end
|
@@ -110,7 +124,8 @@ module GraphQL
|
|
110
124
|
# This subscription was re-evaluated.
|
111
125
|
# Send it to the specific stream where this client was waiting.
|
112
126
|
def deliver(subscription_id, result)
|
113
|
-
|
127
|
+
has_more = !result.context.namespace(:subscriptions)[:final_update]
|
128
|
+
payload = { result: result.to_h, more: has_more }
|
114
129
|
@action_cable.server.broadcast(stream_subscription_name(subscription_id), payload)
|
115
130
|
end
|
116
131
|
|
@@ -119,7 +134,13 @@ module GraphQL
|
|
119
134
|
# It will receive notifications when events come in
|
120
135
|
# and re-evaluate the query locally.
|
121
136
|
def write_subscription(query, events)
|
122
|
-
channel = query.context
|
137
|
+
unless (channel = query.context[:channel])
|
138
|
+
raise GraphQL::Error, "This GraphQL Subscription client does not support the transport protocol expected"\
|
139
|
+
"by the backend Subscription Server implementation (graphql-ruby ActionCableSubscriptions in this case)."\
|
140
|
+
"Some official client implementation including Apollo (https://graphql-ruby.org/javascript_client/apollo_subscriptions.html), "\
|
141
|
+
"Relay Modern (https://graphql-ruby.org/javascript_client/relay_subscriptions.html#actioncable)."\
|
142
|
+
"GraphiQL via `graphiql-rails` may not work out of box (#1051)."
|
143
|
+
end
|
123
144
|
subscription_id = query.context[:subscription_id] ||= build_id
|
124
145
|
stream = stream_subscription_name(subscription_id)
|
125
146
|
channel.stream_from(stream)
|
@@ -154,12 +175,14 @@ module GraphQL
|
|
154
175
|
# so just run it once, then deliver the result to every subscriber
|
155
176
|
first_event = events.first
|
156
177
|
first_subscription_id = first_event.context.fetch(:subscription_id)
|
157
|
-
object ||=
|
178
|
+
object ||= load_action_cable_message(message, first_event.context)
|
158
179
|
result = execute_update(first_subscription_id, first_event, object)
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
180
|
+
if !result.nil?
|
181
|
+
# Having calculated the result _once_, send the same payload to all subscribers
|
182
|
+
events.each do |event|
|
183
|
+
subscription_id = event.context.fetch(:subscription_id)
|
184
|
+
deliver(subscription_id, result)
|
185
|
+
end
|
163
186
|
end
|
164
187
|
end
|
165
188
|
end
|
@@ -167,6 +190,18 @@ module GraphQL
|
|
167
190
|
end
|
168
191
|
end
|
169
192
|
|
193
|
+
# This is called to turn an ActionCable-broadcasted string (JSON)
|
194
|
+
# into a query-ready application object.
|
195
|
+
# @param message [String] n ActionCable-broadcasted string (JSON)
|
196
|
+
# @param context [GraphQL::Query::Context] the context of the first event for a given subscription fingerprint
|
197
|
+
def load_action_cable_message(message, context)
|
198
|
+
if @serialize_with_context
|
199
|
+
@serializer.load(message, context)
|
200
|
+
else
|
201
|
+
@serializer.load(message)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
170
205
|
# Return the query from "storage" (in memory)
|
171
206
|
def read_subscription(subscription_id)
|
172
207
|
query = @subscriptions[subscription_id]
|
@@ -188,6 +223,8 @@ module GraphQL
|
|
188
223
|
# The channel was closed, forget about it.
|
189
224
|
def delete_subscription(subscription_id)
|
190
225
|
query = @subscriptions.delete(subscription_id)
|
226
|
+
# In case this came from the server, tell the client to unsubscribe:
|
227
|
+
@action_cable.server.broadcast(stream_subscription_name(subscription_id), { more: false })
|
191
228
|
# This can be `nil` when `.trigger` happens inside an unsubscribed ActionCable channel,
|
192
229
|
# see https://github.com/rmosolgo/graphql-ruby/issues/2478
|
193
230
|
if query
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module GraphQL
|
3
3
|
class Subscriptions
|
4
|
-
class DefaultSubscriptionResolveExtension < GraphQL::
|
4
|
+
class DefaultSubscriptionResolveExtension < GraphQL::Schema::FieldExtension
|
5
5
|
def resolve(context:, object:, arguments:)
|
6
6
|
has_override_implementation = @field.resolver ||
|
7
7
|
object.respond_to?(@field.resolver_method)
|
@@ -16,6 +16,43 @@ module GraphQL
|
|
16
16
|
yield(object, arguments)
|
17
17
|
end
|
18
18
|
end
|
19
|
+
|
20
|
+
def after_resolve(value:, context:, object:, arguments:, **rest)
|
21
|
+
if value.is_a?(GraphQL::ExecutionError)
|
22
|
+
value
|
23
|
+
elsif (events = context.namespace(:subscriptions)[:events])
|
24
|
+
# This is the first execution, so gather an Event
|
25
|
+
# for the backend to register:
|
26
|
+
event = Subscriptions::Event.new(
|
27
|
+
name: field.name,
|
28
|
+
arguments: arguments_without_field_extras(arguments: arguments),
|
29
|
+
context: context,
|
30
|
+
field: field,
|
31
|
+
)
|
32
|
+
events << event
|
33
|
+
value
|
34
|
+
elsif context.query.subscription_topic == Subscriptions::Event.serialize(
|
35
|
+
field.name,
|
36
|
+
arguments_without_field_extras(arguments: arguments),
|
37
|
+
field,
|
38
|
+
scope: (field.subscription_scope ? context[field.subscription_scope] : nil),
|
39
|
+
)
|
40
|
+
# This is a subscription update. The resolver returned `skip` if it should be skipped,
|
41
|
+
# or else it returned an object to resolve the update.
|
42
|
+
value
|
43
|
+
else
|
44
|
+
# This is a subscription update, but this event wasn't triggered.
|
45
|
+
context.skip
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def arguments_without_field_extras(arguments:)
|
52
|
+
arguments.dup.tap do |event_args|
|
53
|
+
field.extras.each { |k| event_args.delete(k) }
|
54
|
+
end
|
55
|
+
end
|
19
56
|
end
|
20
57
|
end
|
21
58
|
end
|
@@ -9,7 +9,7 @@ module GraphQL
|
|
9
9
|
# @return [String] Corresponds to the Subscription root field name
|
10
10
|
attr_reader :name
|
11
11
|
|
12
|
-
# @return [GraphQL::
|
12
|
+
# @return [GraphQL::Execution::Interpreter::Arguments]
|
13
13
|
attr_reader :arguments
|
14
14
|
|
15
15
|
# @return [GraphQL::Query::Context]
|
@@ -23,32 +23,24 @@ module GraphQL
|
|
23
23
|
@arguments = arguments
|
24
24
|
@context = context
|
25
25
|
field ||= context.field
|
26
|
-
|
26
|
+
scope_key = field.subscription_scope
|
27
|
+
scope_val = scope || (context && scope_key && context[scope_key])
|
28
|
+
if scope_key &&
|
29
|
+
(subscription = field.resolver) &&
|
30
|
+
(subscription.respond_to?(:subscription_scope_optional?)) &&
|
31
|
+
!subscription.subscription_scope_optional? &&
|
32
|
+
scope_val.nil?
|
33
|
+
raise Subscriptions::SubscriptionScopeMissingError, "#{field.path} (#{subscription}) requires a `scope:` value to trigger updates (Set `subscription_scope ..., optional: true` to disable this requirement)"
|
34
|
+
end
|
27
35
|
|
28
|
-
@topic = self.class.serialize(name, arguments, field, scope: scope_val)
|
36
|
+
@topic = self.class.serialize(name, arguments, field, scope: scope_val, context: context)
|
29
37
|
end
|
30
38
|
|
31
39
|
# @return [String] an identifier for this unit of subscription
|
32
|
-
def self.serialize(
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
when Hash
|
37
|
-
if field.is_a?(GraphQL::Schema::Field)
|
38
|
-
stringify_args(field, arguments)
|
39
|
-
else
|
40
|
-
GraphQL::Query::LiteralInput.from_arguments(
|
41
|
-
arguments,
|
42
|
-
field,
|
43
|
-
nil,
|
44
|
-
)
|
45
|
-
end
|
46
|
-
else
|
47
|
-
raise ArgumentError, "Unexpected arguments: #{arguments}, must be Hash or GraphQL::Arguments"
|
48
|
-
end
|
49
|
-
|
50
|
-
sorted_h = stringify_args(field, normalized_args.to_h)
|
51
|
-
Serialize.dump_recursive([scope, name, sorted_h])
|
40
|
+
def self.serialize(_name, arguments, field, scope:, context: GraphQL::Query::NullContext.instance)
|
41
|
+
subscription = field.resolver || GraphQL::Schema::Subscription
|
42
|
+
normalized_args = stringify_args(field, arguments.to_h, context)
|
43
|
+
subscription.topic_for(arguments: normalized_args, field: field, scope: scope)
|
52
44
|
end
|
53
45
|
|
54
46
|
# @return [String] a logical identifier for this event. (Stable when the query is broadcastable.)
|
@@ -69,37 +61,83 @@ module GraphQL
|
|
69
61
|
|
70
62
|
class << self
|
71
63
|
private
|
72
|
-
|
64
|
+
|
65
|
+
# This method does not support cyclic references in the Hash,
|
66
|
+
# nor does it support Hashes whose keys are not sortable
|
67
|
+
# with respect to their peers ( cases where a <=> b might throw an error )
|
68
|
+
def deep_sort_hash_keys(hash_to_sort)
|
69
|
+
raise ArgumentError.new("Argument must be a Hash") unless hash_to_sort.is_a?(Hash)
|
70
|
+
hash_to_sort.keys.sort.map do |k|
|
71
|
+
if hash_to_sort[k].is_a?(Hash)
|
72
|
+
[k, deep_sort_hash_keys(hash_to_sort[k])]
|
73
|
+
elsif hash_to_sort[k].is_a?(Array)
|
74
|
+
[k, deep_sort_array_hashes(hash_to_sort[k])]
|
75
|
+
else
|
76
|
+
[k, hash_to_sort[k]]
|
77
|
+
end
|
78
|
+
end.to_h
|
79
|
+
end
|
80
|
+
|
81
|
+
def deep_sort_array_hashes(array_to_inspect)
|
82
|
+
raise ArgumentError.new("Argument must be an Array") unless array_to_inspect.is_a?(Array)
|
83
|
+
array_to_inspect.map do |v|
|
84
|
+
if v.is_a?(Hash)
|
85
|
+
deep_sort_hash_keys(v)
|
86
|
+
elsif v.is_a?(Array)
|
87
|
+
deep_sort_array_hashes(v)
|
88
|
+
else
|
89
|
+
v
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def stringify_args(arg_owner, args, context)
|
95
|
+
arg_owner = arg_owner.respond_to?(:unwrap) ? arg_owner.unwrap : arg_owner # remove list and non-null wrappers
|
73
96
|
case args
|
74
97
|
when Hash
|
75
98
|
next_args = {}
|
76
99
|
args.each do |k, v|
|
77
100
|
arg_name = k.to_s
|
78
101
|
camelized_arg_name = GraphQL::Schema::Member::BuildType.camelize(arg_name)
|
79
|
-
arg_defn = get_arg_definition(arg_owner, camelized_arg_name)
|
80
|
-
|
81
|
-
|
82
|
-
|
102
|
+
arg_defn = get_arg_definition(arg_owner, camelized_arg_name, context)
|
103
|
+
arg_defn ||= get_arg_definition(arg_owner, arg_name, context)
|
104
|
+
normalized_arg_name = arg_defn.graphql_name
|
105
|
+
arg_base_type = arg_defn.type.unwrap
|
106
|
+
# In the case where the value being emitted is seen as a "JSON"
|
107
|
+
# type, treat the value as one atomic unit of serialization
|
108
|
+
is_json_definition = arg_base_type && arg_base_type <= GraphQL::Types::JSON
|
109
|
+
if is_json_definition
|
110
|
+
sorted_value = if v.is_a?(Hash)
|
111
|
+
deep_sort_hash_keys(v)
|
112
|
+
elsif v.is_a?(Array)
|
113
|
+
deep_sort_array_hashes(v)
|
114
|
+
else
|
115
|
+
v
|
116
|
+
end
|
117
|
+
next_args[normalized_arg_name] = sorted_value.respond_to?(:to_json) ? sorted_value.to_json : sorted_value
|
83
118
|
else
|
84
|
-
normalized_arg_name =
|
85
|
-
arg_defn = get_arg_definition(arg_owner, normalized_arg_name)
|
119
|
+
next_args[normalized_arg_name] = stringify_args(arg_base_type, v, context)
|
86
120
|
end
|
87
|
-
|
88
|
-
next_args[normalized_arg_name] = stringify_args(arg_defn.type, v)
|
89
121
|
end
|
90
122
|
# Make sure they're deeply sorted
|
91
123
|
next_args.sort.to_h
|
92
124
|
when Array
|
93
|
-
args.map { |a| stringify_args(arg_owner, a) }
|
125
|
+
args.map { |a| stringify_args(arg_owner, a, context) }
|
94
126
|
when GraphQL::Schema::InputObject
|
95
|
-
stringify_args(arg_owner, args.to_h)
|
127
|
+
stringify_args(arg_owner, args.to_h, context)
|
96
128
|
else
|
97
|
-
|
129
|
+
if arg_owner.is_a?(Class) && arg_owner < GraphQL::Schema::Enum
|
130
|
+
# `prepare:` may have made the value something other than
|
131
|
+
# a defined value of this enum -- use _that_ in this case.
|
132
|
+
arg_owner.coerce_isolated_input(args) || args
|
133
|
+
else
|
134
|
+
args
|
135
|
+
end
|
98
136
|
end
|
99
137
|
end
|
100
138
|
|
101
|
-
def get_arg_definition(arg_owner, arg_name)
|
102
|
-
arg_owner.
|
139
|
+
def get_arg_definition(arg_owner, arg_name, context)
|
140
|
+
arg_owner.get_argument(arg_name, context) || arg_owner.arguments(context).each_value.find { |v| v.keyword.to_s == arg_name }
|
103
141
|
end
|
104
142
|
end
|
105
143
|
end
|