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,245 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
class Subscriptions
|
4
|
+
# A subscriptions implementation that sends data
|
5
|
+
# as ActionCable broadcastings.
|
6
|
+
#
|
7
|
+
# Some things to keep in mind:
|
8
|
+
#
|
9
|
+
# - No queueing system; ActiveJob should be added
|
10
|
+
# - Take care to reload context when re-delivering the subscription. (see {Query#subscription_update?})
|
11
|
+
# - Avoid the async ActionCable adapter and use the redis or PostgreSQL adapters instead. Otherwise calling #trigger won't work from background jobs or the Rails console.
|
12
|
+
#
|
13
|
+
# @example Adding ActionCableSubscriptions to your schema
|
14
|
+
# class MySchema < GraphQL::Schema
|
15
|
+
# # ...
|
16
|
+
# use GraphQL::Subscriptions::ActionCableSubscriptions
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# @example Implementing a channel for GraphQL Subscriptions
|
20
|
+
# class GraphqlChannel < ApplicationCable::Channel
|
21
|
+
# def subscribed
|
22
|
+
# @subscription_ids = []
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# def execute(data)
|
26
|
+
# query = data["query"]
|
27
|
+
# variables = ensure_hash(data["variables"])
|
28
|
+
# operation_name = data["operationName"]
|
29
|
+
# context = {
|
30
|
+
# # Re-implement whatever context methods you need
|
31
|
+
# # in this channel or ApplicationCable::Channel
|
32
|
+
# # current_user: current_user,
|
33
|
+
# # Make sure the channel is in the context
|
34
|
+
# channel: self,
|
35
|
+
# }
|
36
|
+
#
|
37
|
+
# result = MySchema.execute(
|
38
|
+
# query: query,
|
39
|
+
# context: context,
|
40
|
+
# variables: variables,
|
41
|
+
# operation_name: operation_name
|
42
|
+
# )
|
43
|
+
#
|
44
|
+
# payload = {
|
45
|
+
# result: result.to_h,
|
46
|
+
# more: result.subscription?,
|
47
|
+
# }
|
48
|
+
#
|
49
|
+
# # Track the subscription here so we can remove it
|
50
|
+
# # on unsubscribe.
|
51
|
+
# if result.context[:subscription_id]
|
52
|
+
# @subscription_ids << result.context[:subscription_id]
|
53
|
+
# end
|
54
|
+
#
|
55
|
+
# transmit(payload)
|
56
|
+
# end
|
57
|
+
#
|
58
|
+
# def unsubscribed
|
59
|
+
# @subscription_ids.each { |sid|
|
60
|
+
# MySchema.subscriptions.delete_subscription(sid)
|
61
|
+
# }
|
62
|
+
# end
|
63
|
+
#
|
64
|
+
# private
|
65
|
+
#
|
66
|
+
# def ensure_hash(ambiguous_param)
|
67
|
+
# case ambiguous_param
|
68
|
+
# when String
|
69
|
+
# if ambiguous_param.present?
|
70
|
+
# ensure_hash(JSON.parse(ambiguous_param))
|
71
|
+
# else
|
72
|
+
# {}
|
73
|
+
# end
|
74
|
+
# when Hash, ActionController::Parameters
|
75
|
+
# ambiguous_param
|
76
|
+
# when nil
|
77
|
+
# {}
|
78
|
+
# else
|
79
|
+
# raise ArgumentError, "Unexpected parameter: #{ambiguous_param}"
|
80
|
+
# end
|
81
|
+
# end
|
82
|
+
# end
|
83
|
+
#
|
84
|
+
class ActionCableSubscriptions < GraphQL::Subscriptions
|
85
|
+
SUBSCRIPTION_PREFIX = "graphql-subscription:"
|
86
|
+
EVENT_PREFIX = "graphql-event:"
|
87
|
+
|
88
|
+
# @param serializer [<#dump(obj), #load(string)] Used for serializing messages before handing them to `.broadcast(msg)`
|
89
|
+
# @param namespace [string] Used to namespace events and subscriptions (default: '')
|
90
|
+
def initialize(serializer: Serialize, namespace: '', action_cable: ActionCable, action_cable_coder: ActiveSupport::JSON, **rest)
|
91
|
+
# A per-process map of subscriptions to deliver.
|
92
|
+
# This is provided by Rails, so let's use it
|
93
|
+
@subscriptions = Concurrent::Map.new
|
94
|
+
@events = Concurrent::Map.new { |h, k| h[k] = Concurrent::Map.new { |h2, k2| h2[k2] = Concurrent::Array.new } }
|
95
|
+
@action_cable = action_cable
|
96
|
+
@action_cable_coder = action_cable_coder
|
97
|
+
@serializer = serializer
|
98
|
+
@serialize_with_context = case @serializer.method(:load).arity
|
99
|
+
when 1
|
100
|
+
false
|
101
|
+
when 2
|
102
|
+
true
|
103
|
+
else
|
104
|
+
raise ArgumentError, "#{@serializer} must repond to `.load` accepting one or two arguments"
|
105
|
+
end
|
106
|
+
@transmit_ns = namespace
|
107
|
+
super
|
108
|
+
end
|
109
|
+
|
110
|
+
# An event was triggered; Push the data over ActionCable.
|
111
|
+
# Subscribers will re-evaluate locally.
|
112
|
+
def execute_all(event, object)
|
113
|
+
stream = stream_event_name(event)
|
114
|
+
message = @serializer.dump(object)
|
115
|
+
@action_cable.server.broadcast(stream, message)
|
116
|
+
end
|
117
|
+
|
118
|
+
# This subscription was re-evaluated.
|
119
|
+
# Send it to the specific stream where this client was waiting.
|
120
|
+
def deliver(subscription_id, result)
|
121
|
+
payload = { result: result.to_h, more: true }
|
122
|
+
@action_cable.server.broadcast(stream_subscription_name(subscription_id), payload)
|
123
|
+
end
|
124
|
+
|
125
|
+
# A query was run where these events were subscribed to.
|
126
|
+
# Store them in memory in _this_ ActionCable frontend.
|
127
|
+
# It will receive notifications when events come in
|
128
|
+
# and re-evaluate the query locally.
|
129
|
+
def write_subscription(query, events)
|
130
|
+
unless (channel = query.context[:channel])
|
131
|
+
raise GraphQL::Error, "This GraphQL Subscription client does not support the transport protocol expected"\
|
132
|
+
"by the backend Subscription Server implementation (graphql-ruby ActionCableSubscriptions in this case)."\
|
133
|
+
"Some official client implementation including Apollo (https://graphql-ruby.org/javascript_client/apollo_subscriptions.html), "\
|
134
|
+
"Relay Modern (https://graphql-ruby.org/javascript_client/relay_subscriptions.html#actioncable)."\
|
135
|
+
"GraphiQL via `graphiql-rails` may not work out of box (#1051)."
|
136
|
+
end
|
137
|
+
subscription_id = query.context[:subscription_id] ||= build_id
|
138
|
+
stream = stream_subscription_name(subscription_id)
|
139
|
+
channel.stream_from(stream)
|
140
|
+
@subscriptions[subscription_id] = query
|
141
|
+
events.each do |event|
|
142
|
+
# Setup a new listener to run all events with this topic in this process
|
143
|
+
setup_stream(channel, event)
|
144
|
+
# Add this event to the list of events to be updated
|
145
|
+
@events[event.topic][event.fingerprint] << event
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
# Every subscribing channel is listening here, but only one of them takes any action.
|
150
|
+
# This is so we can reuse payloads when possible, and make one payload to send to
|
151
|
+
# all subscribers.
|
152
|
+
#
|
153
|
+
# But the problem is, any channel could close at any time, so each channel has to
|
154
|
+
# be ready to take over the primary position.
|
155
|
+
#
|
156
|
+
# To make sure there's always one-and-only-one channel building payloads,
|
157
|
+
# let the listener belonging to the first event on the list be
|
158
|
+
# the one to build and publish payloads.
|
159
|
+
#
|
160
|
+
def setup_stream(channel, initial_event)
|
161
|
+
topic = initial_event.topic
|
162
|
+
channel.stream_from(stream_event_name(initial_event), coder: @action_cable_coder) do |message|
|
163
|
+
events_by_fingerprint = @events[topic]
|
164
|
+
object = nil
|
165
|
+
events_by_fingerprint.each do |_fingerprint, events|
|
166
|
+
if events.any? && events.first == initial_event
|
167
|
+
# The fingerprint has told us that this response should be shared by all subscribers,
|
168
|
+
# so just run it once, then deliver the result to every subscriber
|
169
|
+
first_event = events.first
|
170
|
+
first_subscription_id = first_event.context.fetch(:subscription_id)
|
171
|
+
object ||= load_action_cable_message(message, first_event.context)
|
172
|
+
result = execute_update(first_subscription_id, first_event, object)
|
173
|
+
if !result.nil?
|
174
|
+
# Having calculated the result _once_, send the same payload to all subscribers
|
175
|
+
events.each do |event|
|
176
|
+
subscription_id = event.context.fetch(:subscription_id)
|
177
|
+
deliver(subscription_id, result)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
nil
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
# This is called to turn an ActionCable-broadcasted string (JSON)
|
187
|
+
# into a query-ready application object.
|
188
|
+
# @param message [String] n ActionCable-broadcasted string (JSON)
|
189
|
+
# @param context [GraphQL::Query::Context] the context of the first event for a given subscription fingerprint
|
190
|
+
def load_action_cable_message(message, context)
|
191
|
+
if @serialize_with_context
|
192
|
+
@serializer.load(message, context)
|
193
|
+
else
|
194
|
+
@serializer.load(message)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# Return the query from "storage" (in memory)
|
199
|
+
def read_subscription(subscription_id)
|
200
|
+
query = @subscriptions[subscription_id]
|
201
|
+
if query.nil?
|
202
|
+
# This can happen when a subscription is triggered from an unsubscribed channel,
|
203
|
+
# see https://github.com/rmosolgo/graphql-ruby/issues/2478.
|
204
|
+
# (This `nil` is handled by `#execute_update`)
|
205
|
+
nil
|
206
|
+
else
|
207
|
+
{
|
208
|
+
query_string: query.query_string,
|
209
|
+
variables: query.provided_variables,
|
210
|
+
context: query.context.to_h,
|
211
|
+
operation_name: query.operation_name,
|
212
|
+
}
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
# The channel was closed, forget about it.
|
217
|
+
def delete_subscription(subscription_id)
|
218
|
+
query = @subscriptions.delete(subscription_id)
|
219
|
+
# This can be `nil` when `.trigger` happens inside an unsubscribed ActionCable channel,
|
220
|
+
# see https://github.com/rmosolgo/graphql-ruby/issues/2478
|
221
|
+
if query
|
222
|
+
events = query.context.namespace(:subscriptions)[:events]
|
223
|
+
events.each do |event|
|
224
|
+
ev_by_fingerprint = @events[event.topic]
|
225
|
+
ev_for_fingerprint = ev_by_fingerprint[event.fingerprint]
|
226
|
+
ev_for_fingerprint.delete(event)
|
227
|
+
if ev_for_fingerprint.empty?
|
228
|
+
ev_by_fingerprint.delete(event.fingerprint)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
private
|
235
|
+
|
236
|
+
def stream_subscription_name(subscription_id)
|
237
|
+
[SUBSCRIPTION_PREFIX, @transmit_ns, subscription_id].join
|
238
|
+
end
|
239
|
+
|
240
|
+
def stream_event_name(event)
|
241
|
+
[EVENT_PREFIX, @transmit_ns, event.topic].join
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
class Subscriptions
|
5
|
+
# Detect whether the current operation:
|
6
|
+
# - Is a subscription operation
|
7
|
+
# - Is completely broadcastable
|
8
|
+
#
|
9
|
+
# Assign the result to `context.namespace(:subscriptions)[:subscription_broadcastable]`
|
10
|
+
# @api private
|
11
|
+
# @see Subscriptions#broadcastable? for a public API
|
12
|
+
class BroadcastAnalyzer < GraphQL::Analysis::AST::Analyzer
|
13
|
+
def initialize(subject)
|
14
|
+
super
|
15
|
+
@default_broadcastable = subject.schema.subscriptions.default_broadcastable
|
16
|
+
# Maybe this will get set to false while analyzing
|
17
|
+
@subscription_broadcastable = true
|
18
|
+
end
|
19
|
+
|
20
|
+
# Only analyze subscription operations
|
21
|
+
def analyze?
|
22
|
+
@query.subscription?
|
23
|
+
end
|
24
|
+
|
25
|
+
def on_enter_field(node, parent, visitor)
|
26
|
+
if (@subscription_broadcastable == false) || visitor.skipping?
|
27
|
+
return
|
28
|
+
end
|
29
|
+
|
30
|
+
current_field = visitor.field_definition
|
31
|
+
apply_broadcastable(current_field)
|
32
|
+
|
33
|
+
current_type = visitor.parent_type_definition
|
34
|
+
if current_type.kind.interface?
|
35
|
+
pt = @query.possible_types(current_type)
|
36
|
+
pt.each do |object_type|
|
37
|
+
ot_field = @query.get_field(object_type, current_field.graphql_name)
|
38
|
+
# Inherited fields would be exactly the same object;
|
39
|
+
# only check fields that are overrides of the inherited one
|
40
|
+
if ot_field && ot_field != current_field
|
41
|
+
apply_broadcastable(ot_field)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Assign the result to context.
|
48
|
+
# (This method is allowed to return an error, but we don't need to)
|
49
|
+
# @return [void]
|
50
|
+
def result
|
51
|
+
query.context.namespace(:subscriptions)[:subscription_broadcastable] = @subscription_broadcastable
|
52
|
+
nil
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
# Modify `@subscription_broadcastable` based on `field_defn`'s configuration (and/or the default value)
|
58
|
+
def apply_broadcastable(field_defn)
|
59
|
+
current_field_broadcastable = field_defn.introspection? || field_defn.broadcastable?
|
60
|
+
case current_field_broadcastable
|
61
|
+
when nil
|
62
|
+
# If the value wasn't set, mix in the default value:
|
63
|
+
# - If the default is false and the current value is true, make it false
|
64
|
+
# - If the default is true and the current value is true, it stays true
|
65
|
+
# - If the default is false and the current value is false, keep it false
|
66
|
+
# - If the default is true and the current value is false, keep it false
|
67
|
+
@subscription_broadcastable = @subscription_broadcastable && @default_broadcastable
|
68
|
+
when false
|
69
|
+
# One non-broadcastable field is enough to make the whole subscription non-broadcastable
|
70
|
+
@subscription_broadcastable = false
|
71
|
+
when true
|
72
|
+
# Leave `@broadcastable_query` true if it's already true,
|
73
|
+
# but don't _set_ it to true if it was set to false by something else.
|
74
|
+
# Actually, just leave it!
|
75
|
+
else
|
76
|
+
raise ArgumentError, "Unexpected `.broadcastable?` value for #{field_defn.path}: #{current_field_broadcastable}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
class Subscriptions
|
4
|
+
class DefaultSubscriptionResolveExtension < GraphQL::Subscriptions::SubscriptionRoot::Extension
|
5
|
+
def resolve(context:, object:, arguments:)
|
6
|
+
has_override_implementation = @field.resolver ||
|
7
|
+
object.respond_to?(@field.resolver_method)
|
8
|
+
|
9
|
+
if !has_override_implementation
|
10
|
+
if context.query.subscription_update?
|
11
|
+
object.object
|
12
|
+
else
|
13
|
+
context.skip
|
14
|
+
end
|
15
|
+
else
|
16
|
+
yield(object, arguments)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
class Subscriptions
|
4
|
+
# This thing can be:
|
5
|
+
# - Subscribed to by `subscription { ... }`
|
6
|
+
# - Triggered by `MySchema.subscriber.trigger(name, arguments, obj)`
|
7
|
+
#
|
8
|
+
class Event
|
9
|
+
# @return [String] Corresponds to the Subscription root field name
|
10
|
+
attr_reader :name
|
11
|
+
|
12
|
+
# @return [GraphQL::Query::Arguments]
|
13
|
+
attr_reader :arguments
|
14
|
+
|
15
|
+
# @return [GraphQL::Query::Context]
|
16
|
+
attr_reader :context
|
17
|
+
|
18
|
+
# @return [String] An opaque string which identifies this event, derived from `name` and `arguments`
|
19
|
+
attr_reader :topic
|
20
|
+
|
21
|
+
def initialize(name:, arguments:, field: nil, context: nil, scope: nil)
|
22
|
+
@name = name
|
23
|
+
@arguments = arguments
|
24
|
+
@context = context
|
25
|
+
field ||= context.field
|
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
|
35
|
+
|
36
|
+
@topic = self.class.serialize(name, arguments, field, scope: scope_val, context: context)
|
37
|
+
end
|
38
|
+
|
39
|
+
# @return [String] an identifier for this unit of subscription
|
40
|
+
def self.serialize(_name, arguments, field, scope:, context: GraphQL::Query::NullContext)
|
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)
|
44
|
+
end
|
45
|
+
|
46
|
+
# @return [String] a logical identifier for this event. (Stable when the query is broadcastable.)
|
47
|
+
def fingerprint
|
48
|
+
@fingerprint ||= begin
|
49
|
+
# When this query has been flagged as broadcastable,
|
50
|
+
# use a generalized, stable fingerprint so that
|
51
|
+
# duplicate subscriptions can be evaluated and distributed in bulk.
|
52
|
+
# (`@topic` includes field, args, and subscription scope already.)
|
53
|
+
if @context.namespace(:subscriptions)[:subscription_broadcastable]
|
54
|
+
"#{@topic}/#{@context.query.fingerprint}"
|
55
|
+
else
|
56
|
+
# not broadcastable, build a unique ID for this event
|
57
|
+
@context.schema.subscriptions.build_id
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class << self
|
63
|
+
private
|
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
|
96
|
+
case args
|
97
|
+
when Hash
|
98
|
+
next_args = {}
|
99
|
+
args.each do |k, v|
|
100
|
+
arg_name = k.to_s
|
101
|
+
camelized_arg_name = GraphQL::Schema::Member::BuildType.camelize(arg_name)
|
102
|
+
arg_defn = get_arg_definition(arg_owner, camelized_arg_name, context)
|
103
|
+
|
104
|
+
if arg_defn
|
105
|
+
normalized_arg_name = camelized_arg_name
|
106
|
+
else
|
107
|
+
normalized_arg_name = arg_name
|
108
|
+
arg_defn = get_arg_definition(arg_owner, normalized_arg_name, context)
|
109
|
+
end
|
110
|
+
arg_base_type = arg_defn.type.unwrap
|
111
|
+
# In the case where the value being emitted is seen as a "JSON"
|
112
|
+
# type, treat the value as one atomic unit of serialization
|
113
|
+
is_json_definition = arg_base_type && arg_base_type <= GraphQL::Types::JSON
|
114
|
+
if is_json_definition
|
115
|
+
sorted_value = if v.is_a?(Hash)
|
116
|
+
deep_sort_hash_keys(v)
|
117
|
+
elsif v.is_a?(Array)
|
118
|
+
deep_sort_array_hashes(v)
|
119
|
+
else
|
120
|
+
v
|
121
|
+
end
|
122
|
+
next_args[normalized_arg_name] = sorted_value.respond_to?(:to_json) ? sorted_value.to_json : sorted_value
|
123
|
+
else
|
124
|
+
next_args[normalized_arg_name] = stringify_args(arg_base_type, v, context)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
# Make sure they're deeply sorted
|
128
|
+
next_args.sort.to_h
|
129
|
+
when Array
|
130
|
+
args.map { |a| stringify_args(arg_owner, a, context) }
|
131
|
+
when GraphQL::Schema::InputObject
|
132
|
+
stringify_args(arg_owner, args.to_h, context)
|
133
|
+
else
|
134
|
+
args
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def get_arg_definition(arg_owner, arg_name, context)
|
139
|
+
arg_owner.get_argument(arg_name, context) || arg_owner.arguments(context).each_value.find { |v| v.keyword.to_s == arg_name }
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
class Subscriptions
|
4
|
+
# Wrap the root fields of the subscription type with special logic for:
|
5
|
+
# - Registering the subscription during the first execution
|
6
|
+
# - Evaluating the triggered portion(s) of the subscription during later execution
|
7
|
+
class Instrumentation
|
8
|
+
def initialize(schema:)
|
9
|
+
@schema = schema
|
10
|
+
end
|
11
|
+
|
12
|
+
def instrument(type, field)
|
13
|
+
if type == @schema.subscription.graphql_definition
|
14
|
+
# This is a root field of `subscription`
|
15
|
+
subscribing_resolve_proc = SubscriptionRegistrationResolve.new(field.resolve_proc)
|
16
|
+
field.redefine(resolve: subscribing_resolve_proc)
|
17
|
+
else
|
18
|
+
field
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# If needed, prepare to gather events which this query subscribes to
|
23
|
+
def before_query(query)
|
24
|
+
if query.subscription? && !query.subscription_update?
|
25
|
+
query.context.namespace(:subscriptions)[:events] = []
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# After checking the root fields, pass the gathered events to the store
|
30
|
+
def after_query(query)
|
31
|
+
events = query.context.namespace(:subscriptions)[:events]
|
32
|
+
if events && events.any?
|
33
|
+
@schema.subscriptions.write_subscription(query, events)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
class SubscriptionRegistrationResolve
|
40
|
+
def initialize(inner_proc)
|
41
|
+
@inner_proc = inner_proc
|
42
|
+
end
|
43
|
+
|
44
|
+
# Wrap the proc with subscription registration logic
|
45
|
+
def call(obj, args, ctx)
|
46
|
+
result = nil
|
47
|
+
if @inner_proc && !@inner_proc.is_a?(GraphQL::Field::Resolve::BuiltInResolve)
|
48
|
+
result = @inner_proc.call(obj, args, ctx)
|
49
|
+
end
|
50
|
+
|
51
|
+
events = ctx.namespace(:subscriptions)[:events]
|
52
|
+
|
53
|
+
if events
|
54
|
+
# This is the first execution, so gather an Event
|
55
|
+
# for the backend to register:
|
56
|
+
events << Subscriptions::Event.new(
|
57
|
+
name: ctx.field.name,
|
58
|
+
arguments: args,
|
59
|
+
context: ctx,
|
60
|
+
)
|
61
|
+
result
|
62
|
+
elsif ctx.irep_node.subscription_topic == ctx.query.subscription_topic
|
63
|
+
if !result.nil?
|
64
|
+
result
|
65
|
+
elsif obj.is_a?(GraphQL::Schema::Object)
|
66
|
+
# The root object is _already_ the subscription update:
|
67
|
+
obj.object
|
68
|
+
else
|
69
|
+
obj
|
70
|
+
end
|
71
|
+
else
|
72
|
+
# This is a subscription update, but this event wasn't triggered.
|
73
|
+
ctx.skip
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|